Tag: software architecture

  • HTTP API Design: Stable Semantics, Useful Errors and Safe Change

    HTTP API Design: Stable Semantics, Useful Errors and Safe Change

    A maintainable HTTP API is a contract between independent systems. Good design lets a client understand what a request means, which outcomes are safe to retry, how to recover from an error and how the contract will evolve. URL style matters, but predictable semantics and operational behaviour matter more.

    HTTP already defines methods, status codes, conditional requests, content negotiation and caching. Reusing those semantics reduces private conventions and helps clients, gateways and observability tools behave correctly. RFC 9110 defines HTTP as a stateless request/response protocol with a uniform interface to resources (RFC 9110 — HTTP Semantics).

    Not every JSON-over-HTTP interface is strictly REST, and a service does not become RESTful merely by using plural nouns. This guide focuses on practical resource-oriented HTTP APIs without claiming conformance to every architectural constraint associated with REST.

    Article map for HTTP API Design: Stable Semantics, Useful Errors and Safe Change, covering Model resources and business transitions, Respect HTTP method semantics, Use status codes to describe the HTTP outcome and relat…
    Article map: Model resources and business transitions; Respect HTTP method semantics; Use status codes to describe the HTTP outcome; Give errors one predictable shape.

    Model resources and business transitions

    Start with business concepts, identities and state changes. A resource is something a client can identify and exchange a representation of, such as:

    • /customers/{customerId};
    • /orders/{orderId};
    • /orders/{orderId}/items; or
    • /orders/{orderId}/cancellations.

    Use stable identifiers that do not expose a storage layout or sensitive sequence without a reason. Keep paths understandable, but do not force every complex action into a verb-free fiction. A cancellation can be represented as a subordinate resource when it has its own status, reason, timestamp and permissions.

    Separate the API representation from database rows. The representation should express the contract, not leak internal column names, join tables or fields that may later change. Decide whether absence, null, an empty collection and a default value mean different things, then document them.

    Respect HTTP method semantics

    RFC 9110 defines GET, HEAD, OPTIONS and TRACE as safe methods: the client is not requesting a state change. Safe does not mean that the server performs no logging or accounting; it means the requested semantics are read-only. GET must not trigger a destructive business action through a query parameter.

    Idempotent methods can be repeated with the same intended effect. PUT and DELETE are idempotent by definition, while POST is not generally idempotent. Idempotency does not require byte-identical responses or prohibit audit timestamps; it constrains the requested effect.

    A practical mapping is:

    Intent Common method Notes
    Retrieve a representation GET Safe; define cache and authorisation behaviour
    Create under a server-selected URI POST to a collection Return the resulting status and location where appropriate
    Replace the state of a known resource PUT Define complete-replacement semantics explicitly
    Apply a partial modification PATCH Document the patch media type and conflict rules
    Remove a resource or make it unavailable DELETE Repeated requests should preserve the intended removed state

    If clients may retry a POST after a network failure, design an idempotency mechanism. A client-generated key can bind repeated attempts to one operation, but the server must define scope, expiry, payload comparison, storage and concurrent-arrival behaviour. Do not label a request idempotent without implementing the guarantee.

    Use status codes to describe the HTTP outcome

    Choose the most specific standard status that matches the result. Common examples include:

    • 200 OK for a successful response with a representation;
    • 201 Created when a new resource is created;
    • 202 Accepted when processing is accepted but not complete;
    • 204 No Content when success has no response content;
    • 400 Bad Request for malformed or invalid request content;
    • 401 Unauthorized when authentication is required or invalid;
    • 403 Forbidden when the authenticated principal is not allowed;
    • 404 Not Found where the target is unavailable, including deliberate concealment where appropriate;
    • 409 Conflict for a conflict with current resource state;
    • 412 Precondition Failed for a failed conditional request;
    • 422 Unprocessable Content for syntactically valid content that cannot be processed as supplied;
    • 429 Too Many Requests for rate limiting; and
    • 500-class statuses for server-side failure.

    Do not return 200 with { "success": false } for every failure. HTTP-aware clients and monitoring should be able to classify the response without first decoding a private envelope.

    Give errors one predictable shape

    RFC 9457 defines Problem Details for HTTP APIs using the application/problem+json media type. Its members can include type, status, title, detail and an occurrence-specific instance; an API can add extension fields for structured validation information (RFC 9457 — Problem Details for HTTP APIs).

    For example:

    {
      "type": "https://api.example.test/problems/invalid-request",
      "title": "The request contains invalid fields",
      "status": 422,
      "detail": "Correct the listed fields and submit again.",
      "instance": "/problems/01J8EXAMPLE",
      "errors": [
        { "pointer": "/email", "code": "invalid_format" }
      ]
    }

    Keep human text safe for display, but give clients stable machine-readable types or codes. Do not expose stack traces, SQL, filesystem paths, secrets or internal hostnames. Put a correlation identifier in the response and logs so support can investigate without revealing internals.

    Decision path for HTTP API Design: Stable Semantics, Useful Errors and Safe Change, covering Use status codes to describe the HTTP outcome, Give errors one predictable shape, Design collections, filters and pagination e…
    Decision path: Use status codes to describe the HTTP outcome; Give errors one predictable shape; Design collections, filters and pagination explicitly; Protect concurrent updates.

    Design collections, filters and pagination explicitly

    Collections grow. Define pagination before a client depends on an unbounded response. Offset pagination is easy to understand but can duplicate or skip records as data changes and can become expensive at large offsets. Cursor pagination can provide stable traversal when the cursor encodes a deterministic order, but cursors should be opaque to clients and protected from tampering.

    Document:

    • default and maximum page size;
    • stable sort keys and tie-breakers;
    • filter syntax and allowed combinations;
    • whether total counts are exact, estimated or omitted;
    • links or tokens for next and previous pages; and
    • behaviour when records change between requests.

    Reject unsupported filters rather than silently ignoring them. Bound expensive search and aggregation paths to protect the service.

    Protect concurrent updates

    Two clients can read the same resource and overwrite each other's change. HTTP conditional requests provide a standard control. The server can return an ETag; a client sends If-Match with an update, and the server returns 412 Precondition Failed if the representation changed. RFC 9110 specifies the evaluation order for request preconditions.

    Do not invent a last-write-wins policy accidentally. Choose whether conflicts should be rejected, merged or represented as a business workflow. Test simultaneous requests, retries and partial failures.

    Define caching rather than inheriting surprises

    GET responses can be cacheable, including by browsers and intermediaries. Use Cache-Control, validators and Vary according to whether content is public, private, personalised or dependent on request headers. RFC 9111 describes when caches may store and reuse a response (RFC 9111 — HTTP Caching).

    Authenticated does not automatically mean uncacheable, but shared caching of personalised content requires careful, explicit controls. Test that one user's response cannot be served to another. Mutation responses should invalidate or update relevant cached representations through a defined strategy.

    Treat authentication and authorisation as separate decisions

    Authenticate the caller using a mechanism appropriate to the client type and threat model. Authorise every operation and object at the server; possession of a valid token does not grant access to every resource. Scope credentials narrowly, rotate secrets, validate token audience and issuer, and require protected transport.

    Apply input limits, schema validation and safe parsing. Rate limits can protect capacity but are not a complete denial-of-service strategy. Log security-relevant decisions without storing access tokens or unnecessary personal data.

    For browser clients, assess cross-origin policy and request-forgery risks based on where credentials are stored and automatically sent. CORS is a browser read-control mechanism, not authentication.

    Control and evidence map for HTTP API Design: Stable Semantics, Useful Errors and Safe Change, covering Protect concurrent updates, Define caching rather than inheriting surprises, Treat authentication and authorisation…
    Control and evidence map: Protect concurrent updates; Define caching rather than inheriting surprises; Treat authentication and authorisation as separate decisions; Document and test the contract.

    Document and test the contract

    An OpenAPI description can make operations, parameters, schemas and responses reviewable and can support generated documentation or tests. Keep it in the same change workflow as implementation, and verify that deployed behaviour matches the description. Generated clients do not resolve ambiguous business semantics.

    Test:

    • valid and invalid representations;
    • authentication and object-level authorisation;
    • each documented status and problem type;
    • pagination boundaries and concurrent data changes;
    • timeouts, retries and duplicate requests;
    • conditional updates;
    • cache behaviour; and
    • backward compatibility with supported clients.

    Evolve deliberately

    Prefer additive changes: new optional fields, new resources and new operations. Clients should usually ignore unknown response fields, while servers should reject unknown or invalid request fields according to the documented policy. Changing a field's type or meaning is breaking even if its name remains.

    When a breaking change is unavoidable, define the supported versions, migration guide, telemetry, deprecation notice and removal date. A version number in the path does not replace lifecycle management. Keep old versions secure during their support window and remove them only after checking actual client use.

    For help designing or reviewing a web API contract and implementation plan, see Ozlin Info's web development services or contact Ozlin Info.

    Related reading: Testing web applications: a risk-based strategy that scales.


    General-information disclaimer

    This article provides general technical information only. A production API design must reflect its clients, data, threat model, performance, compatibility and contractual requirements. Standard HTTP semantics do not by themselves make an API secure or reliable.

    AI-assistance disclosure

    AI tools assisted with source discovery, outlining and copyediting. A human reviewer must verify the protocol statements, examples, security guidance, service claims and publication decision before release. No compatibility, security or availability outcome is guaranteed.

    Practical checklist for HTTP API Design: Stable Semantics, Useful Errors and Safe Change, covering Evolve deliberately, General-information disclaimer, AI-assistance disclosure and related review points.
    Practical checklist: Evolve deliberately; General-information disclaimer; AI-assistance disclosure; Primary sources checked.

    Primary sources checked

    Source access date: 29 August 2026.