DevNotes
Support
System Design Basics · Chapter 11

API Design & Communication Protocols

REST resource design, HTTP methods and status codes, idempotency and idempotency keys, pagination, filtering and versioning, error formats, REST vs GraphQL vs gRPC, WebSockets, server-sent events, polling and webhooks.

◷ 6 min read§ 13 sections? 6 interview questions{ } 0 code examples

#Introduction

An API is a contract between a system and its clients, and once published it's hard to change. Good API design makes systems easy to use, safe to retry and possible to evolve.

#REST Resource Design

Model resources (nouns) and use HTTP methods as verbs:

MethodPathMeaning
GET/ordersList orders
GET/orders/{id}Get one order
POST/ordersCreate an order
PUT/orders/{id}Replace an order
PATCH/orders/{id}Partially update an order
DELETE/orders/{id}Delete an order
GET/customers/{id}/ordersA customer's orders (sub-resource)
POST/orders/{id}/cancellationAn action modelled as a resource

Guidelines: plural nouns, lowercase with hyphens (/order-items), no verbs in paths (/getOrders ❌), and consistent naming across the whole API.

#Methods: Safe and Idempotent

MethodSafe (no side effects)Idempotent (repeating has the same effect)
GET, HEAD, OPTIONS✅✅
PUT❌✅
DELETE❌✅
POST❌❌
PATCH❌❌ (can be made idempotent)

Idempotency matters because networks fail: a client that times out doesn't know whether its request succeeded, and will retry.

Idempotency Keys

Make POST safe to retry by having the client send a unique key:

HTTP
POST /payments
Idempotency-Key: 3f9a1c7e-58b2-4f2a-9d1e-1b2c3d4e5f6a
Content-Type: application/json

{ "orderId": "ORD-1001", "amount": 2499.00 }
Request arrives with an idempotency key
Key seen before?
return the stored response — no second charge
process, store key + response with a TTL, return it

Payment APIs such as Stripe and Razorpay work this way.

#Status Codes

CodeMeaningUse
200 OKSuccessGET, PUT, PATCH
201 CreatedResource createdPOST — include a Location header
202 AcceptedAccepted for asynchronous processingLong-running jobs
204 No ContentSuccess with no bodyDELETE
400 Bad RequestInvalid inputValidation errors
401 UnauthorizedNot authenticatedMissing or invalid token
403 ForbiddenAuthenticated but not allowedInsufficient permissions
404 Not FoundResource doesn't exist
409 ConflictConflicts with current stateDuplicate, version conflict
422 Unprocessable ContentWell-formed but semantically invalidBusiness-rule violations
429 Too Many RequestsRate limitedWith Retry-After
500 Internal Server ErrorUnexpected server failureBugs — never leak stack traces
502 / 503 / 504Bad gateway / unavailable / gateway timeoutUpstream or overload problems

#Consistent Error Responses

Use one error format everywhere — the Problem Details standard (RFC 9457), supported by Spring 6 (ProblemDetail):

JSON
{
  "type": "https://api.example.com/errors/insufficient-stock",
  "title": "Insufficient stock",
  "status": 409,
  "detail": "Only 2 units of SKU-1 are available.",
  "instance": "/orders",
  "traceId": "4bf92f3577b34da6a3ce929d0e0e4736"
}

#Pagination

Never return unbounded lists.

Offset paginationCursor (keyset) pagination
GET /orders?page=3&size=20GET /orders?limit=20&after=eyJpZCI6MTAwfQ
Easy; supports jumping to page NStable and fast at any depth
Slow for deep pages (OFFSET 100000)No random page access
Items shift when data changes — duplicates or gapsConsistent while data changes

For large or frequently changing datasets (feeds, logs), cursor pagination is the scalable choice — it maps to keyset queries like WHERE id < ? ORDER BY id DESC LIMIT 20 (see the SQL chapters).

JSON
{
  "items": [ … ],
  "nextCursor": "eyJpZCI6ODB9",
  "hasMore": true
}

#Filtering, Sorting and Field Selection

Text
GET /orders?status=PAID&createdAfter=2026-10-01&sort=-createdAt&fields=id,total,status

Validate and whitelist parameters, index the columns you allow filtering on, and cap page sizes.

#Versioning

StrategyExample
URI path/v1/orders, /v2/orders — the most common and visible
HeaderAccept: application/vnd.example.v2+json
Query parameter/orders?version=2

Prefer additive, backward-compatible changes (new optional fields) so new versions are rarely needed; when they are, run both for a deprecation period and announce removal dates (Deprecation / Sunset headers).

#Other API Concerns

  • Authentication with OAuth2 bearer tokens; HTTPS only.
  • Rate limiting with 429 responses.
  • Caching of GET responses with Cache-Control and ETag (conditional requests return 304 Not Modified).
  • Optimistic concurrency with ETag / If-Match to prevent lost updates (412 Precondition Failed).
  • Documentation with OpenAPI (springdoc-openapi generates it from Spring controllers).

#REST vs GraphQL vs gRPC

RESTGraphQLgRPC
StyleResources + HTTP methodsOne endpoint, client-specified queriesRemote procedure calls
FormatJSON (usually)JSONProtocol Buffers (binary)
TransportHTTP/1.1 or 2HTTPHTTP/2
StrengthsSimple, cacheable, universalClients fetch exactly the fields they need in one request; good for varied UIsFast, compact, strongly typed, streaming
WeaknessesOver- and under-fetching; many round tripsCaching and rate limiting are harder; complex queries can be expensiveNot browser-friendly; binary debugging
Best forPublic APIs, CRUDMobile/web apps aggregating many entitiesInternal service-to-service calls

#Real-Time Communication

TechniqueHow it worksUse
Short pollingClient asks every few secondsSimple, wasteful; rarely ideal
Long pollingServer holds the request until there's news or a timeoutFallback for older clients
Server-Sent Events (SSE)One long-lived HTTP connection; server pushes text eventsOne-way updates: notifications, live scores, progress
WebSocketsFull-duplex connection over a single TCP socketChat, collaborative editing, games, trading
WebhooksYour server calls the client's URL when something happensServer-to-server events (payment succeeded)

Webhooks Done Right

  • Sign payloads (HMAC) so receivers can verify them.
  • Retry with back-off; receivers must be idempotent (deduplicate by event ID).
  • Respond quickly (2xx) and process asynchronously.

#Interview Questions

Q1What makes an API RESTful?

Resources identified by URIs, standard HTTP methods with their semantics, stateless requests, representations such as JSON, meaningful status codes, and cacheable responses where appropriate.

Q2Which HTTP methods are idempotent?

GET, HEAD, OPTIONS, PUT and DELETE. POST and PATCH aren't by default; idempotency keys make POST safe to retry.

Q3Offset vs cursor pagination?

Offset uses page numbers — simple, but slow for deep pages and unstable when data changes. Cursor pagination continues from the last item seen — fast and consistent at any depth, without random page access.

Q4REST vs GraphQL vs gRPC — when would you use each?

REST for public and CRUD APIs; GraphQL when varied clients need flexible data fetching in one request; gRPC for high-performance internal service-to-service communication.

Q5WebSockets vs SSE?

WebSockets are full-duplex for interactive two-way communication; SSE is simpler one-way server push over HTTP with automatic reconnection.

Q6401 vs 403?

401 means not authenticated (missing or invalid credentials); 403 means authenticated but not permitted.

#Key Takeaways

  • Design around resources and standard HTTP methods; be consistent.
  • Know safe and idempotent methods; use idempotency keys for retried POSTs.
  • Return precise status codes and one consistent error format (Problem Details).
  • Paginate everything — cursors for large, changing datasets.
  • Evolve additively; version only for breaking changes.
  • REST for public APIs, gRPC internally, GraphQL for flexible clients; SSE/WebSockets/webhooks for real-time.