Representative interview topic

Backend interview: design a unified HTTP API error contract with RFC 9457

BackendMedium
Offer.cc Editorial TeamPublished Updated

Question

Several clients call an HTTP API whose error formats are inconsistent. Design a unified RFC 9457 error response covering types, status codes, validation fields, retry hints, localization, and sensitive information.

Prompt and scope

This backend question tests API contracts and failure boundaries. The goal is not to wrap every exception in one JSON object, but to let clients take a stable action while logs, tracing, permissions, and user-facing copy remain separate layers.

What the interviewer is evaluating

  • Whether you understand the relationship between application/problem+json and HTTP status codes.
  • Whether validation, authentication, authorization, conflicts, rate limits, temporary failures, and unknown errors are distinct.
  • Whether you design field-level errors, traceable instances, and controlled extension members.
  • Whether you avoid exposing stacks, internal IDs, personal data, and untrusted raw exceptions.

Clarifying questions to ask first

Confirm whether clients need machine decisions or only display text, whether localization, batch validation, and asynchronous jobs exist, whether types are shared across services, which states are retryable, and what the gateway, service, and client each log, display, and use for request correlation.

A 30-second answer structure

Use application/problem+json with type, title, status, detail, and instance as a stable base. Add controlled extensions for codes, field paths, retry time, and documentation version. HTTP status carries general semantics and type carries a programmable category; internal causes stay in logs while the response contains safe, actionable information.

Deep-dive answer

1. Establish status and type boundaries

Use 400 for syntax or general request failures, 401 for missing authentication, 403 for a recognized but disallowed request, 404 for a missing resource, 409 for a current-state conflict, 429 for rate limiting, and 5xx for server or dependency failures. Make type a stable, documented URI; clients should not parse volatile title or detail text.

2. Design the Problem Details fields

type classifies the problem, title is a stable human summary, status mirrors the response, detail explains this request, and instance identifies this occurrence. Extensions may include a code, field path, parameter name, retry time, or documentation version, with bounded vocabulary and length. Batch validation can return an array where each item points to an input location.

3. Handle validation, conflicts, and retries

Validation failures should tell clients how to correct a field and should not request a retry. A conflict needs a re-read or a different business action. A 429 or temporary dependency failure may include Retry-After, but clients still need backoff and an attempt limit. Make retryability explicit instead of asking clients to infer it from a sea of 200 responses.

4. Protect security and privacy boundaries

Never include stacks, SQL, keys, internal hostnames, cross-tenant details, or complete personal records. Detail should state only an actionable fact; instance should be an unpredictable or controlled reference. Correlate the raw exception in internal logs with a request ID. Avoid account-enumeration leaks in authentication errors and filter field errors by permission.

5. Evolve across services and versions

Put shared types, statuses, and extensions in versioned documentation and contract tests; a gateway should not rewrite service semantics. Add fields compatibly and provide a migration period for retired types. Clients should fall back to status and safe detail for an unknown type. Monitor type distribution, retry success, field-error hotspots, and request-ID traceability.

Example of a strong answer

I would declare every error as application/problem+json with a stable type URI, title, status, detail, and instance. Distinguish 400/401/403/404/409/429 and 5xx by HTTP semantics; add field paths and a safe code for validation, and Retry-After for rate limiting or a temporary dependency failure. Clients use type to decide whether to correct, re-fetch, back off, or contact support rather than parsing volatile detail. Internal logs retain stacks, dependency state, and request IDs, while responses exclude SQL, keys, tenant data, and personal records. Versioned types and contract tests protect cross-service evolution; unknown types fall back to status. Monitor error types, retry outcomes, and field hotspots after launch.

Common mistakes

  • Returning 200 and a sentence for every error, leaving clients unable to decide programmatically.
  • Making clients depend on the literal title or detail wording, so a translation breaks behavior.
  • Labeling validation, conflicts, rate limits, and temporary failures as 500.
  • Returning stacks, SQL, internal hostnames, or complete user data in detail.
  • Exposing internal exception class names as public types and freezing implementation details into the contract.
  • Having no unknown-type fallback or cross-service contract tests.

Follow-up questions

Must type be a reachable URL?

It should be a stable URI and may point to explanatory documentation, but clients should not require a network request for handling. The important properties are semantic identity, versioning, and migration guidance.

Should detail be localized?

Keep machine fields and type stable and render user copy on the client for its language and context. If the server must return detail, use safe templates and language negotiation; never expose an untranslated internal exception.

Should a gateway rewrite every error?

It may add request IDs, timeout errors, and protocol-level failures, but should preserve business types. Rewriting needs versioned rules and observability or clients will see semantics that no longer match the real cause.

How do you handle partial success in a batch request?

Define a batch result with per-item status, input location, and retryability, and state what the overall HTTP status means. A vague detail cannot express partial success, and clients must not resubmit items that already succeeded.

Public sources

Related questions