RESTful API Design Guidelines

This document covers the guidelines for the designing RESTful style APIs. It represents best practices gleaned from studying industry standard APIs.  The intended audience of this document is someone planning on exposing an existing service or a newly implemented service via RESTful API. 

This document references the following subject areas that will be involved in implementing a RESTful API.   

  1. HTTP Methods
  2. Media Types
  3. URI Templates
  4. Resource Modelling
  5. Data Representation

HTTP Methods

Advantages of uniform interfaces are familiarity, interoperability, and scalability. 

GET – Retrieve a resource

  • It is used to query the server.
  • It is both an idempotent and safe operation. There should not be any side-effects, other than possible meta-data (e.g. last accessed timestamp)
  • The idempotent property enables various caching strategies to be leveraged by browsers and proxy servers.

PUT – Update a resource.

  • Updates the representation of an entity. The updated representation is passed in the message body.
  • The semantic of PUT is modeled as an insert or update.
  • It is also idempotent.

DELETE – Delete a resource

  • It removes a given resource.
  • It is also idempotent.

POST – Create a new resource

  • Creates a new entity.
  • Post is both a non-idempotent and an unsafe operation.

HEAD

  • This is similar to GET except it returns a response code only.  This is generally not needed for standard REST calls.

OPTIONS

  • This allows the requester to retrieve the capabilities of a server without any effect on a resource.  This is generally not needed for standard REST calls except for implementing preflight requests.

Safety and Idempotency

Method

Safe (no side effects)

Idempotent (request can be repeated)Description
GETYesYesRead a resource
PUTNoYesUpdate a resource
DELETENoYesDelete a resource
POSTNoNoCreate a resource


Specific Items and Collections

MethodSpecific ItemEntire Collection
GET200 (OK), 404 (Not Found)200 (OK)
PUT200 (OK), 204 (No Content), 404 (Not Found)404 (Not Found) – not desirable to update every resource in a collection
DELETE200 (Ok), 404 (Not Found)404 (Not Found) – not desirable to delete an entire collection.
POST *404 (Not Found) – should use PUT to update an item.201 (Created)

Note: sometimes POST is used as a catch-call for special cases that defy standard use cases

Other Special Cases

  • POST for long-running requests. Use a “Job” or “Task” to wrap response
  • POST for sensitive data – To ensure that sensitive data (e.g., credentials) is encrypted via https, always use POST so that the sensitive data is in the payload and not in the URL.
  • POST for complicated queries – POST can be used when it would be too cumbersome to use query parameters with GET. For example, supposing the use-case is to search for several customers simultaneously and each customer is represented as First Name, Last Name and ZIP code. Then, the input is an array of objects, which is cumbersome to represent in a flattened way, as you would with query parameters.

Status Codes

Use HTTP status codes and try to map them cleanly to relevant standard-based codes.  Minimally, use the following 10 codes.

  • 200 – OK
  • 201 - Created – always use this for POST
  • 304 - Not Modified
  • 400 - Bad Request
  • 401 – Unauthorized
  • 403 – Forbidden
  • 404 - Not Found
  • 406 - Not Acceptable
  • 415 - Unsupported Media Type
  • 500 - Internal Server Error (Normally this is generated by the server and not the application code). Never expose stack exceptions in a response.

Media Types

Interaction with a service takes place using resource representations of that service. From the client point of view, the Content-Type header specifies data format of the representation for describing input, while the Accept header specifies data format of the representation for describing output. The client and server can negotiate the representation format using these headers to indicate a media type (also known as MIME).

The most commonly used media type is application/json.

Others media types used are:

  • application/x-www-form-urlencoded (for PUT and POST only)
  • application/xml or text/xml
  • application/javascript
  • text/plain
  • text/csv (facilitates “export” capability)

 

Content-Type Header

To describe the request input payload format and/or response payload output, use the Content-Type header.

Minimally, support these media types using these headers:

  • application/json
  • application/x-www-form-urlencoded

If a request call includes a Content-Type header that indicates an unsupported media type, a 415 Unsupported Media Type error should be returned.

 

Accept Header

To request (content negotiation) a specific response format (other than the default), use the Accept header

  • application/json

If a request call includes an Accept header that indicates an unsupported media type, a 406 Not Acceptable error should be returned.


URI Templates

Uniform Resource Identifier (URI) is used to identify a resource. https://tools.ietf.org/html/rfc6570

A URI Template is a compact sequence of characters for describing a range of Uniform Resource Identifiers through variable expansion. This specification defines the URI Template syntax and the process for expanding a URI Template into a URI reference, along with guidelines for the use of URI Templates. URI is standardized as follows:

  • Scheme://host:port/path?queryString#fragment
  • URI = scheme “:” authority “/” path [ “?” query ] [ “#” fragment ]
    • Scheme – is the communication protocol and is one of: http | https.  However we only use https for better security.
    • Authority is the host and port and represents the base location of a given resource.
    • Path is a set of text strings delimited by “/”
    • Query string is list of parameters specified as name=value pairs. Each pair is delimited by the “&” character.
    • Fragment is delimited by “#” and fragment points to a specific location within the document.

NOTE:  the following examples are for the front-end façade URIs.  The exact base path for the front-end URI is determined as a part of the API Publishing task.  Front-end URIs can map to any backend URIs up to the point of the first resource.

Backend URIs that talk directly to a given service provider's (you), service should follow a similar pattern.

  • URI = scheme “:” authority “/” path [ “?” query ] [ “#” fragment ]
  • For the internal services that are developed for NEXEN Access Cloud, the following is the suggested URI pattern:

Resource Modeling 

“The key abstraction of information in REST is a resource. Any information that can be named can be a resource: a document or image, a temporal service (e.g. "today's weather in Los Angeles"), a collection of other resources, a non-virtual object (e.g. a person), and so on. In other words, any concept that might be the target of an author's hypertext reference must fit within the definition of a resource. A resource is a conceptual mapping to a set of entities, not the entity that corresponds to the mapping at any particular point in time.” - Roy Fielding’s dissertation.

Resources can be: 

  • Collections, for example /customers
  • A single entity within a collection, also known as a document, for example "/customers/C12345"

A resource may contain sub-collection resources.  This is desirable to minimize the number of API calls needed to perform a task and thus reduce “chattiness”.  On the other hand, embedding too many sub-collections in a response may make the response payload large and unwieldy. The right balance needs to be considered.  One possible solution to this is to include a query parameter to indicate whether or not the sub-collection should be expanded in the response payload.  For example ?expand=true.

Sometimes there will be functionality in the service that defies being factored into resources.  This is sometimes true of actions, for example, calculate, translate, convert, etc.  For this situation, verbs are used, but they are a rare exception.

Resource Naming 

Resource names should be simple, single-word nouns that are pluralized.

The resource identifier should be in the URI template – not a query string parameter.

Use lower case, with hyphens to separate words, but try to use a single word whenever possible.

Use the resource path hierarchy to signify structure and scoping.

  • Good Example /customers/C12345/orders/O12345/items/I12345
  • Bad Example /customer?customerId=C12345&order=O12345&item=I12345

Where relationships are complex and deeply nested it is not advisable to go deeper than more than 3 relations deep.

  • Do not use a trailing forward slash (/) in URIs
  • Ideally there should be only 2 base URLs per resource. The first URL is for a collection; the second is for a specific element in the collection.
    • /transactions
    • /transactions/<transactionId> 

      ResourcePOSTGETPUTDELETE
      /transactionsCreate a new transactionList transactions(generally not done)(generally not done)
      /transactions/{transid}Create a new transaction with ID (generally not done)Retrieve transidUpdate transidDelete transid
  • Use the HTTP-provided media selection mechanisms.
    • Use the query component of a URI to filter collections or stores:
      • /positions?accountNumber=946432
    • Use the query component of a URI to paginate a collection:
      • /positions?offset=0&limit=10
    • Use path variables for direct ID of a noun:
      • For example, to retrieve an account with ID 65248 : GET /accounts/65248
    • Use parameters for non-direct IDs:
      • /positions?isdn=35346546
    • Do not use underscores (_) in resource names; use hyphens (-) instead. Avoid multi-word resource names altogether wherever possible. Use lowercase letters in URI paths.
    • Do not include file extensions in a URI.

Disallowed URL Characters

Due to cross-site vulnerability reasons, the following characters should not be used in a URL 

BadCSSChars

<,>,',%22 

These are considered as cross-site scripting characters
BadFormChars<,>,&,%22Characters used in Html Form 
BadURLChars//,./,/.,/*,*.,~,\,%00-%1f,%7f-%ff,%25,%25U,%25uThese are considered as bad URL characters


Pagination and Partial Responses

  • Partial response allows developers to ask for exactly just the information they need.
  • It's almost always a bad idea to return every resource in a given database table, assuming that table is not fixed, and contains a potentially large number of rows.
  • There should be a reasonable attempt of consistency in results for collections that change often, given that each query is independent and stateless.
  • Use limit, offset, sortKey, and dir (asc or desc). These names are common, well understood and the concepts are present in database interfaces, and therefore easy for developers to use. The parameters offset and limit are zero-based.
    • The offset parameter defines the number of skipped records and limit defines the number of records to be returned. For example …?offset=100&limit=50 means that we are expecting the service to return records 101-150 from the result set.
    • When the limit parameter is not present, a given service should choose a reasonable default limit.  For some services the default might be unlimited: for example, ...?offset=100 would mean return all records starting with record 101.  For other services the default limit might be something finite such as 100:  for example, ...?offset=100 would mean return records 101-200.
    • When the offset parameter is not present, the default is 0.
    • sortKey specified a field or field list, for which to sort first, before applying offset or limit.
    • dir specifies the sort direction – ascending or descending.  The default sort direction is ascending.

Data Representation Formats

The default resource representation is JSON (Content-Type: application/json). 
Tip: Always use a standard library to stream to JSON to ensure the data is well-formed and is valid JSON (e.g. com.google.gson.Gson)

Formatting 

  • Attribute Name should be lowerCamelCase
  • Attribute Names, if query-able, should match their associated query parameter name exactly; for example

GET /positions?accountNumber=12345 

should return, 
{
   "accountNumber" : 12345,
   …
}

and not:
{
   "acctNo" : 12345,
   …
}

  • Lists are zero-based index. 
  • For the Java value Null, or C value of NULL (0), use the text-based literal value "null". 

Date and Timestamp

  • Dates can use either Coordinated Universal Time (UTC) or an offset from the UTC, thus avoiding issues with time zones and daylight saving times.
  • Dates should be returned in the format with timestamp YYYY-MM-DDTHH:MMZ with zero UTC offset so that conversion to respective time zone is accurate.

Examples:

Zero UTC offset -> 2014-10-17T10:12:30Z

  • Timestamp formats with seconds and milliseconds can be used for specific use cases : YYYY-MM-DDTHH:MM:SSZ / YYYY-MM-DDTHH:MM:SS.ssssssZ
  • Additional details on Date and Timestamp formats are available at W3, ISO 8601 Wiki and ISO 8601

Countries and Territories

  • Countries should be represented as 2 letter code e.g., US, DK, IN ISO_3166-1
  • subdivisions – WS-WA, US-CO, CA-BC, IN-AP. ISO-3166-2

Currencies

  • ISO 4217 specifies three letter currency codes for names of the currencies
  • List of codes are available in alphabet and numeric at Table A.1
  • Examples: 

    EntityCurrencyAlphabetic Code
    AfghanistanAfghaniAFN
    Aland IslandsEuroEUR
    AlbaniaLekALL
    AlgeriaAlgerian DollarDZD
    American SamoaUS DollarUSD

Language Tags

  • Best practices for the language tag are available at W3C \.
  • The new IANA registry is the right place to check sub tags
  • Examples:

    CodeLanguageSubtags
    enEnglishlanguage
    esSpanishlanguage
    fr-CAFrench, used in Canadalanguage+region

Data Validation

  • Validation of incoming payload needs to be performed by the services.
  • Validation may include
    • Minimum, maximum length.
    • Enumeration Facets validation.
    • Special characters.
    • Application-dependent inter-object validation rules.

Tip: Use a standard REST framework to implement the validation functionality such as Spring MVC, Spring BootJersey or REST WS with Jackson.

Handling Representation Errors

Use a consistent form across the application to represent errors

  • Use errorCode as “<Application Mnemonic><code>” e.g., MFD1001
  • A code can belong to a range to classify:
    • 0001-0999: Warning
    • 1000-2000: Invalid request or request parameters
    • 2001-3000: Error
  • A Log file entry should include errorCode, userid and timestamp. Log4j will take care of the timestamp.
  • The error description is a user-friendly message. Due to security restrictions, it is not advisable to include text descriptions for internal errors, as a text description may expose internal implementations could thusly be exploited.


Need help? Email help@finos.org we'll get back to you.

Content on this page is licensed under the CC BY 4.0 license.
Code on this page is licensed under the Apache 2.0 license.