Prompt and scope
A batch write API serves clients with different bandwidth and latency goals: some need only status, some need a complete resource representation, and some will wait a few seconds to avoid polling. Using RFC 7240, design the Prefer request header, Preference-Applied response header, error handling, caching, and fallback behavior.
Prefer is a request preference, not a server mandate. This question tests protocol semantics and API contracts; it does not assume every proxy preserves preference headers.
What interviewer is testing
- Whether you distinguish a client preference from a server promise and report what was actually applied.
- Whether you use
return=minimal,return=representation,respond-async, andwaitcorrectly. - Whether you account for response variants,
Vary, cache keys, and proxy compatibility. - Whether retries, idempotency keys, and async job state remain safe when a preference is not honored.
Clarifying questions
- Is the API a safe read or a side-effecting write, and does it already have an idempotency key?
- What are the size, generation cost, and maximum wait for a full representation?
- Can clients accept 202 and a status resource, polling, or a callback?
- Will middleboxes forward Prefer, and can caches be shared?
- Which stable fields must clients see when a preference is not honored?
A 30-second answer
“Prefer expresses a client preference that a server may ignore or partially apply; Preference-Applied reports what was applied. A write can use return=minimal to reduce the response, a synchronous caller can request return=representation, and a long operation can use respond-async with a bounded wait. I would design idempotency keys, a 202 status resource, cache Vary, and client fallback together: without Preference-Applied, parse the default response and never assume the preference succeeded.”
Step-by-step design
1. Treat preference as ignorable negotiation
RFC 7240 defines the Prefer request header and Preference-Applied response header. A server can decline a preference, so the body and status code need a stable default contract. A client must not skip parsing merely because it sent Prefer.
2. Choose the right preference tokens
return=minimal fits a write that needs only confirmation; return=representation fits a synchronous caller that needs the updated resource. respond-async says the client accepts asynchronous processing, while wait=n gives a waiting budget. These are hints, not an SLA guarantee.
3. Confirm the result in the response
Send Preference-Applied when a preference is used. When it is not used, the header may be absent and the default contract applies. An async path returns 202, a status URI, and a traceable ID; after a synchronous wait budget expires, the operation remains queryable instead of causing a client to resubmit a side effect.
POST /v1/imports HTTP/1.1
Prefer: return=minimal, respond-async, wait=3
Idempotency-Key: imp-8f2
HTTP/1.1 202 Accepted
Preference-Applied: respond-async
Location: https://api.example/imports/jobs/42
Cache-Control: no-store4. Handle cache variants
If a safe GET representation varies with Prefer, declare Vary correctly or keep preference-dependent responses out of shared caches. Writes normally use no-store. An async status resource should define short caching, ETags, or explicit polling conditions so a middlebox does not return stale progress.
5. Preserve idempotency and fallback
Prefer does not change operation semantics, so retries still need idempotency. Use an idempotency or business deduplication key for writes. If a client sees 202, a timeout, or no Preference-Applied, it should query the job or follow the default response contract rather than create the resource again.
6. Set observability and limits
Record preference tokens, whether they were applied, wait duration, response size, 202 rate, and proxy path. Bound wait, rejecting or truncating excessive values. Ignore and record unknown preferences instead of turning arbitrary client strings into expensive execution paths.
Model high-quality answer
“I would treat Prefer as an ignorable client preference, not a promise. A write defaults to a stable status; a client wanting a small response sends return=minimal, one needing the resource sends return=representation, and a long job uses respond-async with a bounded wait. The server sends Preference-Applied only when it applies the preference; an async result is 202 with Location and a job ID. Writes use idempotency keys, responses use no-store where appropriate, and representation differences are isolated with Vary or cache policy. Without Preference-Applied, the client follows the default parser and queries the job, never duplicating a side effect.”
Common mistakes
- Treat Prefer as mandatory → servers and proxies may ignore it → use a default contract and Preference-Applied.
- Treat wait as a completion guarantee → long jobs can still exceed it → bound the budget and expose a 202 status resource.
- Resubmit after an async timeout → duplicate side effects result → use an idempotency key and query first.
- Ignore cache variants → clients receive a mismatched representation → set Vary or isolate the cache.
- Execute arbitrary unknown preferences → attackers can amplify resource cost → ignore, record, and bound tokens.
Follow-up questions and responses
Does a missing Preference-Applied mean the request failed?
No. The server may choose its default behavior or ignore the preference. The client should parse the stable default contract and call it a failure only when protocol or business state says it failed.
Can every write use return=minimal?
No. It only says the client does not need a full representation. If the client needs a server-generated version, digest, or next link, it should request the representation or fetch the resource rather than infer fields from an empty response.
Does wait=5 make the server block for five seconds?
No hard guarantee follows. It is the client's upper waiting preference; the server may finish earlier, ignore it, or switch to async. Server timeouts, concurrency limits, and resource budgets still apply.