Follow @bissell
Michael's blog
Michael's resume


The API Contract
Default Formats
General API Management
Error Standards
Response Codes
Collections
Filters
Cherry Picking
Conveniences
Change Logs

Default Formats

We like to think of REST as a standard, but it's a very flexible standard that's open to a lot of interpretation. In order to help keep development teams focused and make it easier for the QA team to know what to expect, I have created (with a lot of collaboration!) some basic guidelines for how I like to design API contracts.

IDs

A unique identifier is required for every resource. We prefer UUIDs (universally unique identifier), a 128-bit number used to identify information in computer systems. The term globally unique identifier (GUID) is also used.

{ "dogId" : "0f52e80c-4fc5-11e8-9c2d-fa7ae01bbebc", "color" : "brown", "breed" : "mutt", "created" : 1378860183, "modified" : 1478040396 }

JSON vs XML

We assume that all items will be returned by default as JSON -- currently we do not support XML but if we do, it would be by specifying the "Accept" header with
Accept: text/xml

camelCase Fieldnames

Generally, the fieldnames in a JSON response are all lowercase. However, if you have a compound word, like "My Record" should be forced to camel case, i.e. myRecord.

Timestamps

Each item should have a created and a modified timestamp. Timestamps should be in seconds from the epoch, so Wed, 11 Sep 2013 00:43:03 GMT becomes 1378860183 and Tue, 01 Nov 2016 22:46:36 GMT becomes 1478040396. All timestamps are in UTC and should always be returned as numbers, not strings.

Therefore, a record created on September 11, 2013 at 12:43:03 AM and modified on November 1, 2016 at 10:46PM would look like this:

{ "myRecordId" : "some string", "created" : 1378860183, "modified" : 1478040396 }

Deletions

There are two ways to handle a DELETE. In most cases we do not want to actually, permanently DELETE content on the API. So the preferred method is to intercelt the DELETE verb, and update the record with an "isDeleted" : true
DELETE /mycollection/0f52e80c-4fc5-11e8-9c2d-fa7ae01bbebc Response: 200 OK { "resourceId" : "0f52e80c-4fc5-11e8-9c2d-fa7ae01bbebc", "isDeleted" : "Y" }

We need to decide if that record is visible to all requests, if we keep the other data that was associated with the record, or if we just hide it as if it was deleted.

The other option is to actually remove the record from the data store in which case we respond with a 200 OK and an empty payload:

DELETE /mycollection/0f52e80c-4fc5-11e8-9c2d-fa7ae01bbebc Response: 200 OK

200 OK tells me something happened. It's not a 404 because the system actually removed the record. It would be a 404 if you tried to DELETE something that didn't exist. So if you sent the request a second time you'd get this:

DELETE /mycollection/0f52e80c-4fc5-11e8-9c2d-fa7ae01bbebc Response: 404 Not Found {optional error message payload}

Filtering Deletions

By default, a collection will not return deleted records. In effect
GET /collection
Is the same as requesting
GET /collection?isDeleted=false
To get all the records in the collection (deleted or not), you would pass the isDeleted filter explicitly, which overwrites the default "false" filter with whatever you passed, such as
GET /collection?isDeleted=true GET /collection?isDeleted=$in:true,false

Null Values

The API contract should return fields even if there is no value in that field; how we represent that field changes depending on what type of data the field would return if there was a value.

string: return empty string ""
number: return null
array: return empty array []

So, that means we could have a payload like this:

{ "myString": "", "myNumber": null, "myArray": [] }


Be sure to see my blog over at Cloudenity. This week's topic: Identity Isn't Just for Users Anymore