Windsurf Rules for Full-Stack TypeScript Apps
Keeps a TypeScript monorepo honest: one source of truth for types, API contracts that cannot drift, and forms validated by the same schema the server uses.
Use when: Use in a full-stack TypeScript repo (Next.js, TanStack Start, or Express + React) where the client and server share a package and you want Windsurf to stop duplicating types and hand-rolling fetch calls.
The skill file
Windsurf · .windsurfrules.windsurfrules2731 chars
# Full-Stack TypeScript Rules
## Type sharing
- All types shared between client and server live in `packages/shared/src/types/`. Never redeclare a shape that already exists there — import it.
- Before defining a new interface, search the shared package for an existing one. If a near-match exists, extend it with `Pick`/`Omit` instead of copying fields.
- Database row types come from the ORM (`InferSelectModel` / Prisma generated types). Never hand-write a type that mirrors a table.
- `any` is banned. If a type is genuinely unknown, use `unknown` and narrow it with a type guard in the same file.
## API contract sync
- Every API endpoint has exactly one Zod schema for its input and one for its output, defined next to the handler and exported.
- The client never calls `fetch` directly. All requests go through the typed client (`api.*`). If an endpoint is missing from the client, add it there first.
- When you change an endpoint's input or output, update in the same edit: the Zod schema, the handler, every client call site, and the endpoint's test. Do not leave any of these for "later".
- Breaking changes to a deployed endpoint require a new versioned route (`/v2/...`); never repurpose an existing field's meaning.
## Form validation
- Every form uses `react-hook-form` with `zodResolver`, and the resolver schema must be the same exported schema the server validates with. Never write a separate client-only copy.
- Client-side refinements that the server cannot check (e.g. "passwords match") go in a `.extend()`/`.refine()` wrapper around the shared schema, not a fork of it.
- Display server validation errors via `setError` mapped from the error response — never a generic "Something went wrong" toast when field-level info is available.
- Disable the submit button while `isSubmitting` is true; every mutation needs a pending state.
## Project hygiene
- Run `npm run typecheck` after any change that touches more than one file. Type errors are never acceptable in a suggested diff.
- Never edit generated files: `*.gen.ts`, `prisma/client`, anything under `dist/`. Fix the generator input instead.
- Server-only modules (`db`, `env`, anything importing `node:` builtins) must never be imported from a file that renders on the client. If unsure, check the import chain before adding the import.
- Environment variables are read only through the validated `env` object, never `process.env` directly.
## Testing
- New shared schemas get a test asserting one valid and two invalid payloads.
- API handler changes require updating the handler's integration test in the same commit.
- Do not mock the typed API client in component tests — mock at the HTTP boundary (MSW) so contract drift surfaces in tests.
Install
drop into your repoSave this to your project or home directory so Windsurf can load it.
path./.windsurfrules
Discussion
What people are saying
Forks
0 forks of this skillLoading forks…