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

Collections

Collections are like a big file folder -- you all objects in a collection should use the same fields and, therefore, be easily filtered and sorted.

Collections should always be plural

/people
/agencies
/roles

Collections should list the items (not be the contents)
This is a tricky balance: you want to include enough information in the response from a collection to be meaningful, but you don’t need to return the complete detail of the actual object in that collection.

For example, the /people collection may return this:

{ "content": [ { "id": "0f52e80c-4fc5-11e8-9c2d-fa7ae01bbebc", "userName": "somebody@somewhere.ext", "firstName": "", "lastName": "", "agencyCode": [ "10301", "8765" ], "created": 1289664881, "modified": 1475142772 }, { "id": "1f52e80e-efc5-a1e6-0c2d-ea7ae01bbc098", "userName": "john.doe@tic.ext", "firstName": "John", "lastName": "Doe", "agencyCode": [ "5217", "1234" ], "created": 1389664892, "modified": 1575142792 }

but the detail of a person may contain a great deal more information

{ "id": "a795b4c2-2dd6-4004-8ecd-daa772bb78e9", "userName": "somebody@somewhere.ext", "firstName": "", "lastName": "", "emailAddress": [ { "email": "somebody@somewhere.ext", "verified": "no", "source": "from userName", "roles": [ { "email": "somebody@somewhere.ext", "roleType": "Company Roles", "role": "Company Coordinator", "source": "Salesforce", "created": 1389664892, "modified": 1575142792, "agencyCode": "10301", "accountId": "8a1d7aca-8e2f-4dbc-88d5-bd0c3fec0833" } ] } ], "agencyCode": [ "10301", "8765" ], "groups": [ { "agencyCode": "8765", "agencyName": "Thee Initial Corporation", "groups": [ "Company Roles Company Coordinator", "A - Company (Web-based)", "Partner" ] }, { "agencyCode": "10301", "agencyName": "Some Co.", "groups": [ "Company Roles Company Coordinator", "A - Company (Web-based)", "Partner" ] } ], "created": 1289664881, "modified": 1475142772 }

Informative footers

The bottom of the collection should return information about the collection that can be used to paginate and to understand the way the data was returned:

"last": false, "totalPages": 1488, "totalElements": 29754, "first": false, "sort": [ { "direction": "ASC", "property": "accountName", "ignoreCase": false, "nullHandling": "NATIVE", "ascending": true } ], "numberOfElements": 20, "size": 20, "number": 5

Or, a more old-school comparison of data and descriptions:
"totalElements": 29754,
"size": 20,
"totalPages": 1488,
"numberOfElements": 20,
"first": false,
"last": false,
"number": 5,
"sort": null
There are 29,754 items in this collection
We're returning 20 elements per page
There are 1,488 pages in this response (20/page)
There are 20 items on this page
This isn't the first page of the collection
This isn't the last page of the collection
We're on page 5
The results aren't sorted (asc|desc) (see below for sort array)

Because we allow sorting multiple ways, the sort block is an array of JSON elements that describe the different elements and the direction they are sorted on

"sort": [ { "direction": "ASC", "property": "accountName", "ignoreCase": false, "nullHandling": "NATIVE", "ascending": true }, { "direction": "DESC", "property": "agencyCode", "ignoreCase": false, "nullHandling": "NATIVE", "ascending": false } ],

Accessing Items in Collections

Generally, individual items inside a collection should be accessible by a unique key like

    /collection/[itemId]

Or, as a real world example:

    /people/56e21c83e4b055afc5ee1705

However, it is permissible to alias the unique key to a human readable id such as

    /people/bisselaltor@gmail.com

This human readable also provides us with the ability to add path-variable security based on OAuth scopes (for example, if I have a scope that restricts access to my own company record based on /agencies/$agencyCode/* and my agecnyCode is returned as 1234, I would able to access /agencies/1234/agreeements but not /agencies/4567/agreements.

Collections within collections

When there is a sub collection (say all the products for an agency) you should be able to reference the sub-collection by a unique name.

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