Zalando RESTful API Guidelines

repository·main·Indexed 25 days ago

https://github.com/zalando/restful-api-guidelines

Guidelines and best practices for designing and operating RESTful APIs at Zalando. Includes implementation details for cursor-based pagination, optimistic locking via ETags and Last-Modified headers, OpenAPI specification publishing, and language-specific examples for parsing ISO 8601 intervals in Java, Go, and Python.

Tokens
26.9K
Snippets
50
Records
136
Agent score
84%

What's inside zalando-restful-api-guidelines

  1. Overview of Zalando RESTful API Guidelines

    main
    The Zalando RESTful API Guidelines provide a comprehensive set of best practices designed to help teams build consistent, high-quality APIs. The goal is to promote API adoption and reduce friction for clients by ensuring APIs look like they were designed by a single cohesive team. These guidelines are intended to be used as a living document to inspire discussion, refine API design, and challenge existing API implementations.
  2. Understand REST API terminology

    main

    When following these guidelines, distinguish between the following entity types:

    • OpenAPI Specification (OAS): The standard format for REST API specifications. The guidelines encourage using OpenAPI 3.1.
    • API Specification (OpenAPI Description / OAD): The document in OAS format that defines the API's syntax and semantics. It serves as the interface definition for clients.
    • API: The actual REST API interface provided over HTTP/TCP/IP by a running, deployed service.
    • API Implementation: The actual code (e.g., Java, Python) that makes up the service. Note that the API Specification and the API Implementation may differ if the implementation is incomplete or out-of-date.
  3. Guidelines for using UUIDs as identifiers

    main

    While UUIDs allow for distributed, non-coordinated ID generation, they should be avoided when not strictly necessary due to high memory/bandwidth consumption and lack of human readability.

    Recommendations:

    • Avoid using UUIDs as primary keys for master or configuration data (e.g., brand-ids).
    • Prefer server-side ID generation (e.g., POST on an ID resource followed by PUT on the entity).
    • Use String Types: Always use string rather than number for identifiers to allow for future evolution of the naming scheme.
    • UUID Format: If using UUIDs, do not qualify them with a format property in OpenAPI.
    • Alternative: For pagination where creation order matters, consider ULID (Universally Unique Lexicographically Sortable Identifier) instead of UUID.
  4. Use POST for creating resources

    main

    Use POST to create single resources on a collection or to execute specific requests.

    • Collection Semantics: "add the enclosed representation to the collection identified by the URL."
    • Success Codes:
      • Return 201 and the new resource object (including identifier) in the payload.
      • Provide the URL to the new resource in the Location header.
      • For multiple resources created atomically, return 201.
    • Constraint: The resource identifier MUST NOT be passed in the request body; it must be created and maintained by the service.
    • Async: Return 202 if creation is not finished by the time the request is delivered.
  5. Use full, absolute URIs for resource identification

    main
    All links to other resources must use full, absolute URIs. Do not use relative URIs. If the linked resource is part of the same API (running service), the link should use the same scheme, host, and port combination as the original request URI to avoid client-side complexity and authorization issues.
  6. Document and implement caching for GET, HEAD, and POST endpoints

    main

    Caching should be avoided unless the service requires it (e.g., for rate-limited master data).

    Default Behavior: Servers and clients should default to Cache-Control: no-cache, no-store, must-revalidate, max-age=0 if no header is provided.

    Requirements for Cacheable Endpoints: If you enable caching, you must:

    1. Document all cacheable GET, HEAD, and POST endpoints.
    2. Declare support for Cache-Control, Vary, and ETag headers in the response.
    3. Do not use the Expires header (use Cache-Control instead).
    4. Provide sensible Cache-Control and Vary values to define caching boundaries.
    5. Provide efficient methods to warm up and update caches.

    Important: For proper ETag support, return a 304 Not Modified (without content) for failed HEAD or GET requests with an If-None-Match header, rather than a 412 Precondition Failed.

    Cache-Control: no-cache, no-store, must-revalidate, max-age=0
  7. Use support libraries for RESTful API implementation

    main

    The following utility libraries assist in implementing specific parts of the RESTful API guidelines:

    • Problem: A Java library that implements the application/problem+json media type.
    • Jackson Datatype Money: A Jackson extension module for proper support of javax.money datatypes.
    • Tracer: A library for call tracing and log correlation in distributed systems.
    • Spring Framework (Error Responses): A Java application framework that implements application/problem+json for error handling.
  8. Define useful business resources for events

    main

    Events should be centered around the resources and business processes defined for your service domain and should adhere to their natural lifecycle.

    To avoid an explosion of event types and topics, prefer defining event types that are abstract or generic enough to be valuable for multiple use cases. Avoid publishing event types that do not have a clear business need.

  9. Define Bearer Authentication in OpenAPI

    main

    To secure your API using JWT tokens (standard for internal Zalando APIs), define an http typed bearer security scheme in your OpenAPI specification. This follows RFC 6750 and uses the Authorization: Bearer <token> header.

    Avoid using oauth2 typed security schemes (like implicit) if your service only implements a simple bearer token scheme, as it may unnecessarily expose authentication server details.

    components:
      securitySchemes:
        BearerAuth:
          type: http
          scheme: bearer
          bearerFormat: JWT
  10. Evolve APIs using compatible extensions

    main

    To avoid breaking changes and the need for versioning, follow these rules for evolving schemas. Compatibility depends on whether the schema is used for input, output, or both.

    For Input-only schemas

    • Add optional fields; never add mandatory fields.
    • Make mandatory fields optional, but never vice-versa.
    • Do not remove fields (removing a field is considered non-compatible).
    • Validation logic: Never make validation logic more restrictive. Ensure all constraints are clearly defined in the description.
    • Enums: You can extend enum ranges, but you can only reduce them if the server continues to accept and handle the old values.

    For Output-only schemas

    • Add fields (mandatory or optional).
    • Make optional fields mandatory, but never vice-versa.
    • Do not remove fields.
    • Enums: You can reduce enum ranges, but you cannot extend them (clients may not be prepared for new values). Use extensible enums (see below) to mitigate this.

    For Input and Output schemas (Combined rules)

    • Add only optional fields; never mandatory fields.
    • Do not remove any fields.
    • Do not toggle mandatory/optional status.
    • Validation: Never make validation logic more restrictive.
    • Enums: You can reduce ranges only if the server still accepts old values, but you cannot extend them.
  11. Follow the functional naming schema

    main

    To ensure stability during organizational changes, use a functional naming schema for components. A <functional-name> is composed of a domain and a component name:

    BNF Definition:

    <functional-name>      ::= <functional-domain>-<functional-component>
    <functional-domain>    ::= [a-z][a-z0-9-]*
    <functional-component> ::= [a-z][a-z0-9-]*

    Usage requirements based on Audience:

    • MUST follow for: external-public, external-partner
    • SHOULD follow for: company-internal, business-unit-internal
    • MAY follow for: component-internal