Michael Bissell
  • Twitter
  • LinkedIn
  • Contact me

API Standards

January 3, 2022

All our APIs follow a basic set of rules that provide consistent requests and responses for the API. This document provides the basic rules that you get when you use our platform.

Objects
Objects should be returned in collections and objects and we follow a path structure that reflects this:

collection/{collectionobjectID}

ObjectIDs should be abstract UUIDs that do not contain any identifiable information. UUID v4 format is preferred as they are generated using random numbers and not timestamps (unlike UUID v1). Again, the goal is to limit any meaningful info from the IDs

Here's a quick sample of the UUID format:

19914fd1-aa7a-4c58-acb8-969bcc123bce

Collections
A "collection" is a collection of objects. So, if you have your blog in our API, for example, you can expect to find it in the blogs collection:

GET /v1/blogs

{
  "objects" : [
     {
       object" : {
         "blogtitle" : "This is a blog",
         "blog": "**Markdown** makes storing formatted text easy...",
         "category" : "misc"
       }
     }
     "objectID" : "f9debc51-db17-4db9-a342-52b95b372c08",
  ],
  "totalObjects": 100,
  "returned": 1,
  "page": 0,
  "size": 1,
  "sortOrder": "asc",
  "filters": {}
}

Note that the collection returns some basic meta data that you can use to help navigate your data:

  • totalObjects: The total number of objects/records in the collection
  • returned: How many objects got returned in this response
  • page: Which page of results you're on (NOTE we start couting at 0)
  • size: How many objects the page could have returned (this may be bigger than returned because you might only have 4 objects in your collection on a page size of 20)
  • sortOrder: "asc" means the oldest objects were returned first, "desc" means the objects were returned newest to oldest

Subcollections Subcollections are a great way to add security to a group of objects under a master object ID. The collection pattern continues with the subcollection and its object id:

collection/{collectionobjectID}/subcollection

Which returns a collection of objects within this subcollection and lets you drill down to the individual object under that subcollection.

collection/{collectionobjectID}/subcollection/{subcollectionobjectID}

The pattern supports multiple subcollections so you may also have

collection/{collectionobjectID}/subcollection2

And, of course, you can have sub/sub collections but you will want to consider your data structure to avoid too much nesting.

Filters
You can change what gets returned with some basic query parameters such as:

GET /v1/blogs?sortOrder=desc&size=1&page=2
  • sortOrder: asc|desc sorts based on the created timestamp of the object
  • size: [integer] the number of objects to return in this one request. This can be handy to grab just one object with size=1 or everything with a ridiculously large number (not a good idea)
  • page: [integer] a number to select the page of results you're on (NOTE we start counting at 0)

Filter on Field Values
You can also add a filter using one or more of your fieldnames in the object itself. So if I wanted to just return blogs in the security category I could request

GET /v1/blogs?category=API+Security

Wildcards Fields can include a wildcard in the form of an * at the end of the string you are filtering on to search for a substring. For example

GET /v1/blogs?category=API*

This does two things:

  • Returns all records with a substring of the search (eg dog*will return dogged and boondoggle)
  • Sets search to case-insensitive (eg. dog* will return Big Dog and littledoggy)

Ranges (greater than/less than) Field filters can include $gt: and $lt: (note the colon). For example:

GET /v1/blogs?posted=$gt:2022-12-31

You can compound the filters to create a range:

GET /v1/blogs?posted=$gt:2022-12-31&$lt:2023-02-01

to get all blogs posed in January 2023

And you can still include the standard filters for size, page, and sort order.

Posting to a Collection
We create new objects by POSTing to the collection. So if I want to create a new blog I would POST to /v1/blogs

POST /v1/blogs
Authorization: Bearer {token}

{
  "blogtitle" : "My blog title",
  "blog" : "A nice thing happened while I was making an API",
  "category" : "articles"
}

The API will respond with the object you created, including the meta data for the objectID

{
    "object": {
      "blogtitle" : "My blog title",
      "blog" : "A nice thing happened while I was making an API",
      "category" : "articles"
    },
    "objectID": "6dce1470-1900-11ed-8039-af237906caea",
    "created": 1660172515127,
    "modified": 1660172515127
}

Objects
See above (Posting to Collections) to create an object

You request an object with its objectID

GET /v1/blogs/6dce1470-1900-11ed-8039-af237906caea

Which will return the same payload that the original POST request returned:

{
    "object": {
      "blogtitle" : "My blog title",
      "blog" : "A nice thing happened while I was making an API",
      "category" : "articles"
    },
    "objectID": "6dce1470-1900-11ed-8039-af237906caea",
    "created": 1660172515127,
    "modified": 1660172515127
}

Objects have some consistent metadata:

  • objectID: The objectID is a machine-generated uuid that represents this single object. This is the ID that you use in the path (/v1/collection/{objectID})
  • created: A numeric timestamp (epoch time) for when this object was first created. This is generated by the API and can't be changed.
  • modified: A numeric timestamp (epoch time) for when the object was last updated. This is often the same value as created unless someone edited the object.

Updating an Object
Just a with the POST, you don't send the metadata when you PUT, just the fields from your API contract:

PUT /v1/blogs/6dce1470-1900-11ed-8039-af237906caea
Authorization: Bearer {token}

{
  "blogtitle" : "My Updated Blog Title",
  "blog" : "A nice thing happened while I was making an API",
  "category" : "articles"
}

This will respond with the updated payload and a new modified timestamp:

{
    "object": {
      "blogtitle" : "My Updated Blog Title",
      "blog" : "A nice thing happened while I was making an API",
      "category" : "articles"
    },
    "objectID": "6dce1470-1900-11ed-8039-af237906caea",
    "created": 1660172515127,
    "modified": 1660458132994
}

Deleting an Object
When you want to DELETE an object you simply make the request with the DELETE verb to the object ID

DELETE /v1/blogs/6dce1470-1900-11ed-8039-af237906caea

If the object you are deleting exists, you will receive a 200 OK and a message telling you that you have deleted the object:

{
    "msg": "resource 6dce1470-1900-11ed-8039-af237906caea removed"
}

If, however, you attempt to delete it again or try to delete an object that doesn't exist, you'll get the 404 Not Found error

{
    "error": "resource not found",
    "errorCode": "404"
}

Errors
Note: this document is in early stages and error standards are evolving

Errors are formatted in a JSON response such as

{
  "error" : "the reason for this error"
}

If an object is requested that doesn't exist you should receive a 404 response code and a human readable error. When you try to access an object or collection not described in your API you should receive a 400 Bad Request.

⟪ Build an API from a CSV file in 4 minutes | Terms of Service in lieu of Legal Governance ⟫


REAL RESUME FEEDBACK FROM REAL RECRUITERS
Your resume is your most valuable tool in your job search. But how do you know your resume is in top shape?

Our recruiters will review your resume line by line and give you detailed feedback on how you can improve it.

Visit mjlprojects.com to learn more!

Resume

  • My Resume
  • MJL Projects
  • NWEA Experience
  • Apigee Experience
  • Cloudentity Experience
  • Conquent Experience
  • Technical Skills
  • Presentation and Voice
  • API Standards
  • Podcasts

Articles

© Michael Bissell. All rights reserved.