General API Considerations

From Ocean Framework Documentation Wiki
Jump to: navigation, search


Protocol, Encoding, and Constraints

All services communicate exclusively over HTTP on port 80, using UTF-8 encoded JSON data transferred according to REST conventions, including the HATEOAS hypermedia constraint.


Get–Ignore–Follow

All clients and servers follow the same set of API conventions, which in their most terse form can be summed up as “GET resource, ignore excess, follow hyperlinks”.

The somewhat longer and partly repetitive version is this:

Resources have unique URIs
Every resource in the system – users, shopping carts, items, searches, etc – has its own basic URI from which a JSON resource representation is obtained through an HTTP GET. Documentation describes all such resource URIs and the HTTP methods they support.
Resources are hypermedia
They can link to any URI and any type of content, not just JSON, but any type of text or binary format. API resource representations are in JSON, but they can link to anything anywhere on the internet.
Excess data in resources should be ignored
This allows APIs to evolve without upping the version number when data fields and/or methods are added to resource without affecting the existing contract, which is most of the time. However...
Excess data in resources must be preserved
If a resource contains an unspecified data field, it should be preserved unchanged when updating the resource. Resources should be treated as a whole and updated as a whole, with all the data previously received preserved in the interim.
Resources always contain a self hyperlink
This, the URI mentioned above, is used as an URL in requests and as an URN in databases.
Resources contain hyperlinks for possible next steps
Resources contain named hyperlinks to URIs representing the next possible steps in handling them. Documentation for a resource describes all available hyperlinks. Not all of them might be present at the same time in resources which have state. Clients use only the embedded hyperlinks in order to manipulate a resource.
Hyperlinks are opaque
Query arguments may be added to some URIs, but the URIs themselves are opaque. No conclusions may be drawn from parsing hyperlinks. This frees developers from having to learn conventions for constructing URIs. All they need to know are the public API entry points for each resource. The concept of named hyperlinks also allows the server to modify the URI scheme without the clients having to be updated. This is extremely important as the APIs evolve.
Clients may not construct URIs except initially to obtain the basic resource
To be fair, nothing prevents a client from constructing URLs by concatenating strings, but clients that do so are on their own, as secondary URLs are subject to change at any time without previous notice. By using the main resource entry point to discover data and the available operations on them via their embedded dynamic hyperlinks, a very high degree of decoupling is achieved. This reduces client development costs and increases client life.
Clients may save links persistently
Services will handle detection of legacy links and will upgrade them transparently. This is also how external partners' computer systems store references to resources in our API: they are simply represented by the self hyperlink URI, stored in the external system as a URN. External partners must never decompose any such URI in order to obtain a shorter or simpler ID; they must always store the whole self hyperlink URI as a string, for the purpose of later manipulating the resource. This includes the URI protocol, query args, anchors, etc.

HTTP Methods

The following are the main HTTP methods in Ocean:

POST
A POST to the collection URI of a resource creates a new resource and returns its JSON representation. The operation is not idempotent.
GET
GET requests are use to obtain a JSON resource representation of a resource member or collection. GET operations should always be idempotent and may be cached.
HEAD
A HEAD request is treated as a GET but the response contains only headers.
PUT
Used to update all the attributes of a resource at the same time, i.e., to completely change its state. The operation is idempotent by definition: subsequent PUT requests with the same data should put the resource in exactly the same state and have no other side effects (e.g., a PUT request to an API endpoint designed to add something to a collection must only add it once and must not generate an error for subsequent, identical requests).
In Ocean, however, PUT can be used to only set some attributes of a resource. Ocean services will generate an error if required attributes aren't supplied, but optional attributes can be left out, in which case the semantics become exactly like those of PATCH and idempotency no longer holds. It's up to you to ensure that all attributes are included in the PUT.
PATCH
Used to update some (or all) attributes of a resource. Ocean treats PATCH exactly like PUT. PATCH is not idempotent unless all attributes are set.
DELETE
This method is used to destroy a resource. It is by definition an idempotent operation, as multiple DELETE requests to the same URL always produce the same state.
PURGE
Ocean services can use the PURGE method to expire single URIs in the Varnish cache. PURGE requests are only allowed from the local network.
BAN
Ocean services can use the BAN method to expire multiple URIs in the Varnish cache. BAN requests are only allowed from the local network.


Date and Time

All datetimes are expressed as strings in UTC/GMT, Greenwich Mean Time, expressed unambiguously in ISO-8601 format:

"2012-12-01T17:09:04Z"

It is up to the client to submit datetime strings in a parseable, unambiguous standard ISO format. Times should be in GMT/UTC. If in other time zones, they will be converted to GMT wherever possible. Conversely, clients are also expected to convert GMT time strings to a user-readable format in the local time zone when presenting them to end users.

API Versioning

All resources are versioned. The version number is part of the URI (URL, URN). All clients should specify the exact version of each resource when first requesting them. After sending the request to create, obtain, update or delete a resource, the client should completely ignore the version of the resource and treat the URI as opaque, as the API may transparently upgrade it.

Thus, you may request a resource of version v1 but receive the resource in v8 or any later version. Resources are guaranteed to be backward compatible. Similarly, you may PUT a resource of a certain version but receive the same updated resource in a more recent version. In this way, the API is able to evolve transparently. At any point in time, resources of later versions than available at the time of writing a client can, may, and will float around in your client's data structures. In fact, you will always get Resources of the latest version when you create (POST) or update (PUT) them.

This is the reason for the oft repeated rule in these docs to never draw any conclusion from the structure of any URI you are given by the API. It may change without any notice. Don't build URLs except for the public ones.

Bottle.jpg NOTE: The version is included in the URI and not in an HTTP header is for reasons of caching efficiency in public, shared caches. Many caches don't cache on vendor types, but all cache on URLs.

Unified URI structure

Ocean uses a unified URI structure for all API endpoints:

https://api.example.com/<version>/<plural-resource-name>[<resource-subpath>][?<query>][#<fragment>]

As you can see, the format is the standard HTTP URI format, with the added convention that the URI path always begins with a resource version followed by the name of the resource in plural.

version
The version is always a string starting with a lowercase v, followed by a positive integer, e.g. v1, v2, v3 and so forth.
plural-resource-name
The resource name is always in plural and in all lowercase: texts, media, groups, etc. Underscores may be used, e.g. api_users, media_buckets.

Here's an example:

https://api.example.com/v3/services

This URI represents the collection of all Services available in an Ocean system. It could be used as a URL in an HTTP request to retrieve the representation of all Services, like so:

GET /v3/services HTTP/1.1

Or it could be used as a URN and stored in a database for later use as a URL in a request.

Bottle.jpg NOTE: As a client, if you need to store a resource URI in a database (that is, when you're using the URI as a URN), be sure to store the entire URI string in the database, that is, including the protocol, host name, etc. The whole string is the ID. This is another example of the rule that URIs should be treated as opaque in all situations.

Public URIs

API descriptions give only the Public URIs supported for a service, that is, its entry points. A client should always start with one of these, and then use its embedded hyperlinks to discover what can be done with the resource and any related resources. All URLs necessary for further manipulation, or further discovery of related resources, will be given in the Resource Representation returned by requests to a Public URI.

As an example, a User Management service may define a resource called a User, with a particular URI for a specific user. The representation returned will always contain a link to the User object itself – its universal ID – but it might also contain hyperlinks to an Address resource, a Phone resource, an Employer resource, and so on. It could also contain these resources embedded in itself; if so, the embedded resource representations will be self-contained, each representation having at least a self hyperlink.