Zalando RESTful API Guidelines
repository·main·Indexed 25 days ago
https://github.com/zalando/restful-api-guidelinesGuidelines 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.
What's inside zalando-restful-api-guidelines
- 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.
Understand REST API terminology
mainWhen 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.
Guidelines for using UUIDs as identifiers
mainWhile 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.,
POSTon an ID resource followed byPUTon the entity). - Use String Types: Always use
stringrather thannumberfor identifiers to allow for future evolution of the naming scheme. - UUID Format: If using UUIDs, do not qualify them with a
formatproperty in OpenAPI. - Alternative: For pagination where creation order matters, consider ULID (Universally Unique Lexicographically Sortable Identifier) instead of UUID.
- Avoid using UUIDs as primary keys for master or configuration data (e.g.,
Use POST for creating resources
mainUse
POSTto 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
201and the new resource object (including identifier) in the payload. - Provide the URL to the new resource in the
Locationheader. - For multiple resources created atomically, return
201.
- Return
- Constraint: The resource identifier MUST NOT be passed in the request body; it must be created and maintained by the service.
- Async: Return
202if creation is not finished by the time the request is delivered.
Use full, absolute URIs for resource identification
mainAll 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.Document and implement caching for GET, HEAD, and POST endpoints
mainCaching 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=0if no header is provided.Requirements for Cacheable Endpoints: If you enable caching, you must:
- Document all cacheable
GET,HEAD, andPOSTendpoints. - Declare support for
Cache-Control,Vary, andETagheaders in the response. - Do not use the
Expiresheader (useCache-Controlinstead). - Provide sensible
Cache-ControlandVaryvalues to define caching boundaries. - Provide efficient methods to warm up and update caches.
Important: For proper
ETagsupport, return a304 Not Modified(without content) for failedHEADorGETrequests with anIf-None-Matchheader, rather than a412 Precondition Failed.Cache-Control: no-cache, no-store, must-revalidate, max-age=0- Document all cacheable
Use support libraries for RESTful API implementation
mainThe following utility libraries assist in implementing specific parts of the RESTful API guidelines:
- Problem: A Java library that implements the
application/problem+jsonmedia type. - Jackson Datatype Money: A Jackson extension module for proper support of
javax.moneydatatypes. - Tracer: A library for call tracing and log correlation in distributed systems.
- Spring Framework (Error Responses): A Java application framework that implements
application/problem+jsonfor error handling.
- Problem: A Java library that implements the
Define useful business resources for events
mainEvents 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.
Define Bearer Authentication in OpenAPI
mainTo secure your API using JWT tokens (standard for internal Zalando APIs), define an
httptypedbearersecurity scheme in your OpenAPI specification. This follows RFC 6750 and uses theAuthorization: Bearer <token>header.Avoid using
oauth2typed security schemes (likeimplicit) 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: JWTEvolve APIs using compatible extensions
mainTo 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
enumranges, 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
enumranges, 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.
Implement REST Maturity Level 2
mainAPIs must implement at least REST Maturity Level 2. This means making full use of HTTP verbs (GET, POST, PUT, DELETE, etc.) and appropriate HTTP status codes to manage resource-oriented interactions.Follow the functional naming schema
mainTo 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
- MUST follow for: