Loading...
Loading...
Backend interview guide
HTTP methods and status codes, headers, cookies, sessions, JSON and REST API design. Every answer is short enough to say aloud, and every question links to the lesson that teaches it.
60 questionsLast updated
I find the base URL, the authentication method, the endpoint I need with its parameters, an example response and the error list. Then I get a test key, make one call with curl or Postman, and compare the response with the docs before writing any code.
Lesson: Reading an API's documentationKeys live in environment variables or a .env file that is in .gitignore, with a committed .env.example that shows the variable names. In Postman they go in secret current values. If one leaks, I revoke it at the provider, because Git history keeps the old value.
That baseUrl points at the right server, that auth reads a variable I set in an environment, and that the generated parameter values are real ones, because the converter fills in examples or random values. Then I add tests, since the import creates requests, not checks.
The success case with 201, the Location header and the returned body; validation errors with 400 for missing or wrong-type fields; 401 without login and 403 with the wrong role; 409 for a duplicate unique value; 415 for the wrong content type. Then I check the created resource with a GET and clean it up with a DELETE.
Each test has real assertions, starting with the status. Requests that depend on each other share values through variables, not hard-coded ids. The run creates its own data and deletes it, so it passes twice in a row, and secrets come from environment variables. Timing checks are kept loose so they do not hide real failures.
Lesson: Practice: testing APIs with toolsFirst the environment: the base URL CI uses and whether the secrets reach it through --env-var. Then leftover data on the shared server, timing limits that the slower server misses, and the order of requests, since CI may run a folder on its own. The failure table names the first failing request; I start there.
HttpClient?A client is thread-safe and keeps a pool of open connections plus its own background thread. Reusing it avoids a new TCP connection and TLS handshake for every call. I create it once at startup and pass it to the classes that need it.
Lesson: HttpClient, HttpRequest and HttpResponsesend and sendAsync?send blocks the calling thread until the response arrives and throws checked exceptions. sendAsync returns a CompletableFuture immediately, so several calls can run at the same time; failures come out of join() as a CompletionException. I use send by default and sendAsync for independent calls that I want in parallel.
HttpClient and HttpRequest immutable, and why does that help?Once built, their settings cannot change, so one object can be shared safely between threads without locks. A request can be sent many times, and no part of the program can accidentally change the client's settings for everyone else.
Lesson: HttpClient, HttpRequest and HttpResponseContent-Type and Accept?Content-Type describes the body I am sending, for example application/json. Accept says which formats I can read in the response. A wrong Content-Type usually gives 415, and an Accept the server cannot satisfy gives 406.
PATCH() method on the builder, and how do you send one?The builder has shortcuts only for the most common methods: GET, POST, PUT and DELETE. Any other method, including PATCH, uses .method("PATCH", bodyPublisher). The server sees a normal PATCH request.
I read the Location header, which the server sets to the new resource's address. It is often relative, so I call response.uri().resolve(location) to get the full URI. I prefer this to building the URL from the id myself, because the server owns its URL design.
NewProduct instead of sending your Product model class?The API contract and the model change for different reasons. A request record holds exactly the fields the endpoint accepts, with no database id or internal flags. That keeps the JSON small and clear, and a change in the model does not silently change what the client sends.
Lesson: GET and POST with JSONHttpClient throw for 4xx or 5xx responses?No. Any complete HTTP response, including 404 and 500, is returned normally. Exceptions are only for transport problems like timeouts or refused connections. So the client code must check statusCode() itself, ideally in one helper method.
HttpClient handle redirects?It does not follow them by default (Redirect.NEVER); you get the 3xx response. With Redirect.NORMAL it follows them, except from HTTPS to HTTP. After following, uri() gives the final address and previousResponse() the redirect response.
HttpTimeoutException and HttpConnectTimeoutException?HttpConnectTimeoutException is a subclass for when the connection itself could not be opened in time. Plain HttpTimeoutException means the connection was fine but the response did not arrive in time. The first suggests the server is down or unreachable; the second suggests it is overloaded or slow.
readValue(body, List<Product>.class)?Because of type erasure: generic type arguments do not exist at run time, so List<Product>.class is not valid Java and List.class loses the item type. Jackson then builds LinkedHashMaps. A TypeReference<List<Product>> subclass keeps the full type, so Jackson creates real Product objects.
Usually no. Servers add fields as non-breaking changes, and a client that fails on them breaks for no reason. Jackson 3 already ignores unknown fields by default; I also mark response records with @JsonIgnoreProperties(ignoreUnknown = true) so it stays true under any mapper setting. I still validate the fields I actually need.
HttpClient calls in a typed client class?It gives one place for timeouts, headers, status checks and JSON mapping, so callers just call find(id) or create(product). It removes copy-pasted request code that drifts apart, and it can be tested against a fake server by passing a different base URL into the constructor.
Thread.currentThread().interrupt() in a catch (InterruptedException e)?Throwing InterruptedException clears the thread's interrupt flag. Code further up, such as an executor shutting down or a loop checking isInterrupted(), relies on that flag to know it should stop. Restoring it, and then stopping the work, keeps cancellation working.
The client creates an idempotency key once per order, for example a UUID when the checkout page opens, and sends it in an Idempotency-Key header on every attempt. The server stores the result per key, so a repeated request returns the first order instead of creating a new one. Disabling the button while the request runs helps too, but only the key protects against retries and double clicks reliably.
catch (Exception e) { } around an HTTP call?It swallows every failure, including bugs like NullPointerException, so the program carries on with missing or wrong data and nobody learns why. I catch the specific exceptions I can handle, add context and rethrow the rest with the original as the cause.
SSLHandshakeException: PKIX path building failed. What do you do?It means Java cannot build a trust chain to the server's certificate, often because it is self-signed, expired or from a private CA. I fix the cause: a proper certificate on the server, or the company CA added to the trust store. I never install a "trust all certificates" manager, because that opens the door to man-in-the-middle attacks.
Lesson: Handling network failuresHttpClient usually a singleton in an application?It is immutable and thread-safe, and it owns a connection pool and background threads. Sharing it lets calls reuse open connections and keeps thread counts low. I create it once at startup and inject it into the classes that need it, which also makes testing easier.
Lesson: Practice: calling APIs from JavaAn operation is idempotent if doing it twice has the same effect as once, like GET, PUT or DELETE. After a timeout the client cannot know if the server did the work, so only idempotent requests can be retried blindly. For a POST, such as creating an order, I send an idempotency key so the server can return the first result for a retry.
Lesson: Practice: calling APIs from JavaI set a short request timeout so my own users are not blocked, and I keep the last good result in a cache with its time, showing it when the API is slow or down. I might retry once with backoff for a temporary failure, and if the problem is constant I would talk to the provider or look for a faster API.
Lesson: Practice: calling APIs from JavaHttpHandler and HttpExchange do?HttpHandler is the interface I implement: its handle method runs for every request on its context. HttpExchange is that one request and its response; I read the method, URI, headers and body from it, then send the status, headers and body through it.
The response may never finish: in chunked mode the end marker is never sent, so the client waits until it times out, and the connection is not freed for the next request. I close it with try-with-resources on the response body, or try (exchange), so it also closes when an exception is thrown.
BindException: Address already in use on startup. How do you fix it?Another program, often an older copy of my server, already listens on that port. I find it with netstat -ano | findstr :8080 on Windows or lsof -i :8080 on macOS and stop it, or I start on another port. In the code I catch BindException at startup and print a message that names the port and the fix.
Each context has a path, and a request goes to the context with the longest path that is a text prefix of the request path. So /api/v1/products also receives /api/v1/products/42 and even /api/v1/productsXYZ. That is why I register one router on / and match full paths myself.
The server catches it, closes the connection without sending a response and logs nothing by default. The client gets an IOException such as "header parser received no bytes" and the developer sees no error. That is why the router needs a catch-all that turns exceptions into proper error responses, which ShopLite adds with Problem Details.
I would keep a list of routes, each with a method, a path template with {id} placeholders compiled to a regex, and a handler. One dispatcher walks the list: a path and method match runs the handler with the variables, a path match with another method adds to Allow, and no match is a 404. Adding an endpoint is then one line, and the error rules stay in one place, which is what Spring's @GetMapping does for you.
415 means the server does not accept the body's format at all, for example text/plain or a missing Content-Type. 400 means the format is right but the content is wrong: broken JSON, an unknown field or a bad value. I check the type first, so the client gets the most useful error.
I turn the response record into bytes with writeValueAsBytes, set Content-Type: application/json, call sendResponseHeaders(status, bytes.length) and write the bytes in try-with-resources. Using the byte array's length avoids the classic bug of counting characters instead of bytes.
Every handler needs the same rules: the type check, the size limit, the strict parse, the byte length on write. In one helper they are written and tested once; copied into each handler, one copy is always slightly different, and that is where bugs hide.
Lesson: Reading request bodies and writing JSON responses400 means the request itself is wrong: bad JSON or fields that break their rules. 409 means a valid request clashes with the current data, like a duplicate SKU or deleting a category still in use. 422 means the fields are valid but refer to data that breaks a business rule, such as a categoryId that does not exist.
Lesson: Returning the right status codesServices throw typed business exceptions, handlers do not catch them, and one error mapper in the router's catch-all turns each type into a status and a Problem Details body. Adding a new error kind is one line in the mapper, and every endpoint uses it at once.
Lesson: Returning the right status codesA form with three mistakes would otherwise take three round trips, each showing one new error, which is slow and frustrating. Collecting all problems first lets the client highlight every field at once; it also makes the API easier to test.
Lesson: Returning the right status codesI add an HTTP layer next to the existing one: a router and handlers that read requests, call the existing services and write responses. The services and DAOs stay unchanged, so every rule is shared, and the wiring code creates the handlers with the same service objects.
Lesson: Wiring the service layer and handling many requestsIt must be stateless: only final references to other thread-safe objects in its fields, and all request data in parameters and local variables. Mutable shared fields, such as a counter or a cached "current user", need atomic types or locks, or better, should not exist.
Lesson: Wiring the service layer and handling many requestsThe server stops accepting new requests, lets running ones finish within a time limit, then shuts down its threads and finally closes resources like the connection pool. I run it from a shutdown hook so Ctrl+C or a stop from the operating system does not cut responses in half.
Lesson: Wiring the service layer and handling many requestsIt runs the server and the plumbing: routing by method and path, reading and writing JSON, mapping exceptions to responses, creating and wiring objects, the thread pool, the connection pool and configuration. I write controllers, DTOs and services, which are the parts specific to my app.
Lesson: What Spring Boot will do for you nextThe model, the DAOs, the services with all their rules and exceptions, the request and response records, and the API contract. Only the HTTP layer is replaced, because the layers only depend downwards.
Lesson: What Spring Boot will do for you next@RestControllerAdvice correspond to in your hand-written server?The ErrorMapper called from the router's catch-all: one class that turns each exception type into a status and a Problem Details body. In Spring it is a class of @ExceptionHandler methods that applies to every controller.
I look for handlers that do not close the exchange on some path, such as an early return, and for a Content-Length taken from characters instead of bytes. Both leave the client waiting with no error in the logs. Then I check for slow queries or an exhausted connection pool.
Once the status and headers are sent they cannot change. If the handler fails half-way through writing, the client gets a cut 200. Building the body first lets any failure still become a proper 500 from the error mapper.
Lesson: Practice: a tiny HTTP serverThe dispatcher thread accepts connections and parses requests. With an executor set, it hands each exchange to a worker thread, which runs the handler, so a slow handler does not block new requests. Without an executor, the dispatcher runs the handlers itself, one at a time.
Lesson: Practice: a tiny HTTP serverI nest when the child cannot exist without the parent and is usually read through it, like /sellers/12/products. I stop at one level; deeper data gets its own top-level collection, like /products/41/reviews, so URLs stay short and every item has one address.
Paging with page and size and a maximum size, an allow-listed sort with a default and an id tie-breaker, filters as query parameters, and the edge cases: an empty match is an empty 200 page and bad parameters are a 400 that names them.
404 means the resource named in the URL does not exist. 409 means the request clashes with the current state, like a full batch or a duplicate. 422 means the body is well-formed but refers to data that breaks a rule, like an unknown batchId.
success: false?Everything that reads status codes treats it as a success: client libraries, retries, caches, load balancers and monitoring. The client has to parse every body to find errors, and dashboards show no failures while users see them.
Lesson: Practice: spot the mistakes in a badly designed APII never serialise database entities directly. Each endpoint returns a response record that lists the contract's fields, so a new column or a password hash cannot appear by accident, and error bodies carry a safe detail while the exception goes to the log.
Lesson: Practice: spot the mistakes in a badly designed API16/10/2026 1:10 PM in an API?The format depends on locale (day or month first) and has no time zone, so two clients can read different instants. ISO-8601 with an offset, like 2026-10-16T13:10:00+05:30, is unambiguous and every library parses it.
CORS lets a server tell browsers which other origins may read its responses. For requests that a plain HTML form could not send, like JSON bodies or an Authorization header, the browser first asks with an OPTIONS preflight so an old server is never surprised by them.
I put the major version in the path, /api/v1. Adding fields, endpoints or optional parameters stays in v1 because clients ignore unknown fields; renaming, retyping or new required inputs go to v2, which runs next to v1 until an announced sunset date.
Offset paging when users need page numbers and totals, like an admin table. Cursor paging for feeds and large or fast-changing data, because it does not skip or repeat rows when new ones arrive and stays fast on deep pages.
Lesson: HTTP and REST interview questionsHttpOnly hides the cookie from JavaScript, so a script injected into the page cannot steal it. Secure sends it only over HTTPS. SameSite stops the browser from sending it on cross-site requests, which blocks most cross-site request forgery.
301 moves a URL permanently, so browsers and search engines remember the new address. 302 is a temporary redirect. 304 Not Modified is not a redirect: the cached copy is still valid, so the server sends no body.
Lesson: HTTP and REST interview questionsFor HttpClient a 404 is a successful HTTP exchange; only network failures and timeouts throw. After send, I check statusCode() myself: 2xx reads the body into a record, 4xx or 5xx becomes my own exception with the status and the Problem Details detail.
429 Too Many Requests means the client hit the rate limit. A good client stops, reads the Retry-After header, waits that long and retries, ideally with exponential backoff, instead of hammering the server.
Each lesson has quizzes, practice and more interview questions on the same topic.
Open the first lesson