Server work begins at the domain contract, not at Prisma or the web component that exposed the need.
Before editing
- Find the existing route, model, helper, and tests with
rg. - Compare the published REST API documentation with
routes:listto confirm the documented and implemented method and path. - Identify the caller: public browser, authenticated SDK, Cloudflare Worker, background job, or admin client.
- Decide whether the change is additive, behaviour-changing, or breaking.
- Check whether the response is represented in the published TypeScript SDK.
- Reproduce the current result using an isolated environment or a focused handler test.
Route checklist
- Put the endpoint in the nearest domain route group.
- Describe params, query, body, and responses with TypeBox.
- Reject malformed input at the boundary.
- Use
auth: "required"for authenticated work, then enforce object-level authority in the handler or domain layer. - Return only the fields the caller needs.
- Keep secrets and private server detail out of errors.
- Make repeated requests safe when the client may retry them.
- Register the module explicitly.
- Update the static route snapshot only for an intentional path-set change.
Client flags such as is_staff, is_content_moderator, or a pack permission are useful for affordances. The server is the security boundary and must derive or verify the relevant authority itself.
Response and compatibility changes
Treat the TypeBox response schema, handler result, SDK type/runtime parsing, and UI assumption as one contract. Prefer adding an optional field or new endpoint before removing or changing an existing field. When a break is worthwhile, give dependent repositories a migration path and merge in a compatible order.
Do not filter valid server data merely to fit an outdated client union. Update the contract at its source and add a regression test for the real response.
Separate HTTP from background effects
Some operations, notably howl creation, accept work and return an identifier while an in-memory queue continues it. A successful acceptance response does not prove every later side effect completed. A failed job does not prove nothing was written before the failure.
For asynchronous changes, identify:
- the durable and in-memory state;
- when the caller receives success;
- how status is observed;
- what may already have happened at each failure point;
- whether retry duplicates work; and
- what shutdown does to active work.
Verify
Run focused tests, then at least:
pnpm --dir apps/server routes:check
pnpm --dir apps/server check-types
pnpm --dir apps/server test
git diff --checkCI additionally generates Prisma, verifies the native uWebSockets.js addon, checks the remaining Deno packages, and builds the Docker image. A local typecheck does not replace those results.
Use a disposable PostgreSQL database for an integration or schema check. Do not point a routine development command at production and do not run migrate merely to see whether it works.