API Design Roadmap
An API is the contract between software systems, and most engineering work now crosses one: a front end calling a back end, services calling each other, partners integrating with a platform. Well-designed APIs are easy to learn and hard to misuse; badly designed ones leak data, break clients and slow every team that depends on them.
This roadmap goes from what an API is and how HTTP works, through RESTful resource design, pagination, errors, idempotency and versioning, to authentication, authorisation, security and compliance. It then covers GraphQL, gRPC, real-time and event-driven APIs, performance and gateways, and finally testing, observability and lifecycle management. Core topics come up in most back-end interviews, recommended topics in most jobs, and optional topics in specific industries.
By the end you will be able to design, document, secure and evolve a production API and defend your choices in an interview. Practise design questions in system design practice and track your progress in progress page.
- Level
- Beginner to advanced
- Time
- 12–16 weeks at about 8 hours a week
- Scope
- 60 topics in 9 stages
- Before you start
- Programming in one back-end language such as Python Roadmap, Java Roadmap, Go Roadmap or Node.js Roadmap; Comfort with JSON and the command line, including curl; Basic SQL Roadmap and how a web application talks to a database; Version control with Git and GitHub Roadmap
What you will be able to do
- Design RESTful resources, URLs, methods and status codes that are consistent and predictable.
- Design pagination, filtering, error responses, idempotency and versioning that scale with the API.
- Choose and implement API keys, sessions, JWTs or OAuth 2.0, and design object-level authorisation.
- Defend an API against the common vulnerabilities and protect it with rate limits.
- Choose between REST, GraphQL, gRPC, webhooks, real-time and event-driven interfaces.
- Document APIs with OpenAPI and test them with unit, integration, contract and load tests.
- Operate an API with metrics, traces and a clear deprecation process.
The roadmap at a glance
- Stage 1API Basics and the Network UnderneathBeginner · 1 week · 6 topics
- Stage 2HTTP Building BlocksBeginner · 1–2 weeks · 6 topics
- Stage 3Designing RESTful APIsBeginner · 2 weeks · 7 topics
- Stage 4Designing the ContractIntermediate · 2 weeks · 8 topics
- Stage 5Authentication and AuthorisationIntermediate · 2–3 weeks · 8 topics
- Stage 6Security, Rate Limits and ComplianceIntermediate · 2 weeks · 4 topics
- Stage 7Beyond REST: GraphQL, gRPC, Real-Time and EventsIntermediate · 2–3 weeks · 8 topics
- Stage 8Performance, Gateways and IntegrationAdvanced · 2 weeks · 7 topics
- Stage 9Testing, Observability and LifecycleAdvanced · 2 weeks · 6 topics
Stage 1 API Basics and the Network Underneath
Goal. Explain what an API is, name the main API styles, and follow a request from DNS lookup to HTTP response.
What are APIs Core
An application programming interface is a contract that lets one piece of software use another without knowing how it is built. The contract says which operations exist, what inputs they take, what they return and how failures are reported.
APIs appear at every level: a library's public functions, an operating system's calls, and the web APIs this roadmap focuses on, where clients talk to servers over a network. A web API is a product with users, even when those users are your own front-end team. Its design decides how easy it is to integrate with, how safely it can evolve and how well it performs.
To learn the basics of API design, think of it as designing a user interface for programmers: predictable, consistent and hard to misuse.
Practice. Pick an API you have used and write down its resources, its authentication method and how it reports errors.
Different API Styles Core
The main styles you will meet:
- REST: resources addressed by URLs and manipulated with HTTP methods. The default for public web APIs.
- Simple JSON APIs: JSON over HTTP without full REST discipline, often action-style endpoints such as
POST /sendEmail. Common internally. - GraphQL: one endpoint where clients ask for exactly the fields they want.
- gRPC: typed remote procedure calls over HTTP/2 with Protocol Buffers, strong for service-to-service traffic.
- SOAP APIs: XML envelopes with strict WSDL contracts and WS-* standards. Legacy now, but still found in banking, insurance and government integrations.
- Event and real-time APIs: webhooks, WebSockets, server-sent events and message brokers.
Pick by consumer: public partners usually want REST, internal services gain from gRPC, and rich client apps may prefer GraphQL.
Practice. For one product idea, decide which style you would use for its public API, its mobile app and its internal services, and justify each.
Understand TCP / IP Recommended
Web APIs ride on the TCP/IP stack. IP moves packets between machines by address. TCP adds a reliable, ordered byte stream on top, with a three-way handshake to open a connection and retransmission when packets are lost. TLS then encrypts that stream.
Why an API designer cares: every new connection costs round trips for the TCP and TLS handshakes, so connection reuse, keep-alive and HTTP/2 multiplexing make a large difference to latency. Mobile clients on poor networks feel this most. UDP, which skips reliability, underlies QUIC and therefore HTTP/3.
You should be able to explain ports, the handshake, why a timeout differs from a connection refusal, and what a load balancer does at layer 4 compared with layer 7.
Practice. Use curl with timing output to measure DNS, connect, TLS and first-byte times for an API, with and without connection reuse.
Basics of DNS Recommended
DNS turns a name such as api.example.com into an IP address. The client asks a resolver, which walks from the root servers to the domain's authoritative servers and caches the answer for the record's time to live.
Record types to know: A and AAAA for IPv4 and IPv6 addresses, CNAME for aliases, TXT for verification and email policies, and NS for delegation.
For APIs, DNS matters in a few places: a dedicated subdomain for the API makes routing and certificates simpler, low TTLs allow fast failover between regions, and DNS-based load balancing spreads clients across data centres. Stale DNS caches are a classic reason why a migration seems to work for some clients and not others.
Practice. Use dig or nslookup to trace how a public API's hostname resolves and note its TTL and any CNAME chain.
HTTP in API Design Core
HTTP is a request-response protocol. A request has a method, a path, headers and an optional body; a response has a status code, headers and an optional body. It is stateless: each request carries everything the server needs, which is what lets you put many identical servers behind a load balancer.
POST /v1/orders HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOi...
{"productId": "p_123", "quantity": 2}Good HTTP APIs use the protocol rather than tunnelling everything through POST with a status of 200: methods carry intent, status codes carry outcome, and headers carry caching, auth and content metadata.
Practice. Make raw requests to a public API with curl -v and identify each part of the request and response.
HTTP Versions in API Design Recommended
Three versions are in use.
- HTTP/1.1: text-based, one request at a time per connection, so browsers open several connections. Head-of-line blocking limits throughput.
- HTTP/2: binary framing, many concurrent streams on one connection, header compression and bidirectional streams. gRPC requires it.
- HTTP/3: HTTP over QUIC, which runs on UDP. Faster connection set-up and no TCP-level head-of-line blocking, which helps mobile networks.
The semantics of methods, status codes and headers stay the same across versions, so your API design does not change. What changes is performance: with HTTP/2 or 3, many small requests are cheaper, which weakens the old case for bundling everything into one large call.
Practice. Check which HTTP version a few major APIs negotiate using curl --http2 and browser developer tools.
Stage 2 HTTP Building Blocks
Goal. Use methods, status codes, headers, content negotiation, cookies and CORS correctly in your own endpoints.
HTTP Methods Core
| Method | Use | Safe | Idempotent |
|---|---|---|---|
| GET | Read a resource | Yes | Yes |
| POST | Create, or run an action | No | No |
| PUT | Replace a resource | No | Yes |
| PATCH | Partially update | No | Not by default |
| DELETE | Remove | No | Yes |
Safe means no change on the server; idempotent means repeating the request has the same effect as sending it once. These properties let clients, proxies and retry logic behave correctly. A GET that deletes data breaks caches and crawlers. HEAD and OPTIONS also matter: the second is used by CORS preflight.
Practice. Design the methods and paths for managing a user's saved addresses, marking which calls are safe to retry.
HTTP Status Codes Core
Status codes tell the client what happened without parsing the body. Use a small, consistent set:
200OK,201Created with aLocationheader,202Accepted for async work,204No Content.400malformed request,401not authenticated,403authenticated but not allowed,404not found,409conflict,422validation failed,429too many requests.500server bug,502and504upstream problems,503temporarily unavailable.
The 4xx range means the client should change something; 5xx means the client may retry later. Never return 200 with an error in the body, because clients, monitoring and retry logic all rely on the code.
Practice. Write a table of every status code your sample API returns and the exact situation for each.
HTTP Headers in API Design Core
Headers carry metadata about the request or response. The ones API designers use daily:
Content-TypeandAcceptfor formats.Authorizationfor credentials.Cache-Control,ETag,If-None-MatchandIf-Matchfor caching and optimistic concurrency.Locationfor newly created resources.Retry-Afterwith 429 and 503.Idempotency-Keyfor safe retries of POST.- A request ID such as
X-Request-Id, or the W3Ctraceparent, for tracing.
The X- prefix for custom headers is discouraged now; choose clear names instead. Keep business data in the body and path, not in custom headers that proxies may strip.
Practice. Add ETag and If-Match support to an update endpoint so two clients cannot silently overwrite each other's changes.
Content Negotiation in API Design Recommended
Content negotiation in API design lets one URL serve different representations. The client states preferences with Accept, such as application/json or text/csv, and optionally Accept-Language and Accept-Encoding. The server picks the best match, says what it chose in Content-Type, and returns 406 Not Acceptable if it can serve none.
GET /v1/reports/42
Accept: text/csv, application/json;q=0.8
Accept-Encoding: gzip, brAdd Vary: Accept so caches store each variant separately. Most JSON APIs only need JSON plus compression, but negotiation is useful for exports and for media-type versioning.
Practice. Make one endpoint return JSON or CSV depending on the Accept header and return 406 for anything else.
Cookies in API Design Recommended
Cookies are small values the server sets with Set-Cookie and the browser sends back automatically. In API design they mostly carry session IDs for browser clients.
Always set these attributes on session cookies:
HttpOnlyso JavaScript cannot read it, limiting damage from XSS.Secureso it is only sent over HTTPS.SameSite=LaxorStrictto reduce cross-site request forgery.
Because the browser attaches cookies automatically, cookie-authenticated APIs need CSRF protection for state-changing requests. Non-browser clients such as mobile apps and servers usually use tokens in the Authorization header instead. Browsers increasingly block third-party cookies, so do not design cross-site flows that depend on them.
Practice. Set a session cookie with all three security attributes and confirm in browser tools that scripts cannot read it.
CORS under API Design Core
Browsers block a page from reading responses from another origin, meaning another scheme, host or port, unless the server allows it. Cross-Origin Resource Sharing is how the server grants that permission with response headers.
For non-simple requests, such as JSON bodies or custom headers, the browser first sends an OPTIONS preflight. The server answers with Access-Control-Allow-Origin, -Methods, -Headers and optionally -Max-Age to cache the answer.
Key rules: list exact allowed origins rather than * when credentials are involved, because the wildcard is not allowed with cookies. CORS is a browser rule, not a security boundary: curl and servers ignore it, so still authenticate every request.
Practice. Call your API from a page on a different port, read the CORS error, and fix it by allowing only that origin.
Stage 3 Designing RESTful APIs
Goal. Model resources and design clean, consistent RESTful endpoints with JSON payloads.
REST Principles in API Design Core
REST is an architectural style with a few constraints: client-server separation, stateless requests, cacheable responses, a uniform interface, a layered system and, optionally, code on demand. The uniform interface is the part you design daily: resources are identified by URLs, manipulated through representations such as JSON, messages are self-describing, and responses can link to related actions.
Applying REST principles in API design gives clients predictability. Once they know how /orders works, they can guess /invoices. Few real APIs follow every constraint, and that is fine; what matters is consistency and using HTTP semantics correctly. Call an API RESTful only if it is resource-based, not if it is just JSON over HTTP.
Practice. Review a public API and list where it follows and departs from each REST constraint.
Resource Modeling Core
Resource modelling is deciding which nouns your API exposes and how they relate. Start from the domain and the client's tasks, not your database tables. A single table can back several resources, and one resource can join several tables.
- Collections and items:
/customersand/customers/{id}. - Sub-resources for strong ownership:
/orders/{id}/items. Keep nesting to one or two levels. - Actions that do not fit CRUD become resources or sub-resources:
POST /orders/{id}/cancellationrather than/cancelOrder. - Long-running work becomes a job resource you can poll.
Expose stable public IDs, not auto-increment database keys, which leak volume and are easy to guess.
Practice. Model resources for a cinema booking API, including an action that does not map to CRUD.
URI Design in API Core
URI design in API work follows a few naming conventions that most teams agree on:
- Plural nouns for collections:
/users, not/getUsers. - Lowercase with hyphens in paths:
/payment-methods. - No verbs, because the method is the verb.
- No file extensions; use content negotiation.
- Pick one casing for JSON fields, usually camelCase or snake_case, and use it everywhere.
- Consistent ID formats, often prefixed by type, such as
cus_8f2k, which makes logs easier to read.
Naming conventions matter more than which convention you choose. Write them in a style guide and enforce them with an OpenAPI linter such as Spectral.
Practice. Write a one-page naming style guide for a team API and lint an existing spec against it.
URL, Query & Path Parameters Core
Each part of a URL has a job.
GET /v1/stores/42/products?category=books&sort=-price&limit=20
└ path ─┘ └ path param ┘ └──────── query parameters ────────┘- Path parameters identify a specific resource:
/stores/42. - Query parameters modify the view of a collection: filters, sorting, pagination, field selection.
- Body carries the data you create or change.
- Headers carry metadata such as auth and formats.
Do not put secrets or personal data in URLs, because they end up in logs, browser history and proxies. Encode values properly and validate types and ranges on the server.
Practice. Classify ten pieces of input for a product search endpoint as path, query, header or body, and explain each choice.
Handling CRUD Operations in API Design Core
Handling CRUD operations in API design follows a standard mapping:
POST /v1/notes -> 201 Created + Location: /v1/notes/n_1
GET /v1/notes -> 200 list (paginated)
GET /v1/notes/n_1 -> 200 or 404
PATCH /v1/notes/n_1 -> 200 updated note
PUT /v1/notes/n_1 -> 200 full replacement
DELETE /v1/notes/n_1 -> 204 (repeat: 204 or 404)Decisions to make explicitly: return the created or updated resource so clients avoid a second call; choose between PATCH with JSON Merge Patch and full PUT; decide between hard and soft deletes; and protect against lost updates with ETag and If-Match. Validate everything on the server, even if the client already did.
Practice. Build a notes API with all five operations, validation, and a 412 response when an If-Match ETag is stale.
Building JSON / RESTful APIs Core
JSON is the default payload format for web APIs. Conventions that keep JSON payloads easy to use:
- Use ISO 8601 strings in UTC for dates:
"2025-03-01T09:30:00Z". - Send money as integer minor units or decimal strings with a currency code, never floats.
- Send large IDs as strings, because JavaScript numbers lose precision above 2^53.
- Wrap collections in an object, such as
{"data": [...], "nextCursor": "..."}, so you can add metadata later. - Be explicit about
nullversus a missing field. - Use enums as strings and tell clients to tolerate unknown values.
Generate server and client types from one schema where possible, so the code and docs cannot disagree.
Practice. Design the JSON for an invoice resource that includes money, dates, a status enum and line items, following these rules.
HATEOAS in API Design Optional
HATEOAS, hypermedia as the engine of application state, is the REST constraint where responses include links to the actions available next, so clients follow links instead of building URLs.
{
"id": "ord_9",
"status": "unpaid",
"_links": {
"self": { "href": "/v1/orders/ord_9" },
"pay": { "href": "/v1/orders/ord_9/payment" },
"cancel": { "href": "/v1/orders/ord_9/cancellation" }
}
}HATEOAS in API design is rare in practice because most clients are written against documentation and generated SDKs. Lightweight uses are common, though: pagination links, a Location header, and including only the actions the current user may take.
Practice. Add next and previous links to a paginated endpoint and action links that depend on the resource's status.
Stage 4 Designing the Contract
Goal. Design pagination, filtering, errors, idempotency, versioning and documentation that hold up as the API grows.
Pagination in API Design Core
Every collection endpoint needs pagination from the start; adding it later breaks clients. Two main approaches:
- Offset:
?limit=20&offset=40. Simple, allows jumping to a page, but slow on deep pages and can skip or repeat items when data changes. - Cursor or keyset:
?limit=20&after=eyJpZCI6.... The cursor encodes the position, such as the last ID and sort key. Stable under inserts and fast at any depth, but no random page access.
Good pagination in API design means an opaque cursor, a sensible default limit and an enforced maximum, a stable sort with a unique tie-breaker, and a nextCursor or link in the response. Avoid returning total counts on large tables, because they are expensive.
Practice. Implement cursor pagination on a table of a million rows and compare its speed with offset paging at page 5,000.
Filtering, Sorting & Search Core
Collections need ways to narrow and order results.
- Filters as query parameters:
?status=open&createdAfter=2025-01-01. For ranges, use clear suffixes or bracket syntax, and document it. - Sorting:
?sort=-createdAt,name, with a minus for descending. - Free-text search:
?q=red+shoes, often backed by a search engine rather than SQL. - Sparse fields:
?fields=id,nameto shrink payloads.
Allow only fields you have indexes for, and validate every parameter so clients cannot trigger slow queries. Reject unknown parameters or document that they are ignored. When filters get complex, a POST /search with a JSON body is acceptable.
Practice. Add filtering by two fields, multi-field sorting and sparse fieldsets to a list endpoint, with validation for each.
Error Handling in API Design Core
Good error handling in API design gives clients a correct status code plus a consistent, machine-readable body. RFC 7807, problem details for HTTP APIs, since revised as RFC 9457, defines a standard format with the media type application/problem+json. Use RFC 7807, Problem Details for HTTP APIs, in error handling everywhere so clients parse one shape.
{
"type": "/problems/insufficient-funds",
"title": "Insufficient funds",
"status": 422,
"detail": "Balance is 30.00 GBP but the transfer needs 50.00 GBP.",
"instance": "/v1/transfers/tr_81",
"errors": [{ "field": "amount", "code": "too_large" }]
}Include a stable error code clients can branch on, field-level validation errors, and a request ID. Never return stack traces or SQL errors.
Practice. Implement a shared error handler that returns problem details for validation, not-found and unexpected errors.
Idempotency in API Design Core
Networks fail after the server has acted but before the client gets the reply, so clients retry. Idempotency in API design makes those retries safe. GET, PUT and DELETE are idempotent by definition; POST is not, so a retried payment could charge twice.
The usual remedy works like this:
- For each attempt at one business action, the caller invents a random token and attaches it as
Idempotency-Key. - Inside the same transaction as the payment, the API records that token alongside the response it produced.
- If the token shows up again, the API replays the saved response rather than repeating the work; if the token returns with a different body, it refuses.
Expire keys after a set period, and handle concurrent duplicates with a lock or unique constraint.
Practice. Add idempotency keys to a create-payment endpoint and prove with a test that two identical requests create one payment.
Versioning Strategies in API Design Core
Public APIs need a plan for breaking changes. Common versioning strategies in API design:
- URL path:
/v1/orders. Most common, visible and easy to route. - Header or media type:
Accept: application/vnd.example.v2+json. Cleaner URLs, harder to test in a browser. - Date-based versions pinned per client, which some large payment APIs use.
More important than the mechanism is avoiding breaking changes. Additive changes, such as new optional fields or new endpoints, need no new version if clients ignore unknown fields. Breaking changes include removing or renaming fields, changing types and tightening validation. When you must break, run versions side by side and announce deprecation with a timeline.
Practice. List ten changes to an API and classify each as safe or breaking.
Batch Processing in API Design Recommended
Batch processing in API design lets clients send many operations in one request, cutting round trips and per-request overhead.
- Bulk endpoints:
POST /v1/contacts/batchwith an array of items. - Async jobs for large batches: return
202 Acceptedwith a job URL the client polls, or notify with a webhook.
Decide upfront whether a batch is all-or-nothing or allows partial success. For partial success, return a result per item with its own status and error, often with 207 Multi-Status or a 200 containing per-item results. Cap the batch size, make each item idempotent, and document the limits.
Practice. Design a bulk import endpoint for 10,000 contacts that returns a job, reports per-row errors and can be safely retried.
Swagger / Open API Core
OpenAPI, formerly called Swagger, is the standard machine-readable format for describing HTTP APIs in YAML or JSON: paths, parameters, schemas, auth and examples. Swagger is now the name of a tool family around it, such as Swagger UI and Swagger Editor.
paths:
/v1/notes/{id}:
get:
parameters:
- { name: id, in: path, required: true, schema: { type: string } }
responses:
"200": { content: { application/json: { schema: { $ref: "#/components/schemas/Note" } } } }
"404": { $ref: "#/components/responses/NotFound" }Design-first teams write the spec before code, review it, then generate server stubs, client SDKs, mocks and docs. Lint it in CI so it stays consistent.
Practice. Write an OpenAPI spec for the notes API, lint it, and generate a typed client from it.
API Documentation Tools Recommended
Good docs decide whether developers adopt an API. Include a quick start, authentication steps, a reference for every endpoint with examples, error codes, rate limits, a changelog and SDK samples.
Tools you will hear about:
- Swagger UI and Redoc: render reference docs from OpenAPI.
- Stoplight: Stoplight API design tooling offers a visual editor, style-guide linting and mocking for design-first work.
- ReadMe: Readme.com in the context of API design is a hosted developer portal with try-it consoles and usage analytics.
- Postman: collections double as runnable documentation.
Generate reference pages from the spec so they never drift, and hand-write guides for common tasks.
Practice. Publish reference docs for your notes API from its spec and write a five-minute quick-start guide.
Stage 5 Authentication and Authorisation
Goal. Choose and implement the right authentication method, and design authorisation that is correct for every object.
Authentication Methods in API Design Core
Authentication answers who is calling. The main authentication methods in API design:
- API keys: simple secrets that identify an application, for server-to-server use.
- Session cookies: for browser apps on the same site.
- Bearer tokens, often JWTs, issued after login or through OAuth 2.0.
- Mutual TLS: both sides present certificates, common between internal services and in banking.
- Signed requests: HMAC signatures over the request, as in AWS Signature Version 4 and many webhooks.
Basic auth in API design sends username:password base64-encoded in every request; it is only acceptable over TLS for internal tools. Whatever you choose, always use HTTPS, and keep authentication separate from authorisation.
Practice. For a public partner API, a single-page app and an internal service, choose an authentication method and explain why.
Session Based Authentication in API Design Recommended
Session based authentication in API design stores login state on the server. After the user logs in, the server creates a session record and sends its random ID in a cookie. Each request sends the cookie, and the server looks the session up.
Strengths: revoking access is instant (delete the session), the cookie holds no data worth stealing apart from the ID, and it works naturally in browsers. Weaknesses: a shared session store such as Redis is needed when you run many servers, CSRF protection is required, and it suits mobile and third-party clients poorly.
Regenerate the session ID on login to prevent session fixation, and set idle and absolute timeouts.
Practice. Implement login, logout and session expiry with sessions stored in Redis, and test that logout invalidates the cookie.
Token Based Auth in API Design Core
Token based auth in API design means the client sends a token in the Authorization: Bearer header on each request. The token may be opaque, a random string the server looks up, or self-contained, such as a JWT that the server verifies by signature without a lookup.
A typical pattern pairs a short-lived access token, minutes long, with a longer-lived refresh token used only to get new access tokens. Rotate refresh tokens on each use and detect reuse to catch theft.
Where to store tokens in a browser is a common interview question: memory or an HttpOnly cookie is safer than localStorage, which any injected script can read. Many teams put a backend for front end in charge of tokens so the browser only holds a session cookie.
Practice. Add access and refresh tokens to an API, with refresh-token rotation and reuse detection.
JSON Web Token (JWT) in API Design Core
A JWT is three base64url parts joined by dots: a header, a payload of claims and a signature. The server verifies the signature and the claims instead of looking up a session.
{ "iss": "auth.example.com", "sub": "user_42",
"aud": "orders-api", "exp": 1767225600, "scope": "orders:read" }Verification rules: check the signature with an allow-listed algorithm, never accept none; check exp, iss and aud; fetch public keys from a JWKS endpoint for asymmetric algorithms such as RS256 or ES256. The payload is readable by anyone, so never put secrets in it. JWTs cannot be revoked before expiry without extra state, so keep them short-lived.
Practice. Decode a JWT by hand, then write middleware that verifies signature, expiry, issuer and audience.
OAuth 2.0 Core
OAuth 2.0 is a framework for delegated authorisation: a user lets an application access their data on another service without sharing their password. Roles are the resource owner, the client, the authorisation server and the resource server.
Flows to know:
- Authorisation code with PKCE: the standard for web, mobile and single-page apps.
- Client credentials: machine-to-machine, no user.
- Device code: for TVs and command-line tools.
The implicit and password grants are deprecated; OAuth 2.1 drops them. OAuth handles authorisation; OpenID Connect adds an ID token so the client also learns who the user is. Use a proven identity provider rather than building your own server.
Practice. Implement login with the authorisation code flow and PKCE against an identity provider's sandbox, and inspect each redirect.
Scopes & Permissions Core
Scopes limit what a token can do, such as orders:read or payments:write. The client requests scopes, the user or admin consents, and the API checks the scope on every endpoint.
Scopes describe what the application may do on the user's behalf. They do not replace per-user permissions: a token with orders:read must still only read orders the user is allowed to see.
Design guidance: keep scopes coarse enough to be understandable on a consent screen, split read from write, separate dangerous operations such as deletes or payouts, and grant least privilege by default. Document which scope every endpoint needs.
Practice. Define scopes for a calendar API and map every endpoint to the scope it requires.
Authorization Methods in API Design Core
Authorisation answers is this caller allowed to do this to this object. Authorization methods in API design:
- RBAC: role based access control in API design attaches permissions to roles such as admin or editor. Simple; explodes when roles multiply.
- Attribute Based Access Control: rules over attributes of user, resource and context, such as department, region or time.
- ReBAC, relationship-based: access follows relationships, such as owner of a folder that contains the file. Popular for sharing models; tools include OpenFGA and SpiceDB.
- Policy-Based Access Control: rules written as policy code, for example with Open Policy Agent or Cedar, outside the application.
- Discretionary Access Control lets owners grant access; Mandatory Access Control enforces system-wide labels, as in military systems.
Practice. Model sharing for a document app with RBAC and with ReBAC, and compare how each handles nested folders.
API Keys & Management Recommended
API keys identify the calling application for metering, rate limiting and simple access control. They are not user identity and should not be used from browsers or mobile apps, where they can be extracted.
Good key management:
- Show the key once at creation; store only a hash, like a password.
- Use a recognisable prefix, such as
sk_live_, so leaked keys can be detected by secret scanners. - Allow several active keys per account and scope each one.
- Record last-used time and let owners revoke keys instantly.
Key generation and rotation: generate keys from a cryptographically secure random source with at least 128 bits of entropy, and support rotation with overlap, where the old and new keys both work for a period.
Practice. Build key creation, hashing, prefix display, revocation and last-used tracking for an API.
Stage 6 Security, Rate Limits and Compliance
Goal. Defend an API against common attacks, protect it with rate limits, and handle personal and regulated data correctly.
Common Vulnerabilities in API Design Core
The OWASP API Security Top 10 lists the most frequent API flaws. The ones to know cold:
- Broken object level authorisation: changing
/orders/123to/orders/124returns someone else's order. The top issue; check ownership on every object access. - Broken authentication: weak tokens, no brute-force protection.
- Broken object property level authorisation: returning or accepting fields the user should not see or set, such as
role; mass assignment is one form. - Unrestricted resource consumption: no limits on size, rate or cost.
- Broken function level authorisation: admin endpoints reachable by normal users.
- Server-side request forgery: the API fetches attacker-chosen URLs.
Injection still matters: use parameterised queries and validate input.
Practice. Write an automated test that logs in as one user and tries to read, update and delete another user's resources.
API Security Core
Baseline API security practices:
- HTTPS everywhere, with HSTS.
- Authenticate every request and authorise every object and field.
- Validate input against a schema: types, lengths, ranges and allowed values. Reject unknown fields.
- Use allow-lists for response fields via DTOs.
- Limit payload size, page size, query depth and timeouts.
- Keep secrets in a secrets manager, never in code.
- Log security events without logging tokens or personal data.
- Keep dependencies patched and scan them.
Defence in depth matters: a gateway can enforce auth and rate limits, but the service must still check object ownership, because only it knows the data.
Practice. Run a security checklist against one of your APIs and fix the three most serious gaps.
Rate Limiting / Throttling in API Design Core
Rate limiting in API design caps how many requests a client can make in a time window. It protects capacity, prevents abuse and keeps usage fair. Throttling is the related practice of slowing or queuing excess traffic rather than rejecting it.
Algorithms:
- Fixed window: simple; allows bursts at window edges.
- Sliding window: smoother counting.
- Token bucket: tokens refill at a steady rate and allow controlled bursts. The most common choice.
- Leaky bucket: processes at a constant rate.
Limit per API key, user or IP, store counters in a shared store such as Redis, and return 429 with Retry-After and remaining-quota headers. Apply tighter limits to expensive and login endpoints.
Practice. Implement a token bucket limiter in Redis and verify it with a script that sends bursts of requests.
Standards and Compliance in API Design Recommended
APIs that handle personal data must meet legal requirements. Standards and compliance in API design start with knowing what PII is: personally identifiable information, such as names, emails, addresses, IDs, IP addresses and location.
- GDPR in API design, for people in the EU: lawful basis, data minimisation, purpose limits, and rights to access, correct, export and erase data. Your API needs endpoints or processes for these.
- CCPA under standards and compliance in API design, for California residents: rights to know, delete and opt out of sale or sharing.
Practical design: collect only fields you need, mask PII in logs, encrypt it at rest and in transit, set retention periods, and track where each field flows. Involve your legal or privacy team; this roadmap is not legal advice.
Sector rules add more. HIPAA in API design covers US health data and demands audit logs of every access. PCI DSS in API design covers card data; the best move is never to hold card numbers, using a provider's tokenisation instead.
Practice. Map every personal field in a sample API, its purpose and retention, and design an export and erase endpoint.
Stage 7 Beyond REST: GraphQL, gRPC, Real-Time and Events
Goal. Choose between REST, GraphQL, gRPC, real-time and asynchronous APIs, and design each one properly.
GraphQL APIs Recommended
GraphQL APIs expose one endpoint and a typed schema. Clients send queries naming exactly the fields they need, possibly across related objects, and get that shape back.
query {
order(id: "ord_9") {
status
customer { name }
items { product { title } quantity }
}
}Strengths: no over- or under-fetching, one round trip for nested data, a schema that doubles as documentation. Challenges: HTTP caching is harder, the N+1 problem in resolvers needs DataLoader-style batching, and malicious deep queries need depth and cost limits. Errors usually come back with status 200 in an errors array. The GraphQL Roadmap covers it in depth.
Practice. Build a small GraphQL schema for orders and customers, then fix the N+1 query with batching.
gRPC Recommended
gRPC is a remote procedure call framework. You define services and messages in Protocol Buffers, and tools generate typed client and server code in many languages. It runs over HTTP/2 and encodes messages in a compact binary format.
service Inventory {
rpc GetStock (StockRequest) returns (StockReply);
rpc WatchStock (StockRequest) returns (stream StockReply);
}
message StockRequest { string sku = 1; }
message StockReply { string sku = 1; int32 quantity = 2; }It supports unary calls and client, server and bidirectional streaming, with deadlines and cancellation built in. It suits internal service-to-service traffic. Browsers cannot call it directly without gRPC-Web or a proxy. Never reuse or renumber field tags, which keeps old and new versions compatible.
Practice. Define a proto for an inventory service, generate code in two languages and make a streaming call between them.
Real-time APIs Recommended
Real-time APIs push data to clients as it changes. Three options:
- Web Sockets in API design: a full-duplex connection upgraded from HTTP. Both sides send messages at any time. Good for chat, collaboration and games. You must design your own message protocol, heartbeats, reconnection and authentication.
- Server Sent Events under real-time APIs: a one-way stream from server to client over plain HTTP with the
text/event-streamtype. Automatic reconnection and resume viaLast-Event-ID. Ideal for notifications, live feeds and streaming AI output. - Long polling: a fallback where the server holds a request open until data arrives.
Long-lived connections complicate load balancing and scaling; plan for sticky routing or a shared pub/sub backplane.
Practice. Build a live order-status page twice, once with server-sent events and once with WebSockets, and compare the code.
Streaming Responses Recommended
Streaming responses send data in chunks as it becomes available instead of waiting for the whole result. Uses include large exports, log tails and LLM output that appears token by token.
Mechanisms: chunked transfer encoding in HTTP/1.1, streams in HTTP/2, server-sent events, NDJSON (one JSON object per line), and gRPC server streaming.
Design points: once the status code is sent you cannot change it, so errors mid-stream must be signalled inside the stream; set read timeouts appropriately; make sure proxies and gateways do not buffer the response; and decide whether a client can resume after a disconnect.
Practice. Stream a large CSV export as NDJSON and handle an error that occurs halfway through.
Webhooks vs Polling in API Design Core
Webhooks vs polling in API design is about how clients learn that something changed. With polling, the client asks repeatedly, which is simple but wasteful and delayed. With webhooks, your server sends an HTTP POST to a URL the client registered when an event happens.
Webhook design checklist:
- Sign each payload with HMAC and a timestamp so receivers can verify it and reject replays.
- Retry with exponential backoff, and expect receivers to see duplicates; include an event ID.
- Do not guarantee order; include timestamps or versions.
- Keep payloads small or let receivers fetch the latest state.
- Offer a log of deliveries and a way to replay them.
Many providers offer both: webhooks for speed, a list endpoint for reconciliation.
Practice. Build a webhook sender with signing and retries, and a receiver that verifies signatures and ignores duplicates.
Synchronous vs Asynchronous APIs Core
A synchronous API does the work and replies in the same request. An asynchronous API accepts the request, replies quickly, and completes the work later.
POST /v1/videos/v_7/transcode -> 202 Accepted
Location: /v1/jobs/job_31
GET /v1/jobs/job_31 -> 200 {"status": "running", "progress": 0.4}Go asynchronous when work takes more than a few seconds, depends on slow third parties, or must survive client disconnects. Clients learn the result by polling a job resource, receiving a webhook, or subscribing to events. Asynchronous designs need job state, idempotency, timeouts and clear failure states.
Practice. Convert a slow report endpoint into an async job with a status resource and a completion webhook.
Event Driven Architecture in API Design Recommended
Event driven architecture in API design means services publish events such as OrderPlaced and other services react, rather than calling each other directly. The producer does not know its consumers, so new features plug in without changing it.
Event contracts are APIs too. Give each event a name, a version, an ID, a timestamp and a schema, and evolve it with the same compatibility rules as HTTP payloads. Schema registries and formats such as Avro or Protobuf help. AsyncAPI is the specification for documenting event-driven interfaces, much as OpenAPI is for HTTP.
Costs: eventual consistency, harder debugging, and duplicate or out-of-order delivery, so consumers must be idempotent.
Practice. Write an AsyncAPI document for three events in an order system, including versioning rules.
Messaging Queues in API Design Recommended
Brokers carry events and commands between services.
- RabbitMQ in API design: a traditional message broker with exchanges, routing keys and queues. Messages are removed once acknowledged. Strong for task queues and flexible routing.
- Kafka in API design: a distributed, partitioned log. Messages are kept for a retention period, consumer groups track offsets and can replay. Strong for high-throughput event streams and feeding many consumers.
- Managed services such as Amazon SQS and SNS, Google Pub/Sub and Azure Service Bus.
Design concerns: at-least-once delivery, dead-letter queues, ordering per partition or queue, partition keys, and the transactional outbox so database writes and published events never disagree.
Practice. Publish order events to a broker and consume them in two services, adding a dead-letter queue for failures.
Stage 8 Performance, Gateways and Integration
Goal. Make an API fast and resilient under load, and place gateways, BFFs and integration patterns correctly.
Caching Strategies in API Design Core
Caching strategies in API design work at several layers: the client, a CDN, a gateway, the application and the database.
HTTP caching in API design uses standard headers:
Cache-Control: public, max-age=300for shared, cacheable data;privatefor per-user data;no-storefor sensitive data.ETagwithIf-None-Matchlets clients revalidate and receive304 Not Modifiedwithout a body.Varytells caches which request headers change the response.
Inside the service, cache-aside with Redis is the common pattern: read the cache, fall back to the database, then store the result with a TTL. The hard part is invalidation; prefer short TTLs and explicit eviction on writes, and watch for stampedes when a hot key expires.
Practice. Add ETags and Cache-Control to a read endpoint and confirm 304 responses with curl.
API Performance Core
API performance is mostly about latency and throughput. Common causes of slow APIs and their fixes:
- N+1 database queries: batch or join.
- Missing indexes for filters and sorts: add them.
- Large payloads: paginate, compress with gzip or Brotli, allow sparse fields.
- Chatty clients: offer composite endpoints or a BFF.
- Slow downstream calls: run them in parallel, cache, or move work to async jobs.
- Connection set-up: reuse connections and use connection pools.
Measure before you optimise. Performance metrics in API design start with latency percentiles, p50, p95 and p99, because averages hide the slow tail, plus throughput, error rate and saturation. Fix the biggest cost, then measure again.
Practice. Profile your slowest endpoint, find its biggest time cost and halve its p95 latency.
Load Balancing in API Design Recommended
A load balancer spreads requests across instances so you can scale out and survive failures. Layer 4 balancers route TCP connections; layer 7 balancers understand HTTP and can route by path or header, terminate TLS and retry.
Algorithms include round robin, least connections and consistent hashing for affinity. Health checks remove unhealthy instances. Load balancing in API design works best when services are stateless: keep sessions in a shared store so any instance can handle any request. Long-lived connections such as WebSockets and gRPC streams need care, because connection-level balancing can leave load uneven.
Practice. Put two instances of an API behind a local load balancer, kill one and observe how traffic shifts.
API Gateways Core
An API gateway is a single entry point in front of your services. It handles cross-cutting concerns so each service does not have to: TLS termination, authentication, rate limiting, routing, request transformation, caching, logging and API key management. Examples include Kong, AWS API Gateway, Azure API Management, Apigee and Envoy-based gateways.
Keep business logic out of the gateway; it becomes a bottleneck owned by one team, the same trap that sank the enterprise service bus. Services must still authorise access to their own objects. A gateway also adds a hop, so watch its latency and make it highly available.
Practice. Put a gateway in front of two services, routing by path and enforcing an API key and a rate limit.
BFF Pattern Recommended
The backend for front end pattern gives each client type, such as web, iOS or a partner integration, its own thin API layer. The BFF calls the underlying services, aggregates and reshapes data for that client's screens, and is usually owned by the front-end team.
Benefits: fewer round trips from mobile, payloads tailored to each screen, and freedom for each client to evolve without bending a general API. A BFF is also a good place to hold OAuth tokens on the server side so the browser only has a secure session cookie.
Risks: duplicated logic across BFFs, and business rules creeping in. Keep them thin, and push shared rules into the core services.
Practice. Build a BFF endpoint that combines three service calls into one response shaped for a mobile home screen.
Microservices Architecture Recommended
In a microservices architecture, APIs are the seams between independently deployed services. That raises the stakes on API design: contracts must be versioned carefully, calls cross the network and can fail, and there are no shared transactions.
API integration patterns to know:
- Request-response over REST or gRPC for queries that need an answer now.
- Events for propagating changes.
- Saga for multi-step business processes, with compensating actions instead of rollbacks.
- API composition for queries spanning services.
- Anti-corruption layers for wrapping legacy or third-party APIs.
Keep synchronous call chains short; long chains multiply latency and failure.
Practice. Design a checkout across order, payment and inventory services as a saga, listing each compensating action.
Error Handling / Retries Core
Clients and services calling remote APIs must expect failure. Resilient error handling and retries rely on a few tools:
- Timeouts on every call, shorter than the caller's own deadline.
- Retries only for transient errors, such as timeouts, 429, 502, 503 and 504, and only for idempotent operations or ones with idempotency keys.
- Exponential backoff with jitter, so clients do not retry in lockstep, and a retry budget.
- Circuit breakers that stop calling a failing dependency for a while.
- Fallbacks such as cached data or a degraded response.
Retrying at every layer multiplies traffic during an outage; retry at one layer only.
Practice. Wrap a flaky dependency with timeouts, jittered retries and a circuit breaker, and test it with injected failures.
Stage 9 Testing, Observability and Lifecycle
Goal. Test an API at every level, observe it in production, and manage it from design to retirement.
API Testing Core
API testing works in layers, from many fast tests to a few slow ones.
- Unit testing in API design covers validation, mapping and business logic with no network or database.
- Integration testing in API design runs the real service against real dependencies, often with Testcontainers for databases and brokers.
- Functional testing in API design calls endpoints over HTTP and checks status codes, bodies, headers and side effects against the specification.
- End-to-end tests cover a few critical journeys across services.
Test negative cases as carefully as happy paths: invalid input, missing auth, another user's objects, rate limits and timeouts. For exploration, Postman in API design work, or Bruno, Insomnia and curl, is handy; keep the regression suite in code that runs on every commit.
Practice. Write unit, integration and functional tests for one endpoint, including at least five negative cases.
Mocking APIs under API Testing Recommended
A mock API returns realistic responses without the real service. Mocking APIs under API testing lets front-end teams build before the back end is ready, makes tests independent of flaky third parties, and lets you simulate errors and slowness on demand.
Approaches: generate a mock server from the OpenAPI spec with tools such as Prism; use WireMock or Mock Service Worker to intercept HTTP calls in tests; or write fakes behind an interface in your code.
Mocks drift from reality, so pair them with contract tests or periodic checks against the real API.
Practice. Generate a mock server from your OpenAPI spec and point a client at it, then make it return a 503.
Contract Testing in API Design Recommended
Contract testing in API design checks that a provider and its consumers agree on the API without running everything together. In consumer-driven contracts, with tools such as Pact, each consumer records the requests it makes and the responses it relies on. The provider's build then replays those contracts against the real service and fails if it would break a consumer.
A lighter form is schema-based: validate real responses against the OpenAPI spec in tests, and detect breaking spec changes with a diff tool in CI.
Contract tests are much faster and more reliable than full end-to-end environments for catching breaking changes between services.
Practice. Add a schema-diff check to CI that fails when a pull request removes a field or makes an optional field required.
Performance Testing in API Design Recommended
Performance testing in API design checks speed and capacity before users do. Load testing in API design simulates expected traffic to confirm you meet latency targets. Related types:
- Stress tests push past capacity to find the breaking point and how the system fails.
- Spike tests apply sudden bursts.
- Soak tests run for hours to expose leaks.
Tools include k6, Gatling, Locust and JMeter. Test against a production-like environment with realistic data volumes and request mixes, warm up first, and report percentiles, not averages. Watch the database and downstream services during the test, not only the API.
Practice. Write a k6 script that ramps to your target load and fails if p95 latency exceeds your SLO.
Observability Core
Observability is being able to explain what an API is doing in production from the data it emits. Three signals:
- Logs: structured JSON with request ID, route, status, latency and user or client ID, never tokens or raw PII.
- Metrics: request rate, errors and latency per endpoint, plus saturation.
- Traces: one trace per request across services, with spans for each call, propagated with the W3C
traceparentheader.
OpenTelemetry is the vendor-neutral standard for producing all three. Profiling and monitoring in API design go together: continuous profilers show which functions burn CPU, and synthetic checks call key endpoints from outside to catch outages first.
Practice. Instrument a service with OpenTelemetry and follow one slow request through its trace to the slow span.
API Lifecycle Management Recommended
API lifecycle management covers an API from idea to retirement: design, review, build, test, publish, operate, version and deprecate.
Practices that keep it healthy:
- Design reviews against a style guide before code is written.
- A catalogue of all APIs with owners, specs and status.
- A changelog and clear support policy.
- Deprecation with notice: announce it, add
DeprecationandSunsetheaders, contact active users found in logs, and only remove the version once traffic has moved.
Best practices in API design come down to treating the API as a product: consistent, documented, observed and changed deliberately.
Practice. Write a deprecation plan for an old endpoint, including headers, emails, metrics and a removal date rule.
Projects to build
Build these in order. Each one should end up in a public repository with a short README that explains your decisions.
- Notes APIBeginner
Build a RESTful notes API with full CRUD, validation, consistent problem-details errors and an OpenAPI spec written first. Add optimistic concurrency with ETags.
REST · HTTP methods and status codes · OpenAPI · Error handling
- Product catalogue with searchIntermediate
Expose a catalogue of 100,000 products with cursor pagination, filters, sorting, sparse fields and HTTP caching. Add API keys with hashing and rotation, and a token bucket rate limiter with proper 429 responses.
Pagination · Filtering and sorting · HTTP caching · Rate limiting · API keys
- Payments sandboxIntermediate
Build a fake payments API with OAuth 2.0 client credentials, scopes, idempotency keys on payment creation, and signed webhooks with retries. Write tests that prove duplicate requests and duplicate webhooks are handled safely.
OAuth 2.0 · Scopes · Idempotency · Webhooks
- Real-time delivery trackerAdvanced
Create an order service that publishes events to a broker, a tracking service that consumes them, and a server-sent events endpoint that streams status to a web page. Add a gRPC call between services and a BFF for the web client.
Event-driven design · Server-sent events · gRPC · BFF pattern
- Production-ready API platformAdvanced
Put two services behind a gateway with authentication and rate limits, instrument them with OpenTelemetry, add contract tests and a k6 load test in CI, and run a v1 to v2 migration with Deprecation and Sunset headers.
API gateways · Observability · Contract testing · Load testing · Versioning
Interview questions
Try to answer each question out loud before you open the answer.
What is the difference between PUT and PATCH, and which are idempotent?Junior
PUT replaces the whole resource with the representation sent; PATCH applies a partial change. PUT is idempotent: sending the same body twice leaves the same state. PATCH is not guaranteed to be, because an operation such as incrementing a counter changes state each time, though a merge patch that sets fields usually is in practice. GET, HEAD, PUT and DELETE are idempotent; POST is not.
When do you return 401 and when 403?Junior
401 means the request is not authenticated: no credentials, or invalid or expired ones, and the client should log in or refresh its token. 403 means the caller is authenticated but not allowed to do this. For resources a user must not even know exist, many APIs return 404 instead of 403 to avoid leaking their existence.
How would you design pagination for a large, frequently changing collection?Mid
I would use cursor pagination. The cursor encodes the last item's sort key plus a unique tie-breaker such as the ID, and the query uses a keyset condition instead of OFFSET, so it is fast at any depth and does not skip or repeat items when rows are inserted. I would make the cursor opaque, set a default and maximum page size, return a next cursor, and avoid total counts on large tables.
How do you make a payment creation endpoint safe to retry?Mid
Require an Idempotency-Key header. Store the key, a hash of the request and the result in the same transaction as the payment. On a repeat with the same key, return the stored response; with a different body, return an error. Use a unique constraint or lock for concurrent duplicates and expire keys after a set period. Clients then retry on timeouts without double charging.
How do you version an API, and how do you avoid needing to?Mid
I put a major version in the path, such as /v1, because it is explicit and easy to route, though header or date-based versions also work. Mostly I avoid new versions by making only additive changes and requiring clients to ignore unknown fields and enum values. When a breaking change is unavoidable, I run versions in parallel, add Deprecation and Sunset headers, track who still uses the old version, and migrate them before removal.
Explain the OAuth 2.0 authorisation code flow with PKCE.Mid
The client creates a random code verifier and sends its hash, the code challenge, when redirecting the user to the authorisation server. The user logs in and consents, and the server redirects back with a short-lived code. The client exchanges the code plus the original verifier for tokens. PKCE stops an intercepted code being used by someone else, because they lack the verifier. It replaces the deprecated implicit flow for browser and mobile apps.
What are the risks of JWTs, and how do you mitigate them?Mid
JWTs cannot be revoked before expiry without extra state, their payload is readable, and poor verification can accept forged tokens, for example the none algorithm or algorithm confusion. I keep access tokens short-lived with rotating refresh tokens, verify with an allow-listed algorithm and keys from JWKS, check expiry, issuer and audience, keep secrets out of claims, and avoid localStorage in browsers.
What is broken object level authorisation and how do you prevent it?Mid
It is when an API checks that the caller is logged in but not that they own the object they ask for, so changing an ID in the URL returns someone else's data. It is the most common serious API flaw. I prevent it by checking ownership or permission on every object access, ideally by scoping queries to the caller, using non-guessable IDs as a second layer, and writing automated tests that try cross-user access.
How would you implement rate limiting across many API instances?Senior
I would use a token bucket per key or user, stored in a shared store such as Redis and updated atomically with a Lua script, so all instances see one count. The gateway can enforce coarse limits, while services enforce limits on expensive operations. Responses return 429 with Retry-After and quota headers. I would choose fail-open or fail-closed explicitly if Redis is unavailable and monitor how often clients hit limits.
REST, GraphQL or gRPC: how do you choose?Senior
REST for public and partner APIs: widely understood, cacheable and tool-friendly. GraphQL when many client screens need different shapes of related data and the team can handle query cost limits and resolver batching. gRPC for internal service-to-service calls where typed contracts, low latency and streaming matter. Many systems use all three: gRPC inside, a GraphQL or REST BFF for apps, and REST for partners.
A client says your API is slow. How do you investigate?Senior
First I get specifics: endpoint, time, request ID. I check p95 and p99 latency per endpoint to see whether it is general or one route. Then I follow a slow trace to find the slow span: database query, downstream call, serialisation or queueing. Typical fixes are an index, removing an N+1 query, caching, parallelising downstream calls, smaller payloads or moving work to async jobs. I confirm the fix with metrics and a load test.
How do you design error responses?Junior
I return the correct status code and a consistent JSON body using problem details: type, title, status, detail and instance, plus a stable machine-readable error code, field-level validation errors and a request ID for support. I never expose stack traces or internal messages, and I document every error code clients may need to handle.
How do you prevent breaking consumers when several teams depend on your API?Senior
I treat the spec as the source of truth and run a breaking-change diff on every pull request. Consumer-driven contract tests let each consumer state what it relies on, and my build fails if I break it. Changes are additive by default, deprecations are announced with headers and usage tracking, and I keep a changelog. That catches problems in CI rather than in production.
Common mistakes
- Returning 200 with an error message in the body; use the correct status code and a problem-details body.
- Using verbs in URLs such as /getUser or /deleteOrder; model resources and let the HTTP method carry the action.
- Leaving collections unpaginated because the table is small today; add cursor pagination and a maximum page size from the first release.
- Checking that a user is logged in but not that they own the object; authorise every object access.
- Exposing database entities directly as responses; use DTOs so internal fields and schema changes do not leak.
- Retrying non-idempotent requests blindly; add idempotency keys and retry only transient failures with backoff and jitter.
- Making breaking changes without a version or notice; prefer additive changes and run a deprecation process when you must break.
- Storing tokens in localStorage in browser apps; use HttpOnly cookies or a backend for front end.
- Writing documentation by hand that drifts from the code; generate reference docs from an OpenAPI spec checked in CI.
Ready-for-interview checklist
- I can explain each HTTP method's safety and idempotency and choose the right status codes.
- I can model resources and URLs for a new domain, including actions that do not map to CRUD.
- I can implement cursor pagination and explain why it beats offset paging on large tables.
- I can design problem-details error responses and explain them to client developers.
- I can add idempotency keys to a POST endpoint and explain how they handle concurrent duplicates.
- I can compare versioning strategies and list which changes are breaking.
- I can explain sessions, API keys, JWTs and the OAuth 2.0 code flow with PKCE, and when to use each.
- I can compare RBAC, ABAC and ReBAC and prevent broken object level authorisation.
- I can implement a token bucket rate limiter and return correct 429 responses.
- I can choose between REST, GraphQL, gRPC, webhooks, WebSockets and server-sent events for a scenario.
- I can configure HTTP caching with Cache-Control and ETags.
- I can write an OpenAPI spec and use it for docs, mocks and contract checks.
- I can instrument an API with logs, metrics and traces and debug a slow endpoint.
Related roadmaps
- System Design RoadmapScalability, caching, queues, consistency and the classic designs.
- Software Design and Architecture RoadmapClean code, SOLID, design patterns, DDD and architecture styles.
- GraphQL RoadmapSchemas, resolvers, N+1, caching, federation and security.
- Node.js RoadmapEvent loop, modules, HTTP, streams, frameworks and deployment.
- Spring Boot RoadmapDI, Spring MVC, data access, security and microservices.
- Redis RoadmapData types, caching patterns, persistence, clustering and streams.
FAQ
How long does it take to learn API design?
If you can already build a basic web back end, you can design a solid REST API after three or four weeks of focused practice. Covering security, asynchronous patterns, GraphQL, gRPC and testing to the depth senior interviews expect usually takes three to four months at about eight hours a week.
Should I learn REST or GraphQL first?
REST first. It teaches the HTTP fundamentals that every other style builds on, it is what most public APIs use, and interviews assume it. GraphQL is easier to judge fairly once you know the problems with REST that it solves, and the problems it introduces.
Is REST still relevant with GraphQL and gRPC available?
Yes. REST remains the default for public and partner APIs because it is simple, cacheable and supported by every language and tool. GraphQL and gRPC are strong in specific situations, and many companies run all three side by side for different consumers.
Which framework should I use to practise API design?
Any mainstream framework works: FastAPI, Express or NestJS, Spring Boot, ASP.NET Core or Go's standard library. Choose one in the language you already know. The design ideas in this roadmap carry across all of them, and writing the OpenAPI spec first keeps your focus on the contract rather than the framework.