How to Build a REST API with Best Practices
Build a resource-oriented REST API with correct HTTP verbs and status codes, boundary input validation, consistent Problem Details errors, and path-based versioning for safe evolution.
What and why
REST models your domain as resources addressed by URLs, manipulated with HTTP verbs. A well-designed REST API is predictable: clients can guess routes, rely on status codes, and parse consistent errors. This tutorial builds a small resource API the right way.
Prerequisites
- A runtime and web framework (Node with Express or Fastify here).
- Basic HTTP knowledge.
- A data store, or an in-memory list for the demo.
Steps
1. Model resources and routes
Use plural nouns and let the verb carry the action:
GET /v1/articles list
POST /v1/articles create
GET /v1/articles/:id read
PATCH /v1/articles/:id partial update
DELETE /v1/articles/:id delete
Avoid verbs in paths (no /getArticles).
2. Implement CRUD handlers
app.post('/v1/articles', (req, res) => {
const article = create(req.body);
res.status(201).location(`/v1/articles/${article.id}`).json(article);
});
3. Use correct status codes
200 for success, 201 for created (with a Location header), 204 for a successful delete with no body, 400 for bad input, 404 for missing resources, 409 for conflicts, and 422 for semantic validation errors.
4. Validate request input
Validate at the boundary with a schema and reject invalid bodies before any logic runs:
const schema = z.object({ title: z.string().min(1), body: z.string() });
const parsed = schema.safeParse(req.body);
if (!parsed.success) return res.status(422).json({ errors: parsed.error.issues });
5. Standardize error responses
Return a consistent error shape, ideally RFC 9457 Problem Details:
{ "type": "about:blank", "title": "Not Found", "status": 404, "detail": "Article 7 does not exist" }
6. Version the API
Put the major version in the path (/v1/) so breaking changes ship as /v2/ without affecting existing clients. Keep responses backward compatible within a version: add fields, never remove or repurpose them.
Verification
- Each route returns the documented status code.
- Invalid input returns 422 with field-level errors.
- A 404 returns the standard error shape.
- The version prefix is present on all routes.
Next Steps
Document the API with OpenAPI, add pagination and filtering to list endpoints, secure it with JWT or OAuth, and add rate limiting before exposing it publicly.
Prerequisites
- A runtime and web framework
- Basic HTTP knowledge
- A data store or in memory list
Steps
- 1Model resources and routes
- 2Implement CRUD handlers
- 3Use correct status codes
- 4Validate request input
- 5Standardize error responses
- 6Version the API