INTRO
REST is unfashionable in some circles, but most product teams still consume JSON over HTTP. The goal is not purity - it is contracts that a mid-level developer can call without reading your codebase. That means consistent paths, honest status codes, and error bodies that say what went wrong.
Nouns in URLs, verbs in HTTP methods
I use plural resource names: `/orders`, `/orders/:id`. Actions that are not CRUD become sub-resources when it keeps idempotency clear: `POST /orders/:id/cancel` with a reason body, instead of overloading `PATCH` with magic fields.
Query parameters filter, sort, and paginate: `?status=open&limit=25&cursor=abc`. I document defaults (limit cap, sort order) in OpenAPI or a short internal doc - whichever the team will actually maintain.
Status codes and a single error shape
2xx for success, 4xx for client mistakes or auth, 5xx for things you need to fix. Mixing 200 with `{ error: true }` trains clients to ignore HTTP semantics.
Every error returns the same JSON envelope: `code` (machine), `message` (human), optional `details` for validation fields. Logs include a `requestId` header you return to the client so support can trace.
code-snippet.typescript
export function badRequest(message: string, details?: unknown) {
return {
status: 400,
body: { code: 'BAD_REQUEST', message, details },
}
}Versioning when you actually need it
If only your web app calls the API, you can ship breaking changes with the deploy. When mobile or third parties exist, prefix with `/v1` or use headers - but pick one and never mix.
Deprecation: communicate sunset dates, log old path usage, and keep old behavior until traffic drops. Rushing removals burns trust.
Pagination that scales past demo data
Offset pagination is simple but degrades on large tables. Cursor-based lists work better for feeds and audit logs. Expose `nextCursor` in the response, not just page numbers.
Avoid N+1 in serializers: join or batch-load related ids before mapping to JSON.
KEY TAKEAWAYS
“Use predictable resource paths; reserve sub-resources for non-CRUD actions.”
- 02
Align status codes with reality; standardize error JSON + request IDs.
- 03
Add `/v1` when external clients cannot deploy lockstep with the API.
- 04
Choose cursor pagination for large, append-only datasets.
CONCLUSION
Closing thought
Good Node APIs are boring: the client knows what to send, what it will get back, and how to report a failure. Spend design time on those contracts - not on acronyms. Ready to talk scope? Use Contact — or browse Services for fit.
CONTINUE
Related pages
UP NEXT
MongoDB Schema Patterns for Operational Web Apps
Practical modeling notes from MERN dashboards: tickets, inventory lines, audit trails, and reporting - without documents that grow forever.
Read next article