RESTful API - Best Practices v1
Introduction
This is the initial draft of recommended best practices for API development and consumption at Columbia.
Overview
The central feature of REST architecture is the emphasis on a uniform interface between components
REST is defined by four interface constraints:
Identification of resources;
Manipulation of resources through representations;
Self-descriptive messages; and,
Hypermedia as the engine of application state
RESTful Application URL and methods
The Key principle of REST involves separating API into logical resources. These resources are manipulated using HTTP requests where the method (GET,POST,PUT,PATCH,DELETE) has specific meaning.
REST API resources are plural nouns (not verbs!) from the consumer perspective.
Versioning
Always version the API. There are two widely accepted approaches to versioning
Version as part of URL
Version as part of header
We at Columbia choose version to be part of URL:
https://<domain-app>.api.columbia.edu/{v}/{resources}
Version should be kept to the right of the app name. We choose to prefix 'v' as part of version and use only the ordinal numbers, like “v1”, “v2”, etc.
API Pattern
Use the following example patterns as a guide. Implementing every HTTP method and complex search query parameters is not required but should follow these patterns when used.
GET https://<domain-app>.api.columbia.edu/{v}/{resources} - Retrieves a list of resources
GET https://<domain-app>.api.columbia.edu/{v}/{resources}/{id} - Retrieves a specific resource
POST https://<domain-app>.api.columbia.edu/{v}/{resources} - Creates a new resource using JSON payload
PUT https://<domain-app>.api.columbia.edu/{v}/{resources}/{id} - Updates resource info
PATCH https://<domain-app>.api.columbia.edu/{v}/{resources}/{id} - Partially updates the resource
DELETE https://<domain-app>.api.columbia.edu/{v}/{resources}/{id} - Deletes the resource
Search GET https://<domain-app>.api.columbia.edu/{v}/{resources}?filter[search]={keyword}&sort={attribute} - Search for resource(s) in collection
Sparse Fieldsets List GET https://<domain-app>.api.columbia.edu/{app}/{v}/{resources}?fields[{resource}]=field1,field2,…,fieldN
Note that the above definitions have use the following query parameter names:
filter
sort
fields
For more complete details, see the JSON API Architecture Pattern.
API Pattern Example
Basic Operation APIs are shown with examples (for the directory app, version 1):
GET https://<domain-app>.api.columbia.edu/directory/v1/people - Retrieves a list of People from directory
GET https://<domain-app>.api.columbia.edu/v1/people/jd1234 - Retrieves John Doe's information based on UNI
POST https://<domain-app>.api.columbia.edu/v1/people - Creates a new employee using JSON payload
PUT https://<domain-app>.api.columbia.edu/v1/people/jd1234 - Updates John Doe's info in Directory
PATCH https://<domain-app>.api.columbia.edu/v1/people/jd1234 - Partially updates the John Doe's info in Directory
DELETE https://<domain-app>.api.columbia.edu/v1/people/jd1234 - Deletes John Doe's record in directory
Search GET https://<domain-app>.api.columbia.edu/v1/people?filter=[{“surname”,”like”,”Smith*”}]&sortBy=“surname”&sortOrder=“asc” - Search for people with surnames starting with Smith
Application Metadata Resources (DRAFT)
The following metadata and special resources should be available for every application:
GET https://<domain-app>.api.columbia.edu/{v}/openapi: An endpoint used to obtain the OpenAPI (OAS) 3.0 schema for this app.
Enterprise Metadata Resources (DRAFT)
A top-level Columbia University enterprise metadata directory will contain the list of available types (schemas) and resource types (item, collection, etc.) that are used by the various applications:
GET https://api.columbia.edu/schemas - JSON-Schema.org schemas.
- GET https://api.columbia.edu/scopes - definitions of available custom OAuth2 scopes
SSL Everywhere
Always use SSL, no exceptions. This is enforced at the f5 load balancer (https://<domain-app>.api.columbia.edu) which terminates SSL sessions.
Documentation
Always provide good API documentation. The document should be easy to find and publicly accessible to the consumers. Documentation should consist of:
A OpenAPI Specification (OAS) 3.0 definition that documents the API in general, the specific resources and the methods that operate on them, Meanings of OAuth 2.0 scopes, etc.
An example walkthrough of using the API, perhaps using a Jupyter Notebook.
JSON only responses
At Columbia JSON responses are preferred responses from API
Pagination
For APIs which returns large number of records, links should be used for pagination into the previous and next pages. For example:
{
"links": {
"first": "https://app.api.columbia.edu/v1/things/?page[number]=1",
"last": "https://app.api.columbia.edu/v1/things/?page[number]=448",
"next": "https://app.api.columbia.edu/v1/things/?page[number]=2",
"prev": null
},
"data": [ ... ]
}
Rate Limiting
It is good practice to add rate limiting to an API, use HTTP status code 429 Too Many Requests
Some good example of rate limiting can be found here
Authentication
REST APIs are stateless. API request authentication shouldn't depend cookies and sessions. Instead each request should use some sort of token for authorization.
Tokens can be associated with two types of resources i.e. Applications and/or users of applications
oAuth2 will be used in Columbia to provide the secure token for API authorization.
oAuth2 Protocol
Caching
Use HTTP's inbuilt capability for caching, following can be used as part inbound request header
ETag : An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL. If the resource representation at that URL ever changes, a new and different ETag is assigned. Used in this manner ETags are similar to fingerprints, and they can be quickly compared to determine whether two representations of a resource are the same.
In typical usage, when a URL is retrieved the web server will return the resource's current representation along with its corresponding ETag value, which is placed in an HTTP response header “ETag” field:
ETag: "686897696a7c876b7e"
The client may then decide to cache the representation, along with its ETag. Later, if the client wants to retrieve the same URL again, it will send its previously saved copy of the ETag along with the request in a “If-None-Match” field.
If-None-Match: "686897696a7c876b7e"
On this subsequent request, the server may now compare the client's ETag with the ETag for the current version of the resource. If the ETag values match, meaning that the resource has not changed, then the server may send back a very short response with a HTTP 304 Not Modified status. The 304 status tells the client that its cached version is still good and that it should use that.
Last-Modified : This basically works like to ETag, except that it uses timestamps. The response header Last-Modified contains a timestamp, which is validated against If-Modified-Since.
Errors
An API should provide a useful error message in a known consumable format like JSON. The error message should contain its own set of fields, for example:
{
"errors": [
{
"detail": "Authentication credentials were not provided.",
"status": "401",
"source": {
"pointer": "/data"
},
"code": "not_authenticated"
}
]
}
N.B. See JSON API error objects.
HTTP Codes
API should return HTTP defined status codes, which helps consumer of API route their responses accordingly, see below for the list (HTTP Status code)
200 OK: Successful response. (Use for POST that doesn't result in a creation)
201 Created: Response to POST that results in a creation
400 Bad Request: The request that is malformed, such as if the body doesn't parse
401 Unauthorized: When no or invalid authentication details are provided.
403 Forbidden: When authentication succeeded but authenticated user doesn't have access to the resource.
404 Not Found: When an non-existent resource is requested
405 Method Not Allowed: When an HTTP method is being requested that isn't allowed for the authenticated user
410 Gone: Indicates that the resource at this end point is no longer available, deprecated API
415 Unsupported Media Type: If incorrect content type was provided as part of the request
422 Unprocessable Entity: Used for validation errors
429 Too Many Requests: When a request is rejected due to rate limiting