Interview Prep
Backend & APIs Interview Questions
Server-side design, REST and API questions, and the protocols underneath them.
12 of 12 Backend & APIs questions shown. Answers are hidden by default — try each one before revealing it.
1.Which of these HTTP methods is NOT idempotent, meaning that sending the same request multiple times can leave the server in a different end state than sending it once?
MediumShow answer and explanation
POST is defined as non-idempotent because it is generally used to create a new resource or trigger an action, so sending the same POST twice typically creates two separate resources, or runs the action twice, changing the end result compared to a single call. GET, PUT, and DELETE are all specified as idempotent: GET does not change server state at all, PUT replaces a resource with the same representation no matter how many times it is sent, and DELETE leaves the resource gone whether it is called once or five times, even though the response code on a later call might differ, such as 404 instead of 204. Idempotency is exactly why PUT and DELETE are considered safe to retry automatically after a timeout, while blindly retrying a POST risks duplicate side effects.
httprestidempotency
2.A client sends POST /orders to create a new order. The server creates it successfully and returns a representation of the new order in the body, along with a Location header pointing at it. Which status code should the response use?
EasyShow answer and explanation
201 Created is the status meant specifically for a request that succeeded and resulted in a new resource, and it pairs naturally with a Location header pointing at that resource plus a body describing it. 200 OK is tempting because the request did succeed, but it is the generic success code and does not communicate that a resource was created, so it fits better for a POST that performs an action without creating an identifiable resource, such as POST /orders/123/cancel. 204 No Content is wrong here because the response deliberately has a body; 204 is for successful requests with nothing to return. 202 Accepted would only be correct if order creation were handed off for asynchronous processing and had not actually finished by the time the response was sent.
httpstatus-codesrest
3.What does it mean for a REST API to be stateless, as one of REST's architectural constraints?
MediumShow answer and explanation
Statelessness means the server does not store any per-client session context between requests, no memory of who is logged in or what step of a workflow a particular caller is on. Each request has to be self-contained, carrying whatever credentials and parameters the server needs to understand and fulfill it independently of any earlier request. This is what makes stateless APIs easy to scale horizontally, since any server instance behind a load balancer can handle any request without needing state only some other instance holds. It says nothing about whether the server has a database, since persistent data about resources is a different thing from per-client session state, and nothing about the client's own local state, which the client is free to keep.
reststatelessnessapi-design
4.What is the key practical difference between session-based authentication (server-side session plus a cookie) and token-based authentication using a signed JWT sent as a Bearer token?
HardShow answer and explanation
A session-based scheme stores the actual session data on the server, or in a shared store like Redis that every server checks, and the cookie only carries an opaque session id, so validating a request means looking that id up. A signed JWT instead carries its claims directly in the token, and the server can verify the signature and read the claims without any database or cache lookup, which is why JWTs are popular for stateless services. A standard JWT is signed, not encrypted, so its payload is base64-encoded and readable by anyone who has the token, not hidden. Both schemes work fine over HTTPS, and expiration is a design choice either way: JWTs commonly carry an exp claim, and server-side sessions do not have to expire on a fixed schedule.
authenticationjwtsessions
5.A client sends a request with a valid, correctly authenticated token, but that user is not allowed to access the requested resource. Which status code should the server return?
EasyShow answer and explanation
403 Forbidden means the server understood exactly who is calling, since authentication succeeded, but refuses to authorize the action for that identity, which is exactly this case. 401 Unauthorized, despite its name, actually means authentication itself is missing or invalid, and it is typically paired with a WWW-Authenticate header inviting the client to authenticate or reauthenticate, so it is the wrong choice once the token has already been accepted as valid. 400 is for malformed requests, not authorization failures. 404 is sometimes used deliberately in place of 403 to avoid revealing that a resource even exists to an unauthorized caller, but as a direct match for authenticated but not permitted, 403 is the correct code.
httpstatus-codesauthorization
6.What is the semantic difference between PUT and PATCH when updating a resource?
EasyShow answer and explanation
PUT is defined as a full replace: the request body represents the complete state the resource should have afterward, and any field left out is treated as absent or reset, not left untouched. PATCH is defined as a partial update: the body describes only the changes to apply, leaving other fields as they were. PUT is guaranteed idempotent by the HTTP spec, but PATCH is not guaranteed either way, and a well-designed PATCH, such as one that sets a field to an absolute value, can absolutely be idempotent in practice, so claiming it never is goes too far. PUT is commonly used both to create a resource at a client-specified URL and to replace an existing one, so the create-only and update-only split is incorrect, and PATCH is a distinct, later-standardized method rather than an alias for PUT.
httprestapi-design
7.Design a REST endpoint for creating a comment on a blog post: POST /posts/{postId}/comments. Walk through the request and response you would design, and explain which status codes you would return for the success case and at least two failure cases.
MediumShow model answer
The endpoint accepts POST /posts/{postId}/comments with a JSON body such as { "authorName": "...", "body": "..." }. On success, the server creates a new comment resource tied to postId, and the response should be 201 Created, with a Location header pointing at the new resource, for example /posts/42/comments/107, and a body containing the created comment, including its generated id and createdAt timestamp, since returning the created representation saves the client a follow-up GET. There are at least three failure cases worth distinguishing with different codes. If postId in the URL does not correspond to an existing post, the response should be 404 Not Found, since the parent resource the comment would attach to does not exist. If the request body is missing the required body field, or authorName is empty, that is a client-side validation failure, so 400 Bad Request, or the more specific 422 Unprocessable Entity if the API distinguishes malformed JSON from semantically invalid but well-formed input, with an error body naming which field failed. If the endpoint requires authentication and the request carries no valid credentials at all, that is 401 Unauthorized; if it is authenticated but that particular user has been banned from commenting on this post, that is 403 Forbidden. Keeping these codes distinct matters because clients behave differently based on which one they see: a 400 tells a form to show an inline validation message, a 401 tells the client to prompt for login, and a 404 tells it the post itself may have been deleted, three different responses to what would otherwise look like one generic failure.
A strong answer touches
- success is 201 Created with a Location header and the created resource in the body
- 404 when the parent post referenced in the URL does not exist
- 400 or 422 for request validation failures, ideally with field-level error detail
- 401 versus 403 distinction for missing versus insufficient authentication
- why returning distinct status codes changes how the client should react
This is one genuine answer, not the only correct one — compare it with your own reasoning rather than matching it word for word.
restapi-designstatus-codes
8.Explain the difference between authentication and authorization in the context of an API, and give an example of how a request can fail each one separately.
EasyShow model answer
Authentication answers who is making this request, verifying that the caller is who they claim to be, typically by checking credentials such as a password, an API key, or a signed token. Authorization answers a different question, whether this specific, already-identified caller is allowed to do this specific thing, checking permissions, roles, or ownership after identity has already been established. The two are sequential and independent: a request has to pass authentication before authorization is even evaluated, but passing authentication says nothing about what that identity is permitted to do. Consider DELETE /documents/55 as an example. If the request has no Authorization header at all, or the bearer token is expired or has an invalid signature, that is an authentication failure, since the server does not know who is asking, and the correct response is 401 Unauthorized. Now suppose the token is entirely valid and belongs to a real, logged-in user, so authentication succeeds, but that user does not own document 55 and is not an admin. The server now knows exactly who is asking, and the answer is someone recognized but not someone allowed to delete this particular resource, which is an authorization failure, and the correct response is 403 Forbidden. Conflating the two is a common source of bugs: returning 401 for a permissions problem incorrectly tells the client to try logging in again, which will not fix anything, since the user is already correctly authenticated as themselves and simply is not allowed to perform this action.
A strong answer touches
- authentication verifies identity; authorization verifies permission for that already-established identity
- authentication happens first; authorization is only evaluated once identity is known
- example: missing or invalid credentials map to 401, an authentication failure
- example: a valid but insufficiently privileged identity maps to 403, an authorization failure
- confusing the two produces misleading client-facing errors, such as prompting for login when the real problem is permissions
This is one genuine answer, not the only correct one — compare it with your own reasoning rather than matching it word for word.
authenticationauthorizationapi-design
9.When would you choose to put a message queue between two services instead of having one service call the other's API directly? What do you give up by doing that?
MediumShow model answer
A direct synchronous API call is simplest when the caller needs an immediate answer in order to keep going, such as an authentication check that must return valid or invalid before the caller can respond to its own client. A queue becomes the better choice when the caller does not need to wait for the work to finish, and especially when the two services' availability and throughput should be decoupled from each other. Three situations come up often. First, when the downstream work is slow or bursty relative to the incoming request, such as generating a video thumbnail, sending a batch of emails, or re-indexing a search document after an update: pushing the work onto a queue lets the API respond quickly, for example with 202 Accepted meaning the work will be processed shortly, instead of making the caller wait on a slow operation, and it smooths out spikes because the queue absorbs a burst of work that a downstream service can drain at its own steady pace rather than being hit with everything at once, which is what many simultaneous direct calls would do. Second, when resilience to the downstream service being temporarily unavailable matters: if the consumer is down or mid-deployment, messages simply wait in the queue instead of every caller receiving a failed request. Third, when multiple independent services need to react to the same event: a queue, or more precisely a publish-subscribe topic, lets a new consumer, such as an analytics service that wants to know about every order placed, be added later without changing the code of the service that publishes the event. What is given up is simplicity and immediacy: the caller no longer gets a synchronous result or error back, so the system needs another way to communicate final success or failure, such as a webhook or a status endpoint the client polls, there is operational cost in running and monitoring the queue itself, and the design has to account for a message being processed more than once, since most queues offer at-least-once delivery, which means the consumer needs to be idempotent.
A strong answer touches
- a direct call fits when the caller needs an immediate synchronous result
- a queue fits slow or bursty work, decoupling caller throughput from processor throughput
- a queue improves resilience when the downstream service is temporarily unavailable
- a queue enables multiple independent consumers reacting to one event, publish-subscribe style
- trade-off: no synchronous result, added operational complexity, and the consumer must be idempotent for at-least-once delivery
This is one genuine answer, not the only correct one — compare it with your own reasoning rather than matching it word for word.
system-designqueuesasync
10.Explain what idempotency means for an API endpoint, and why it matters when a client needs to retry a request, such as after a network timeout. Use a payment endpoint as an example.
MediumShow model answer
An operation is idempotent if performing it multiple times leaves the server in the same state as performing it exactly once, even though individual responses might differ slightly, such as a second DELETE returning 404 instead of 204 while the resource ends up gone either way. This matters for retries because a network timeout is ambiguous from the client's point of view: the request might have failed before reaching the server, or it might have succeeded and only the response was lost on the way back. If the operation is naturally idempotent, the client can safely resend the identical request without knowing which case occurred, since the worst case is the server repeats the same, harmless outcome. The classic place this goes wrong is a payment endpoint such as POST /payments implemented naively: POST is not idempotent by default, so if a client's confirmation never arrives and it retries, the server might charge the card twice for one purchase, because from the server's perspective it simply received two separate requests to charge the card, with no way to tell they were meant to be the same attempt. The standard fix is to make the operation idempotent explicitly. The client generates a unique idempotency key, often a UUID, once, before the first attempt, and sends it in a header on every retry of that same logical request. The server stores the key alongside the outcome of the first request it processed under that key, and if it sees the same key again, it returns the original result instead of charging the card a second time. This turns payments from unsafe to retry into safe to retry, which is what lets clients, including payment provider SDKs, retry automatically on timeouts without a human needing to check whether the first attempt actually went through.
A strong answer touches
- idempotent means repeating the request produces the same end state as performing it once
- network timeouts are ambiguous, the request may or may not have succeeded, so retry safety depends on idempotency
- POST is not idempotent by default; naively retrying it can double-charge a payment
- the fix is a client-generated idempotency key sent on every retry, which the server uses to deduplicate
- idempotency is what makes automatic retries safe without manual reconciliation
This is one genuine answer, not the only correct one — compare it with your own reasoning rather than matching it word for word.
idempotencyreliabilityapi-design
11.REST defines statelessness as a core architectural constraint, meaning the server should not store client session state between requests. Explain what this means concretely, and why it matters for scaling an API horizontally.
MediumShow model answer
Concretely, statelessness means every request from a client must contain all the information the server needs to understand and fulfill it, credentials, relevant parameters, and enough context to process it, rather than the server remembering from a previous request who this client is or what step of a workflow they are on. A stateful design might have a client call POST /login, have the server keep an in-memory session object tied to one specific server process, and then expect every later request from that client to land back on that same server so it can find the session. A stateless design instead has the login step return a token, such as a JWT, that the client stores and resends with every subsequent request, so any server that receives that request can independently verify the token and know who is calling, without needing to have handled that client's earlier requests itself. This matters directly for horizontal scaling because a stateless API can sit behind a load balancer using simple, cheap routing, round robin or least connections or whatever is convenient, since any instance can serve any request. A stateful design either has to pin a client to the specific server holding their session, called sticky sessions, which complicates the load balancer and creates hot spots and awkward failover when that one server is redeployed or goes down, or it has to externalize the session into a shared store such as Redis that every server checks, which reintroduces a network round trip and a new component that itself has to be scaled and kept available. Statelessness trades a small amount of per-request overhead, repeating a token or header on every call instead of relying on server memory, for the ability to add or remove server instances freely, since none of them uniquely holds client-specific state.
A strong answer touches
- stateless means each request is self-contained; the server keeps no per-client session between requests
- contrast with sticky-session designs that pin a client to one specific server instance
- stateless requests can be routed to any server behind a load balancer with no special routing rules
- avoids depending on a shared session store, or at least makes that dependency optional rather than structural
- trade-off: a little more data repeated per request in exchange for free horizontal scaling
This is one genuine answer, not the only correct one — compare it with your own reasoning rather than matching it word for word.
restscalabilitystatelessness
12.You need to design pagination for a REST endpoint like GET /orders that can return millions of rows. Compare offset-based pagination (?page=3&pageSize=50) with cursor-based pagination (?after=<cursor>&limit=50), and explain which you would pick and why.
HardShow model answer
Offset-based pagination is simple to implement and simple for clients to reason about: page 3 with a page size of 50 translates directly into something like a LIMIT 50 OFFSET 100 query, and clients can jump straight to an arbitrary page, including page 40, which is genuinely useful for a UI with numbered page links. It has two real problems at scale. Performance-wise, an offset does not let the database skip rows for free; most engines still have to scan and discard every row before the offset, so a large offset gets slower the deeper a client pages, which is a serious problem once a table holds millions of rows. Correctness-wise, offset pagination is unstable under concurrent writes: if a new order is inserted while a client is paging through results ordered newest first, everything shifts down by one position, and the client can see the same row twice or skip a row entirely between page fetches, because an offset is a position in a list that keeps moving under it. Cursor-based pagination avoids both problems by using a pointer into the actual data instead of a position count. The server returns an opaque cursor, often an encoded value derived from the last row's sort key, such as its id or created_at, alongside each page, and the next request asks for the rows after that cursor, which the database can satisfy with an indexed condition like id greater than the cursor value, ordered and limited, fast regardless of how deep into the results the client is, and stable under inserts because it is anchored to a specific row rather than a shifting count. The cost is that cursor pagination gives up random access to an arbitrary page number and requires the client to hold onto the cursor between requests instead of just incrementing a page number. For an endpoint like GET /orders that can grow into millions of rows and is commonly consumed by another service doing a full sync, or a client doing infinite scroll, rather than a human clicking to a specific page number, cursor-based pagination is the better choice, since its performance and consistency guarantees matter more there than jump-to-page access, which most order-listing use cases do not actually need.
A strong answer touches
- offset pagination is simple and supports jumping to an arbitrary page number
- offset pagination gets slower as the offset grows, since the database still scans and discards the skipped rows
- offset pagination is unstable under concurrent inserts or deletes, causing skipped or duplicated rows across pages
- cursor pagination anchors to a row's sort key, giving consistent, indexed page fetches regardless of depth
- trade-off: cursor pagination loses random page access; it fits large or high-churn datasets and sequential consumption better
This is one genuine answer, not the only correct one — compare it with your own reasoning rather than matching it word for word.
api-designpaginationscalability
Put this into practice
Connect the idea to a worked lesson, a practical task or your interview preparation.
- Concept guideREST APIs →An architectural style for exposing data as addressable resources, manipulated with a small, predictable set of HTTP verbs.
- Advanced projectREST API for a notes app →A small HTTP API with a real database behind it, honest status codes, and tests that hit every route.
- Learning roadmapBackend Developer roadmap →One language written well, then HTTP, data storage, authentication and the operational facts of code that has to stay up.
- Concept guideAuthentication vs Authorization →Proving who you are is a different problem from deciding what you're allowed to do.