REST APIs and microservices

5 lessons

HTTP methods and idempotency

GET reads a resource and must not change state (it is safe). POST creates a resource or triggers an action. PUT replaces the whole resource at a known URL.

PATCH applies a partial update. DELETE removes the resource. An idempotent method leaves the server in the same state whether it is called once or many times: GET, HEAD, OPTIONS, PUT and DELETE are idempotent, POST is not, and PATCH is not guaranteed to be.

Idempotency matters for retries: a client can safely resend a timed-out PUT, but resending a POST may create two orders. The standard fix is an idempotency key: the client sends a unique key with the POST, and the server stores the result and returns the same response for any repeat with that key.

Check yourself

A mobile client times out while sending a request and automatically retries it. Which request is unsafe to retry without extra protection?

Status codes

2xx means success: 200 OK, 201 Created (a new resource exists, often with a Location header), 202 Accepted (work queued, not finished), 204 No Content (success with an empty body, common for DELETE). 3xx means redirection. 4xx means the client made an error: 400 Bad Request (malformed or invalid input), 401 Unauthorized (not authenticated, the server does not know who you are), 403 Forbidden (authenticated but not allowed), 404 Not Found, 409 Conflict (duplicate or version clash), 422 Unprocessable Entity (well-formed but semantically wrong), 429 Too Many Requests.

5xx means the server failed: 500 Internal Server Error, 502 Bad Gateway (upstream returned garbage), 503 Service Unavailable (overloaded or in maintenance, often with Retry-After), 504 Gateway Timeout.

Check yourself

A logged-in user tries to delete another user's account and the server refuses. Which status code is correct?

Pagination

Offset pagination (?offset=40&limit=20) is simple and lets users jump to any page, but the database still has to walk past the skipped rows, so deep pages get slow, and if rows are inserted or deleted between requests, items get skipped or repeated. Cursor (keyset) pagination returns an opaque cursor that encodes the position of the last item seen (for example its ID and timestamp), and the next request asks for items after that cursor. It stays fast at any depth and is stable under concurrent writes, but it cannot jump straight to page 50 and needs a stable sort key.

Feeds, timelines and large exports typically use cursors; admin tables with small data often keep offsets.

Check yourself

Users scrolling a busy activity feed report seeing the same items twice. The API uses offset pagination. What is the best fix?

Retries, backoff and circuit breakers

Calls between services fail sometimes, so clients retry. Naive immediate retries make an overloaded dependency worse. Exponential backoff waits longer after each failure (1s, 2s, 4s...), and jitter adds randomness so that thousands of clients that failed together do not retry in synchronised waves.

Only retry operations that are safe to repeat, or attach idempotency keys, and cap the number of attempts. A circuit breaker tracks failures to a dependency: after a threshold it opens and fails fast without calling the dependency, giving it time to recover; after a cooldown it half-opens and lets a few trial calls through; if they succeed it closes again. Timeouts, bulkheads (isolated resource pools) and fallbacks (serve cached or default data) complete the toolkit.

Check yourself

Why is jitter added to exponential backoff?

Microservices basics

Microservices split an application into small services that are deployed independently, each owning its own data store. Benefits: teams ship on their own schedule, services scale separately, and a failure can be contained. Costs: network calls replace function calls, failures become partial and distributed, debugging needs tracing across services, and keeping data consistent across services is hard.

An API gateway is a single entry point that routes requests to the right service and centralises cross-cutting concerns such as authentication, rate limiting, logging and TLS. Service discovery lets services find each other's current addresses as instances come and go. When one business operation spans several services, the saga pattern runs a sequence of local transactions and triggers compensating actions (such as a refund) if a later step fails, instead of a global distributed transaction.

Check yourself

Which responsibility belongs to an API gateway rather than to an individual microservice?