REST
The resource-oriented HTTP style most public APIs still use: modelling resources, treating verbs and status codes as the contract, statelessness, caching, and versioning. Interviewers ask because 'we built a REST API' is the default claim and they want to know how much of REST you actually applied.
part ofAPI stylesoverview, primer and where to startread it →on this pageshowhide
explore
- Resources and URIs16 questions
- Resource Modeling4 questions
- URI Naming Conventions3 questions
- Collections and Sub-Resources3 questions
- Path vs Query Parameters3 questions
- Non-CRUD Actions3 questions
- HTTP Verbs and Status Codes22 questions
- Verb Selection in API Contracts5 questions
- PUT vs PATCH vs POST5 questions
- Idempotency as an API Promise3 questions
- Bulk and Batch Operations3 questions
- Status-Code Selection6 questions
- Statelessness9 questions
- What the Server May Hold3 questions
- Token Placement and Session State3 questions
- Scaling Implications3 questions
- Caching and Conditional Requests12 questions
- Cacheability as a REST Constraint4 questions
- Conditional GET as API Design4 questions
- Invalidation Strategy4 questions
- API Versioning9 questions
- Versioning Strategies3 questions
- Breaking vs Additive Change3 questions
- Deprecation and Sunset3 questions
- HATEOAS10 questions
- Hypermedia Controls and Link Relations3 questions
- HAL, JSON:API, and Hypermedia Formats4 questions
- Richardson Maturity Model3 questions
- Pagination, Filtering, and Field Selection13 questions
- Offset vs Cursor Pagination3 questions
- Page Envelopes and Link Headers3 questions
- Filtering and Sorting Grammars4 questions
- Sparse Fieldsets and Expansion3 questions
- Error Design16 questions
- Error Body Design4 questions
- Problem Details (RFC 9457)4 questions
- Validation Error Shapes4 questions
- Retry Semantics and Retry-After4 questions
- Concurrency and Idempotency13 questions
- Optimistic Concurrency with ETag/If-Match3 questions
- Idempotency Keys6 questions
- Safe-Retry Design4 questions
- AI Engineerroleanchors this topic
- Android Developerroleanchors this topic
- Backend Developerroleanchors this topic
- Frontend Developerroleanchors this topic
- Full Stack Developerroleanchors this topic
- Java Backend Developerroleanchors this topic
- Java SDETroleanchors this topic
- Kotlin Backend Developerroleanchors this topic
- QA Engineerroleanchors this topic
- iOS Developerroleanchors this topic
- AI Red Teamingrole
- API Designskill
- API Testingskill
- Forward Deployed Engineerrole
- Server-Side Game Developerrole
- Software Architectrole
questions
120 · 9 sectionsWhen designing URL paths for an HTTP API, what naming conventions do you follow, and why is POST /createUser considered worse than POST /users?
basics
~10 sPaths name things, not actions: nouns, usually plural collections (/users, /users/42/orders). The HTTP method supplies the verb, so POST /users beats POST /createUser. Verbs in paths duplicate the method and multiply near-identical endpoints.
In an HTTP API, how do you decide whether a value belongs in a path segment such as /orders/{id} or in the query string such as ?status=open?
basics
~20 sPath carries identity and hierarchy — what resource you are addressing; it is required and usually part of a single thing's URL. Query carries modifiers on a collection — filtering, sorting, pagination, field selection; these are optional and any combination is valid.
When designing an HTTP API, how do you decide between exposing a nested path like `/orders/{orderId}/items` and a top-level collection with a filter like `/items?orderId={orderId}`? What does each choice commit you to?
basics
~20 sNest when the child is owned by the parent, has no meaning or identity outside it, and is always accessed through it. Use a top-level collection with a filter when the child is independently identifiable and queried across parents. Many APIs do both: nested for creation and scoped listing, a canonical top-level URI for the item itself.
When designing an HTTP API, how do you decide what becomes a resource? Explain why a resource is not simply a database table or a domain entity, and give an example where they differ.
basics
~20 sA resource is anything worth naming with a URI and giving a representation — driven by what clients need to address, not by storage. Entities usually become resources, but resources also cover projections, computed views, and processes that no table matches, and one entity may back several resources.
Your HTTP API needs to let a client cancel an order. Cancelling is not a plain create, read, update, or delete. How do you model it, and what are the options?
basics
~20 sThree mainstream options: POST a state-transition sub-resource (POST /orders/{id}/cancellation), PATCH the status field, or an explicitly-marked custom method (POST /orders/{id}:cancel). Prefer the sub-resource when the action has its own data or history; keep POST for anything with side effects.
In an HTTP API contract, what is the difference between a method being safe and being idempotent, and which of GET, POST, PUT, PATCH and DELETE are which?
basics
~20 sSafe means the request does not change server state - GET and HEAD. Idempotent means doing it N times leaves the same state as doing it once - GET, HEAD, PUT and DELETE. POST and PATCH are neither by default. Safe implies idempotent.
When you design a write endpoint in an HTTP API, how do you choose between the PUT, PATCH and POST methods, and what does each one promise the client?
basics
~20 sPOST creates a resource or runs a process; the server picks the URL and repeating it repeats the effect. PUT sends the complete new representation to a URL you already know; repeating it is harmless. PATCH sends only the fields to change.
You are specifying the successful responses for create, update and delete endpoints in an HTTP API. How do you choose between status 200, 201 with a Location header, and 204, and when would you return a body at all?
basics
~20 s201 plus a Location header when the request created a new resource; 200 with a body when you have something useful to return; 204 when the operation succeeded and there is genuinely nothing to send. Deletes typically return 204.
Why can a client safely retry an HTTP PUT or DELETE after a timeout, but not a bare POST? Explain what actually goes wrong.
basics
~20 sA timeout hides whether the server applied the request. PUT and DELETE describe an end state, so reapplying converges to the same result. POST creates something new each time, so a retry after a lost response creates a duplicate.
Your API lets clients create resources with HTTP PUT to a URL they choose, instead of POSTing to a collection. What does that buy you, what identifier scheme does it require, and what should the server return?
basics
~20 sPUT-as-upsert makes creation idempotent: the client generates the id (usually a UUID), so a retried request lands on the same URL and cannot create a duplicate. Return 201 when it created, 200 or 204 when it replaced.
A candidate says a REST API cannot be stateless because it stores orders in a database. Explain why that is wrong by distinguishing the two kinds of state involved.
basics
~20 sTwo different things. Resource state is the durable data the API manages - orders, users - and storing it is the point of the API. Application state is per-client context between requests, like where you are in a flow. Statelessness forbids only the second.
An HTTP API can carry its credential in a cookie or in an Authorization: Bearer header. Compare the two placements, including how each is attached by the browser and what that means for cross-site request forgery.
basics
~20 sCookies are attached automatically by the browser to matching requests, which is convenient and allows HttpOnly protection from JavaScript, but that automatic sending is what enables CSRF. A Bearer header must be added explicitly by code, so a forged cross-site request carries none - but the token must be stored where script can reach it.
A REST API is described as stateless so that any instance can serve any request. Explain concretely what that means for load balancing and horizontal scaling, and what kinds of server-side data break the property.
basics
~20 sEach request carries everything needed to process it, so no instance holds per-client memory. The load balancer can send any request to any instance and you scale by adding instances. Per-client data in one instance's local memory breaks it.
Compare a self-contained signed token, such as a JWT whose claims the API validates locally, with an opaque token that the API must resolve by calling an introspection or lookup service. What does each cost at request time?
basics
~20 sA self-contained token carries its claims and a signature, so the API validates it locally with no lookup: fast and dependency-free, but the claims are a snapshot valid until expiry. An opaque token is a random string the API must resolve remotely: always current and instantly revocable, at the cost of a call on every request.
What does it mean for an HTTP request to be self-contained, and how would you redesign a paginated endpoint that currently relies on the server remembering which page each client last received?
basics
~20 sSelf-contained means the request carries everything needed to process it: credentials, target, and position. For pagination, put the position in the request - an offset or an opaque cursor the server returns to the client - instead of a server-held cursor.
REST lists 'cacheable' as one of its architectural constraints. What does that constraint actually require of an API's responses, and which responses in a typical HTTP API can be cached at all?
basics
~20 sIt requires every response to say, implicitly or explicitly, whether it may be reused and for how long. In practice GET and HEAD responses are the cacheable ones; POST responses only with explicit freshness; PUT, PATCH and DELETE responses are never cached.
You are designing a JSON API and must decide, for each resource, whether responses carry an ETag validator, a Last-Modified date, both, or neither. How do you make that call?
basics
~20 sUse an ETag when you have a cheap exact version (row version, sequence, content hash) or when changes can happen within the same second. Use Last-Modified when a meaningful modification time already exists and human readability or heuristic freshness helps. Emit both when free; emit neither for tiny or per-request-volatile responses.
How do you choose a TTL for an HTTP API response — the number you put in Cache-Control: max-age or s-maxage — for resources that change at very different rates?
basics
~20 sPick the TTL from how stale the data may safely be, not from how often it changes. Volatile or personalised resources get seconds or no shared caching; stable reference data gets minutes to hours; immutable, uniquely-addressed content gets a year. Use s-maxage to give shared caches a different, usually longer, value.
Reviewing a client integration, you notice every API call appends a unique query parameter such as ?_=1739382736 or ?nocache=<uuid>. What effect does that have on caches, and when is a changing URL the right tool rather than a mistake?
basics
~20 sA unique parameter makes a unique cache key, so nothing is ever reused: hit rate is zero and every request reaches the origin. A changing URL is right only in the inverse case — content-addressed or versioned URLs that change when the content changes, allowing very long lifetimes.
For a JSON REST API, which changes to a request or response are backward-compatible (additive) and which break existing clients? Give concrete examples.
basics
~20 sAdditive: new endpoints, new response fields, new optional request parameters, relaxed validation. Breaking: removing or renaming a field, changing its type, units or format, making a parameter required, tightening validation, changing status codes or the error body shape.
Compare the main ways to version an HTTP API — a version in the URI path such as /v1/orders, a query parameter, a custom request header, and media-type versioning via the Accept header. What are the trade-offs?
basics
~20 sURI path versioning is the most visible, cacheable and easiest to route or test in a browser, but versions the whole API. Header and media-type versioning keep URIs stable and allow per-resource versions, at the cost of discoverability, tooling friction and cache correctness needing Vary.
An HTTP API version has been switched off permanently. What status code should its endpoints return — 404, 410 Gone, 301, or something else — and what should the response body contain?
basics
~20 sReturn 410 Gone: the resource existed and is intentionally, permanently removed. Include a machine-readable error body naming the version, the removal date and the replacement. Use 301/308 redirects only when the new endpoint is genuinely equivalent.
What is a tolerant-reader client in the context of consuming a JSON HTTP API, and what concrete parsing rules does it follow so that additive server changes do not break it?
basics
~20 sA tolerant reader ignores unknown fields, reads only what it needs, does not depend on field order, and handles unknown enum values with a default branch instead of failing. That lets the server add fields and values without redeploying clients.
How do the HTTP `Deprecation` and `Sunset` response headers work, what do they carry, and how should a client and a provider each use them?
basics
~20 sSunset (RFC 8594) gives an HTTP-date after which the resource becomes unresponsive; Deprecation says it is already or will be deprecated. Pair them with Link rel="sunset" or rel="deprecation" to documentation. Clients should log and alert on them.
API responses often carry links tagged with relation names such as self, next, and edit. What does a link relation name mean, and why prefer registered names like those over inventing your own?
basics
~20 sA link relation is a short label saying how the target URL relates to the current resource: self is this resource's own URL, next the following page, edit the URL you write to. Registered IANA names give clients a shared vocabulary so generic code can follow links without endpoint-specific rules.
An order resource returns a cancel link while the order is pending, and stops returning it once the order has shipped. What is the client supposed to gain from that, and what does the client still have to know on its own?
basics
~20 sThe response advertises the transitions currently available, so the server owns the state machine and the client renders whatever affordances it is given instead of reimplementing the rules. The client still needs to understand each relation's meaning and what payload it takes, and the server still enforces every rule itself.
Describe how a HAL document (media type application/hal+json) is laid out. What do the reserved _links and _embedded properties hold, and what problem does _embedded solve?
basics
~20 sA HAL resource is ordinary JSON plus two reserved keys. _links maps relation names to link objects with an href (optionally templated, type, title). _embedded maps relation names to full nested resources, so a client gets related data in one response instead of following every link.
How is a JSON:API document (media type application/vnd.api+json) laid out - what goes in data, relationships and included - and what does that structure buy you over ad-hoc JSON?
basics
~20 sTop level holds data, errors or meta. data is a resource object with type, id, attributes and relationships; relationships hold identifier objects (type plus id) and links; included carries the full related resources, deduplicated. It gives you a normalised graph plus fixed conventions for includes, sparse fields and paging.
Walk through the four levels of the Richardson Maturity Model for HTTP APIs, from level 0 to level 3, with an example of what an API looks like at each level.
basics
~20 sLevel 0: one URL, one verb, the operation named in the body. Level 1: many resource URLs. Level 2: proper HTTP verbs and status codes per resource. Level 3: responses carry hypermedia links telling the client what it can do next.
Pagination, Filtering, and Field Selection
all 13 Pagination, Filtering, and Field Selection questions →When you design a JSON endpoint that returns a list of resources, what is a response envelope with `data` and `meta` sections, and why might you prefer it over returning a bare JSON array as the top-level body?
basics
~20 sAn envelope wraps the items in an object: data holds the array, meta holds paging info (page size, cursors, counts), and links holds next/prev URLs. A bare array leaves no room to add that without breaking clients.
Design the query parameter that lets a client of a REST list endpoint sort by several fields at once, in either direction — for example newest first, then by name ascending. What syntax would you choose and why?
basics
~10 sUse one comma-separated sort parameter where order matters and a leading - means descending: sort=-created_at,name. Whitelist the allowed field names, define a default sort, and always append a unique tiebreaker such as the id.
A REST list endpoint accepts a `limit` (or `size`) query parameter. What should the API do when a client omits it, sends `limit=0`, sends `limit=100000`, or sends something non-numeric?
basics
~20 sOmitted: apply a documented default (often 20-50) — never "everything". Over the maximum: either clamp to the max or return 400, but document which. Non-numeric or negative: 400. Echo the effective limit in the response so the client can see what actually applied.
The HTTP `Link` header standardized by RFC 8288 is how GitHub's API exposes pagination. Describe its syntax, which relation types are used for paging, and how a client should consume it correctly.
basics
~20 sLink carries one or more comma-separated entries of the form <url>; rel="next". Paging uses rel values next, prev, first, last. Clients must parse the header and follow the URLs opaquely, never rebuild them by incrementing a page number.
Plain `field=value` query parameters can only express equality. Compare the common ways a REST API expresses richer filter operators — bracket suffixes like `created_at[gte]=`, right-hand-side prefixes like `created_at=gte:`, and full expression languages such as RSQL/FIQL or OData `$filter` — and say which you'd pick.
basics
~20 sBracket suffixes (created_at[gte]=2024-01-01) and RHS prefixes (created_at=gte:2024-01-01) add per-field operators while keeping ordinary query parsing; RSQL/FIQL and OData $filter add a real expression language with AND/OR/grouping, at the cost of a parser, a validator, and unbounded query complexity. Start with the simple forms.
Why is it a problem for an HTTP API to return stack traces, SQL fragments, framework class names or internal database identifiers in an error response body, and what do you return instead?
basics
~20 sThose details leak your internals to attackers — stack, framework versions, table names, ID ranges — and are useless to callers. Return a stable code, a safe message and a request id; keep the trace server-side in logs, keyed by that id.
What is the application/problem+json media type defined by RFC 9457, and which members does a problem document define?
basics
~20 sIt is a standard JSON format for HTTP error bodies, media type application/problem+json. Its members are type (a URI identifying the problem kind), title, status, detail and instance — all optional, plus your own extension members.
What does the HTTP Retry-After response header mean, what value formats does it accept, and on which responses should an API send it?
basics
~20 sRetry-After tells the client how long to wait before trying again. It takes either delay seconds (Retry-After: 120) or an HTTP-date. Send it on 429 and 503, and on 3xx redirects that ask the client to wait.
A client posts a JSON body to your REST API and several fields fail validation. How would you shape the response body so the caller can highlight the exact inputs that failed, rather than returning one human-readable sentence?
basics
~20 sReturn an array of violations, one per failed field. Each entry carries a machine-readable code, a target naming the field, and a human message. Clients switch on the code and attach the message to the right input; a prose string cannot be parsed.
What should the body of an HTTP API error response contain, and why do teams separate a stable machine-readable error code from the human-readable message?
basics
~20 sAn error body should carry a stable machine-readable code, a human message, and a request id for support. Clients branch on the code; the message is prose that can be reworded or localized without breaking any caller.
What is the Idempotency-Key HTTP header pattern used by payment and other write APIs, who generates the key, and which requests should carry one?
basics
~20 sThe client generates a unique key (usually a UUID) per logical operation and sends it as the Idempotency-Key request header. The server records the key with the result; if the same key arrives again it returns the stored response instead of re-executing. It makes retrying a POST safe.
You are implementing support for the Idempotency-Key request header on a POST endpoint. What does the server store, and what is the request-handling flow for a first request versus a replay?
basics
~20 sStore a row keyed by (scope, key) holding a fingerprint of the request, a state (in-progress / completed), and the saved status, headers and body. First request: insert the row, execute, save the response. Repeat: find the completed row and return the stored response without re-executing.
Your HTTP client times out waiting for a response to a POST request. What do you actually know about whether the server applied it, and how should the client behave?
basics
~20 sAlmost nothing: a timeout means you lost the answer, not that the work did not happen. The request may have been fully applied. Retrying a non-idempotent POST can duplicate the effect, so either make the operation retry-safe first, or reconcile by querying before retrying.
A client's write to your API is rejected with HTTP 412 because the version token it sent was stale. Walk through what the client should do next, and what the API has to provide for that step to be possible.
basics
~20 sRe-read the resource to get current state and a fresh validator, reconcile the user's intended change against what actually changed, then resend with the new validator. Never blindly resend the same body with a refreshed token, and cap the retry loop.
A client reuses the same Idempotency-Key HTTP header value but sends a different request body than the first time. How should the API respond, and how does the server detect this?
basics
~20 sReject it without executing anything. The server stores a fingerprint (hash of method, path and body) with each key and compares on every arrival; a mismatch means the client has a bug, so return an error — commonly 422 Unprocessable Content — rather than replaying or executing.