This issue contains a draft style guide for use with Zalando APIs akin to that provided by Google, for discussion. It could be a standlone document or a top level section of the existing guidelines (the latter would save having to work with two documents). There is something to be said for being able to think about media type and JSON content design independent of protocol concerns instead of having them all at the same level.
If we go ahead with this, the prs would probably be
- Create a top level section in the current guidelines and re-organize what's already in place to refactor the document. Existing guidelines will get moved around but won't have their specifications changed.
- Add in new guidelines as another pr.
- See if we want to break the JSON guidelines out to another document or leave them in place.
Note: sections below marked with an asterisk are extracted from the existing guidelines - the exact content below might be out of date with the exact guideline text in master, but that can be fixed up if this becomes a pr. In the meantime it's representative of what things would end up looking like.
JSON Guidelines
Table of Contents generated with DocToc
Introduction
This guide provides recommendations for defining JSON data at Zalando. JSON here refers to RFC 7159, which updates RFC 4627 and the “application/json” media type. This guide clarifies some specific cases to allow Zalando JSON data to have an idiomatic form across teams and services and must be applied to JSON based APIs.
The keywords "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", and "MAY" in this document are to be interpreted as described in RFC 2119.
Property Naming
Must: Property names must be snake_case (and never camelCase)*.
No established industry standard exists, but many popular Internet companies prefer snake_case: e.g. GitHub, Stack Exchange, Twitter. Others, like Google and Amazon, use both - but not only camelCase. It’s essential to establish a consistent look and feel such that JSON looks as if it came from the same hand.
Must: Property names must be an ASCII subset
Property names are restricted to ascii encoded strings. The first character must be a letter, an underscore or a dollar sign, and subsequent characters can be a letter, an underscore, a dollar sign, or a number.
Should: Reserved JavaScript keywords should be avoided
Most API content is consumed by non-JavaScript clients today, but for security and sanity reasons, JavaScript (strictly ECMAScript) keywords are worth avoiding.
Should: Array names should be pluralized
To indicate they contain multiple values prefer to pluralize array names. This implies that object names in turn should be singular.
Property Values
Must: Boolean property values must not be null
Schema based JSON properties that are by design booleans must not be presented as nulls. A boolean is essentially a closed enumeration of two values, true and false. If the content has a meaningful null value, strongly prefer to replace the boolean with enumeration of named values or statuses - for example accepted_terms_and_conditions with true or false can be replaced with terms_and_conditions with values yes, no and unknown.
Should: Null property values should have their fields removed*
Swagger/OpenAPI, which is common use, doesn't support null field values (it does allow omitting that field completely if it is not marked as required). However that doesn't prevent clients and servers of sending (and accepting) those fields with null values.
Should: Empty array values should not be null
Empty array values can unambiguously be represented as the the empty list, [].
Should: Enumerations should be represented as Strings
Strings are a reasonable target for values that are by design enumerations.
Should: Date property values should conform to RFC 3399*
use the date and time formats defined by RFC 3339 — e.g. 2015-05-28T14:09:17+02:00
for a point in time (note that the OpenAPI format "date-time" corresponds to "date-time" in the RFC) and 2015-05-28
for a date (note that the OpenAPI format "date" corresponds to "full-date" in the RFC). Both are specific profiles, a subset of the international standard ISO 8601.
A zone offset may be used -- this is simply defined by the standards. However, we encourage to restrict to UTC (and go without offsets) when dates are passed and returned. From experience we know that zone offsets are not easy to understand and often not correctly handled. Note also, that zone offsets are different from local times that may include daylight saving time.
When it comes to storage, all dates should be consistently stored in UTC without zone offset. Localization should be done locally by the services that provide user interfaces, if required.
Sometimes it can seem data is naturally represented using numerical timestamps, but this can introduce interpretation issues with precision - for example whether to represent a timestamp as 1460062925, 1460062925000 or 1460062925.000. Date strings though more verbose and requiring more effort to parse, lack this ambiguity.
May: Time durations and intervals may conform to ISO 8601
Schema based JSON properties that are by design durations and intervals may be strings formatted as recommended by ISO 8601.
note: unsure if we need to spec the form of durations/intervals yet, or we should wait for implementation feedback/need. The duration guideline is taken from google's document.
May: Standards may be used for Country, Language and Currency Codes*
Common Objects
Should: Money should be represented using a common object*
Use the following common money structure:
Money:
type: object
properties:
amount:
type: number
format: decimal
example: 99.95
currency:
type: string
format: iso-4217
example: EUR
required:
- amount
- currency
Make sure that you don’t convert the “amount” field to float
/ double
types when implementing
this interface in a specific language or when doing calculations. Otherwise, you might lose
precision. Instead, use exact formats like
Java’s BigDecimal
.
See Stack Overflow for more info.
Some JSON parsers (NodeJS’s, for example) convert numbers to floats by default. After discussing the
pros and cons,
we’ve decided on "decimal" as our amount format. It is not a standard OpenAPI format, but should
help us to avoid parsing numbers as float / doubles.
Should: Addresses should be represented using a common object*
Address structures play a role in different functional and use-case contexts, including country variances. The address structure below should be sufficient for most of our business-related use cases. Use it in your APIs — and compatible extend it if necessary for your API concerns:
address:
description:
a common address structure adequate for many use cases
type: object
properties:
salutation:
type: string
description: |
A salutation and/or title which may be used for personal contacts. Hint: not to be confused with the gender information that is stored per customer account
example: Mr
first_name:
type: string
description: given name(s) or first name(s) of a person; may also include middle names
example: Hans Dieter
last_name:
type: string
description: family name(s) or surname(s) of a person
example: Mustermann
business_name:
type: string
description: company name of the business organization
example: Consulting Services GmbH
street:
type: string
description: full street address including house number and street name
example: Schönhauser Allee 103
additional:
type: string
description: further details like suite, apt no, etc.
example: 2. Hinterhof rechts
city:
type: string
description: name of the city
example: Berlin
zip:
type: string
description: Zip code or postal code
example: 14265
country_code:
type: string
format: iso-3166-1-alpha-2
example: DE
required:
* first_name
* last_name
* street
* city
* zip
* country_code
Hypermedia
Should: HATEOAS should be used
This is a challenging topic. We don’t have specific recommendations yet; we expect them to emerge
from trial and error, and from the API Guild’s ongoing research. For now, we strongly encourage
teams to apply this principle and document results.
Notes on aspects to be analyzed:
- Security concerns with respect to passing OAuth2 tokens
- API discovery by client programmers vs. automated hypermedia driven workflows
- Additional client development work vs. reduced efforts in case of API and state handling changes
Additional resources: Wikipedia, The RESTful CookBook
Should: Standards should be used for Hypermedia Link Types
To represent hypermedia links in payload results, we should consider using a standard format like HAL, JSON API, JSON-LD with Hydra, or Siren. (Hint: Read Kevin Sookocheff’s post, for instance, to learn more about the hypermedia types.)
Should: Standards should be used for linked/embedded resources
For HTTP Link headers, consider using a format like HAL,JSON-LD with Hydra, or Siren. For a comparison of these hypermedia types, see
Kevin Sookocheff’s post.
May: URIs may be used for Custom Link Relations
Related or even embedded (sub-) resources should be linked via “meta link attributes” within the response payload; use here the following HAL compliant syntax:
{
“_links”: {
“https://docs.team.zalan.do/rels/my-entity”: [{
“href”: “https://service.team.zalan.do/my-entities/123”
}]
}
}