sviluppiamo comunicazione con weLaika Advertising

API Design - Introduction

Tag: api, mobile, rest, webservice, server, platform, json
Matteo Piotto -

We developed several platforms for our customer and also their clients. So we have realized API interfaces for several applications, from JS interface (such as AngularJS or React apps) to mobile apps for iOS and Android (native or hybrid with Ionic for example).

We shared our experience about API design during the RubyDay 2014 (slides and video) but, after two years, there are been several changes and novelty: this is the first post about how to design a good API with tips, examples and technical implementation (especially for Ruby on Rails, the 5th version of which contains an API mode), as well as theoretic considerations and practices.

First of all in our product we break up the application into multiple services, where each of them has a single responsibility: they’re like Lego building blocks.

The API design isn’t simple: is itself a product and the customers are other developers, so we follow a steady design: REST.

Representational State Transfer (REST)

Representational State Transfer (REST) is a term introduced and defined by Roy T. Fielding in 2000 as part of his doctoral dissertation “Architectural Styles and the Design of Network-based Software Architectures”.

Fielding described REST as a software architecture style for distributed systems such as the World Wide Web. REST nowadays is one of the most popular design in web services API and, perhaps, a “fashion statement”.

The are few basic design principles:

  • Client Request - Server Response scenario
  • Use explicitly HTTP methods
  • Be completely stateless
  • Expose directory/hierarchical structure-like URIs
  • It can be cache-able
  • Hard to misuse
  • Uniform interface
    • in the application domain we have only resources like entities
    • we have a unique identifier URL for each resource and collection of resources
    • the resources manipulation is through these representations
    • each message contains enough information to process it (self-descriptive messages)
    • hypermedia as the engine of application state (HATEOAS)
    • Remember what the API is designed for, alias “Treat others the way you wish to be treated”:
      everything must be simple (easy to learn), intuitive (usable, even if without reading the documentation) and consistent (easy to extend)

An API REST call is simple

1
GET https://www.domain.com/api/v1/resources
  • GET: verb / method
  • https://www.domain.com/api/v1: API endpoint
  • resources: resource (what you are trying to access)

The resource in the URI is used to obtain multiple endpoints that are used to represents resources rather than making every call to a single endpoint.


A small tip: the API endpoint should contain also the version because after a while you should support more than one version together (a native app needs longer update cycle and you cannot deploy a native client to all customers at the same time).

Otherwise you can set a generic API endpoint for all versions and the client uses the HTTP Header as versioning strategy using Accept-Version or Accept.

1
2
3
curl -H "Accept-Version:v1" https://www.domain.com/api/resources
curl -H "Accept: application/json; version=1" https://www.domain.com/api/resources
curl -H "Accept:application/vnd.welaika-v1+json" https://www.domain.com/api/resources

A good way is the Stripe approach to API versioning: the URL contains the mayor version (for example https://www.domain.com/api/v1) and the client can request a sub-version with HTTP Headers.

1
curl -H "Accept-Version:v1.6" https://www.domain.com/api/v1/resources

REST verbs

The verb represents the action to be performed on the resource(s) and it corresponds to CRUD operations

  • POST means CREATE
    • Used to create a resource
  • GET means READ
    • Used to request (read) informations, like a resource or a resources collection
  • PUT / PATCH means UPDATE
    • Used to update a resource
  • DELETE means DELETE
    • Used to delete a resource

Thanks to the verbs, you can use concrete naming and not action-verbs to describe your resources.

For example a SOAP / RPC way could be

  • getResources()
  • getResource(1)
  • createResource(...)
  • updateResource(1, ...)
  • deleteResource(1)

By contrast, in a RESTful approach you interact with the resource using HTTP verbs

  • GET /resources/
  • GET /resources/1
  • POST /resources/
  • PUT /resources/1
  • DELETE /resources/1


A small tip: use the plural as resource, for example resources instead of resource.

Relationship

You should be careful with nested relationship: you should use them only if a resource can exists within another one, for example

  • GET /movies/1/actors: list all the actors of the movie with id 1
  • GET /movies/1/actors/1: get the actor with id 1 of the movie with id 1
  • POST /movies/1/actors: create a new actor for the movie with id 1
  • PUT /movies/1/actors/1: update the actor with id 1 of the movie with id 1
  • DELETE /movies/1/actors/1: delete the actor with id 1 of the movie with id 1

Don’t exceed with nested levels of resources!

Response

HTTPS Status Code

Each response must have the right HTTP status code to help the client to decide how to act. They have been established, agreed upon (RFC 7231).

Usually we spend a lot of time picking the right status code trying to be more semantic.

The status codes summary is:

  • 10x: informational
  • 20x: everything is OK. Hurray!!!
  • 30x: go there
  • 4xx: it’s YOUR fault
  • 50x: it’s OUR fault

20x - Successful

204 No Content

  • 200 OK
    • Basic success code. Right for the most cases
    • Especially used on successful first GET requests or PUT/PATCH updated content
  • 201 Created
    • It indicates that the resource has been created
    • Typically replying to PUT and POST requests
    • You should specify URI of new resource in Location header
  • 202 Accepted
    • It indicates that the request has been accepted for processing
    • Typically used as answer to an asynchronous processing call
  • 204 No Content
    • The request succeeded and the server have no intended return. Usually sent after a successful DELETE
    • You should use a 200 code when the result is an empty array/collection because the 204 one it’s intended for input actions
    • Another problem with the 204 use for empty collection is the client needs extra code to check for that case and skip the parsing instead of parse an empty valid array
  • 206 Partial Content
    • The returned resource is incomplete
    • Typically used with paginated resources

4xx - Client Error

401 Unauthorized

  • 400 Bad Request
    • Generic error for a request that cannot be processed due to malformed syntax of the request (not of the resource). You shouldn’t get confused with the 422 HTTP status code
  • 401 Unauthorized
    • I don’t know you: tell me who are you before asking me anything
  • 403 Forbidden
    • Your rights are not enough to access this resource
  • 404 Not Found
    • The resource you are requesting doesn’t exists. You shouldn’t get confused with the 410 HTTP status code.
  • 405 Method Not Allowed
    • The method used in the request is not supported or relevant on this resource.
  • 406 Not Acceptable
    • There is nothing to send that matches the Accept-* headers. For example, you have requested a resource with XML (shame on you!) but there is only JSON available
  • 410 Gone
    • The resource you are requesting does not exist anymore (but in the past it existed)
  • 422 Unprocessable Entity
    • The resource you sent in a PUT/PATCH/POST request can’t be accepted, for example because of a missing required field or a not acceptable value in a field
    • Even if the 422 is part of the WebDAV extension, from several years it has become a reasonable alternative to the 400 HTTP status code: for example it’s used in JSON API.
  • 429 Too Many Requests
    • Chill out, bro. You are stressing me out. Take a break.
    • It has been introduced with RFC 6585
    • In a good post about Best Practices for Designing a Pragmatic RESTful API, the author suggested that the server should include the following HTTP headers:
      X-Rate-Limit-Limit: The number of allowed requests in the current period
      X-Rate-Limit-Remaining: The number of remaining requests in the current period
      X-Rate-Limit-Reset: The number of seconds left in the current period before the reset. Don’t use a timestamp with unnecessary information: the client needs only to know when it can send a new request again.

50x - Server Error

500 Internal Server Error

  • 500 Internal Server Error
    • The request seems right, but a problem occurred on the server. The client cannot do anything about that.
  • 510 Not implemented
    • You make a request to an endpoint that hasn’t been implemented. Yet.

Query and Filter string

Filter and search

You must to use ‘?’ and the name of the attribute to filter resources

1
GET /resources?type=bill&id_user=007&title=qwerty

You may use the “Google way” to perform a search on multiple fields.

1
GET /resources/?q=qwerty+ytrewq

For common queries you can add an alias

1
GET /resources/released

Pagination

You may use a range query parameter. Pagination is mandatory: a default pagination has to be defined, for example

1
https://www.domain.com/api/v1/resources?page=7

The client should be able to define also how many resources should be in each page

1
https://www.domain.com/api/v1/resources?page=7&per-page=15

The response should contain the Link HTTP header and other informations about the collection as the proposed RFC-5988 standard for Web linking.

1
2
3
4
5
6
Link: <https://www.domain.com/api/v1/resources?page=1>; rel="first",
  <https://www.domain.com/api/v1/resources?page=123>; rel="last",
  <https://www.domain.com/api/v1/resources?page=7>; rel="next",
  <https://www.domain.com/api/v1/resources?page=5>; rel="prev"
Total: 4321
Per-Page: 15

Sort

Use the query parameter sort to sort resources.

1
?sort=field1,fieldN

By default resources are sorted in ascending order. Use the minus in a specific attribute to sort resources in descending order

1
?sort=-field1,-fieldN

So you can obtain a complex sorting in ascending and descending order

1
GET /movies?sort=id,-rating,-name,updated_at

Representations

Clients don’t have direct access to resources: they only see their representations (the response of the API) and it can be in XML or in JavaScript Object Notation (JSON).

The format of the representation is managed with content negotiation in a pure RESTful way: in the Accept header of the request, in order of preference. Usually the default format is JSON.

For example Accept: application/json, text/plain

and not /api/v1/resources.json.

In the next post we will see how to design a good JSON representation.

welaika