Tags: api, rest, http, graphql, design, architecture
Level: intermediate
TL;DR: Best practices for contract-first API design, clear resource naming, predictable HTTP semantics, idempotency, and robust error handling.
Treat API contracts as public interfaces where breaking changes require explicit versioning.
Use nouns for resource endpoints and HTTP verbs for operations (GET, POST, PUT, PATCH, DELETE).
Differentiate PUT (full replacement) from PATCH (partial resource update).
Maintain idempotency on PUT, DELETE, and safe read methods like GET and HEAD.
Implement idempotent retries for mutating POST operations using unique Idempotency-Key headers.
Return standardized machine-readable error responses adhering to RFC 7807 (Problem Details).
Decouple internal database representations from external API DTOs and view models.
Support pagination via cursor-based tokens rather than offset/limit for large or mutable datasets.
Use explicit API versioning strategies such as URI pathing (/v1/) or custom request headers.
Enforce strict rate limiting using standard response headers (Retry-After, RateLimit-Remaining).
Q: How do you handle breaking changes in an active production API?R: I introduce a new API version under a versioned path or header while deprecating the old endpoint with sunset warning headers, running both in parallel until clients migrate.
Q: What is the difference between PUT and PATCH, and when do you choose each?R: I use PUT when the client provides the full representation to replace the resource, and PATCH when sending partial deltas to modify specific fields without overwriting the rest.
Q: How do you guarantee idempotency in distributed payment or creation requests?R: I require clients to send a unique Idempotency-Key header, which I cache atomically with the execution state and response payload to return cached results on retries.
Using verbs in endpoint paths like /getUser or /createOrder instead of standard REST resources.
Returning 200 OK with an error message in the payload body instead of proper HTTP status codes.
Leaking internal database identifiers, primary keys, or stack traces directly into API responses.
Relying on OFFSET pagination on volatile data tables, leading to skipped or duplicated records.
Over-fetching large datasets by not providing field filtering, sparse fieldsets, or pagination.
```POST /v1/payments HTTP/1.1
Host: api.example.com
Content-Type: application/json
Idempotency-Key: 7b2c9e42-8f92-4917-91a5-83e91d5fa114
{
"amount": 4900,
"currency": "EUR"
}
{
"type": "https://api.example.com/errors/resource-not-found",
"title": "Resource Not Found",
"status": 404,
"detail": "Customer 48102 does not exist.",
"instance": "/v1/customers/48102"
}
```
[ ] Cursor-based pagination vs offset pagination trade-offs
[ ] RFC 7807 Problem Details for HTTP APIs
[ ] Idempotency keys implementation with Redis
[ ] GraphQL vs REST trade-offs for over-fetching scenarios