The name is API... REST API

profile picture

Jeroen Tech Lead

7 Dec 2017  ·  11 min read


Do you need to use APIs for the first time, but unsure where to start? Buckle up for our complete beginner’s guide to REST APIs.

Let’s start with a refresher: what exactly are APIs? Short for application programming interface, an API is a communication tool between different pieces of software or applications.

And today, there are more APIs available than you might think. It’s important to make the distinction between internal and external APIs.

  • Internal: APIs within your immediate reach, for example the API of a specific language, or one a library exposes.
  • External: APIs that live outside your environment and that you need to connect to.

You can further categorise them – web service APIs, library APIs, language APIs – but the crucial thing to remember is that there is no standard, only a set of conventions and constraints.

In this post however, we are going to talk about external APIs, more specifically about Web Service APIs.

Types of web service APIs

Before we go further into specifics, it’s important to be aware of the three most used and best known types of web service APis: SOAP, REST, and GraphQL.

SOAP APIs use a WSDL (Web service description/definition language) to define structure and functionality. They are function-driven and use a “do this” principle: they will always execute a function x for data y.

They are also expensive: there’s no caching available, so each request needs to be handled by servers. And finally, they don’t “do” stateless: you always need to retrieve or remember the state of the data.

REST APIs’ main purpose is to create uniformity. They must be stateless, meaning you do not need to know or remember the state of the data. They use a “become this” principle: put data x for resource y.

Their consistency and uniformity makes it easy to cache, and they aim to be fast and scalable.

Finally, GraphQL APIs are the new kid on the block. They became known through Facebook and their main selling point is that they give a lot of freedom to the client that uses them. Their main principle is that the client defines the data and structure. However, this amount of freedom has a cost: it takes a lot of calculation power, and there’s no HTTP Caching possible on the server.

Our focus for the rest of this post will be REST APIs.

REST & HTTP

As we said, a REST API’s main focus is uniformity. It achieves this by using an interface, which is almost always HTTP.

HTTP or HyperText Transfer Protocol is the protocol used to talk between Hypermedia. We all know it because, of course, it’s used by the internet. It’s important to know that it always consists of a request that will return a response, meaning there are different components responsible for requests and responses. And REST APIs will make full use of everything HTTP has to offer.

Let’s look at each component and how we use each one in a REST API.

1. URI

The URI component defines the resource you want to talk to. For REST, try to think in a data/object structure: a collection of data to a single item. An example:

/users
/users/123
/users/123/addresses
/users/123/addresses/123

You can extend the resource with GET parameters for filtering, paging or searching:

/users?search=john
/users?p=1
/users?email=hello@world.com

2. Verbs

The main verbs are GET, POST, PUT, PATCH, DELETE. There are more than these – COPY and LINK, for instance – but these are rarely used. A verb defines the action of the request, and they can be categorized in safe, idempotent and normal operations.

  • Safe: Does not modify the resource.
  • Idempotent: Will change the resource, but it can be called as many times as you want – it will not give you a different response.
  • Normal: Not safe or idempotent.

A little more background on the most-used verbs in REST:

  • GET: Safe operation. Used to get a certain resource.
  • POST: Normal operation. Used to create a new resource.
  • PUT: Idempotent operation. Updates an existing resource.
  • DELETE: Idempotent operations. Removes a resource.

A bit of a special case is the PATCH verb. PATCH is used if you only want to update a part of the resource. You could call it a Partial PUT. To make this possible, you can implement a custom workflow or have a certain workflow that gives lots of flexibility to the client, but not to the server (because the client decides the fields he wants to update). This causes it to be less RESTful, and for that reason, the use of PATCH will always be a source of disagreement.

3. Body

The body is the actual content of the request or response.

For REST, you will always use the Raw type for the request. The data structure is mostly JSON, but this isn’t mandatory. You can use whatever you want: XML, Google’s Protobuf, Apache Avro, Thrift,… Just remember to always be consistent in the structure. To achieve this consistency in the way you send & receive data from your API, it’s best to define a body structure in REST:

{
    "data": [
        {
            "id": 1,
            "name": "Sinter",
            "facebook_uid": 1254861
        },
        {
            "id": 2,
            "name": "Klaas",
            "facebook_uid": 1254861
        }
    ],
    "_links": {
    },
    "_meta": {
    }
}

4. Headers

We already have a body, now we need a head ;-) Headers are functional parameters for the request and response. A header can and will manipulate the request and response, by changing the language, defining the response data type, etc.

You can use existing headers (Cookies, Authorization, Accept-Language, …) or create your own. Custom headers are mostly prefixed with an X (X-Client-Id, My-Custom-Header, …).

Basically, you use headers to define parts of the request that are not specific to the resource, like defining language, sending access keys, or requesting data in a certain content type. The header will also give the client more information about the response: its status code, caching information…

5. Status codes

The state codes tell you (the client) what the server thinks about the request you made. The most relevant ones are:

  • 201 = Created
  • 200 = OK
  • 404 = Not found
  • 400 = Bad Request
  • 204 = No content
  • 422 = Unprocessable Entity
  • 418 = I’m a teapot

Now, REST APIs use these status codes wisely! But if you do want to have some fun with http codes, there are a lot of sites that make images based on codes – https://http.cat and https://httpstatusdogs.com are popular options.

Creating a request

Now let’s combine these components into a real request.

First step: verbs and endpoints

In a REST API, we always start by thinking about the verbs and the endpoints.

  • I can GET/POST on /users/123
  • What can I do with /users/123: GET/POST

In each request, we can see the combination of all components: the verb and the endpoint, some headers and a request body if needed. The response will always give you a status code, some response headers and a response body.

Let’s dive into a few examples.

Firstly, a GET request. A GET request will return a 200 if it found the resource and a 404 if not. But remember, a GET on a collection can’t return a 404: it can be an empty collection but not a “not found” collection.

Request:

GET /api/users/183/coupons HTTP/1.1
Host: https://domain.com
Content-Type: application/json
Authorization: Bearer OGRlNTI4NDFiYzRlZWR2YzZGRkMjEzNzkzOWYzMDkwMzU5MDlzZhZDM4NGVmZWU2ZjI3MjA3YWQ4NTRlMw
Accept-Language: nl
X-Custom-Header: aspecificvalue
Cache-Control: no-cache

Response:

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-cache, private

{
    "data": [
        {
            "id":55,
            "url":"https:\/\/domain.com\/p\/4fbn7t\/88",
            "claimed":false,
            "created_at":"2017-12-05T12:49:45+0000"
        },
        {
            "id":56,
            "url":"https:\/\/domain.com\/p\/4fbn7t\/88",
            "claimed":false,
            "created_at":"2017-12-05T12:49:47+0000"
        },
        {
            "id":57,
            "url":"https:\/\/domain.com\/p\/4fbn7t\/88",
            "claimed":false,
            "created_at":"2017-12-05T12:50:17+0000"
        },
        {
            "id":58,
            "url":"https:\/\/domain.com\/p\/4fbn7t\/88",
            "claimed":false,
            "created_at":"2017-12-05T12:50:18+0000"
        }
    ],
    "_links": {},
    "_meta": []
}

Secondly a POST request. If a POST is successful, it will return a 201 Created, if not, you will need to return an error status code. Mostly this will be 400 Bad Request or 422 Unprocessable entity.

Normally, a POST must return an empty body, because the client knows what he posted and a correct URI structure must make it possible for the client to know where to find the resource. However, we always return the created object, because clients are sometimes lazy and you save an extra request for the created object.

Request:

POST /api/users/183/coupons HTTP/1.1
Host: https://domain.com
Content-Type: application/json
Authorization: Bearer OGRlNTI4NDFiYzRlZWR2YzZGRkMjEzNzkzOWYzMDkwMzU5MDlzZhZDM4NGVmZWU2ZjI3MjA3YWQ4NTRlMw
Accept-Language: nl
X-Custom-Header: aspecificvalue
Cache-Control: no-cache

{
    "url": "https:\/\/domain.com\/p\/4fbn7t\/88945",
    "claimed": true
}

Response:

HTTP/1.1 201 Created
Content-Type: application/json
Cache-Control: no-cache, private

{
    "id":59,
    "url": "https:\/\/domain.com\/p\/4fbn7t\/88945",
    "claimed": true
    "created_at":"2017-12-05T12:49:45+0000"
}

Thirdly, a PUT request. This one look almost the same as a POST. A PUT will normally return an empty body like a POST, but for the same reasons, we return the object. A PUT will return a 200 OK if successful, or an error status like 400 or 422 when something is wrong.

Request:

PUT /api/users/183/coupons/59 HTTP/1.1
Host: https://domain.com
Content-Type: application/json
Authorization: Bearer OGRlNTI4NDFiYzRlZWR2YzZGRkMjEzNzkzOWYzMDkwMzU5MDlzZhZDM4NGVmZWU2ZjI3MjA3YWQ4NTRlMw
Accept-Language: nl
X-Custom-Header: aspecificvalue
Cache-Control: no-cache

{
    "url": "https:\/\/domain.com\/p\/4fbn7t\/845",
    "claimed": false
}

Response:

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-cache, private

{
    "id":59,
    "url": "https:\/\/domain.com\/p\/4fbn7t\/845",
    "claimed": false
    "created_at":"2017-12-05T12:49:45+0000"
}

And lastly, a DELETE request. A delete doesn’t need a body, it just needs the resource. If successful, you will get a 204 No Content, because the content has just been deleted. If the resource is already deleted, you will return a 404 telling the client that you don’t know it.

Request:

DELETE /api/users/183/coupons/59 HTTP/1.1
Host: https://domain.com
Content-Type: application/json
Authorization: Bearer OGRlNTI4NDFiYzRlZWR2YzZGRkMjEzNzkzOWYzMDkwMzU5MDlzZhZDM4NGVmZWU2ZjI3MjA3YWQ4NTRlMw
Accept-Language: nl
X-Custom-Header: aspecificvalue
Cache-Control: no-cache

Response:

HTTP/1.1 204 No Content
Content-Type: application/json
Cache-Control: no-cache, private

[]

How to make your API the BEST!

Now that you know how to use the HTTP interface, you have already come a long way. Lots of great minds have extended the possible functionality of your API by creating tools, libraries and standards. Here are some of the most needed and interesting ones.

First, there’s HATEOAS. It’s short for Hypermedia As The Engine Of Application State, and is Richardson Maturity Level 3. Hateoas is made to create discoverability, like links on a website. It will show you the connection between resources. This makes it perfect for chatty applications. It does generate lots of traffic, because every resource is called separately, but this won’t cause any issues when you use HTTP/2. It has the added plus of making caching easier.

Next, look into Authentication. REST is Stateless, which means you need stateless authentication – so you can’t use the usual sessions or cookies to authenticate your users! Instead, use the Authorization header to send tokens to identify yourself.

There are several authentication methods available. These are the most used:

  • Basic authentication: encoded username and password inside the Authorization headers.
  • oAuth: request authentication tokens based on credentials that you send to a certain endpoint. Authenticate to the server with those token.
  • JWT Tokens: partially the same as oAuth. Only here, the token is signed and can contain much more information about the user or more application data. It’s also useful for transferring secure data.

And thirdly, there’s Versioning. Versioning prevents the client from breaking when you change the API. There’s two different ways to do this: either use a header (custom or Accept Header), or make use of the URI (/api/v2/users/123).

As a general rule though, it’s better to not implement versioning straight away; instead, start adding it when you get to the second version. Keep in mind that you want to work backwards compatible.

Let’s look at some edge cases

As always, in REST APIs, you can’t always get what you want.

Typically, we recommend building your API the way we detailed throughout this post. But sometimes, things just don’t work out. You might run into URIs that can’t fit into the collection/singular structure. In some cases, your bodies can’t be consistent for the same resource. In these cases, you’ll have to adapt!

As a general rule, the best way to deal with edge cases is by always trying to find structure and consistency.

You can do this by namespacing endpoints:

/login              /auth/login
/forgot-password    /auth/forgot-password

/faqcategories      /faq/categories
/faqquestions       /faq/questions

Or by defining a body structure. First create a base structure; then validate content based on the resource of identifier in the body.

/api/guests/12

{
    "id": 12,
    "name": "Hans Schoen",
    ...
    "is_invitee": false,
    "invitees": [
        {
            "id": 13,
            "name": "Brad Wursten"
        },
        {
            "id": 14,
            "name": "Donald Duk"
        }
    ]
}

/api/guests/14

{
    "id": 14,
    "name": "Donald Duk",
    ...
    "is_invitee": true,
    "inviter": 12
}

And finally, let’s look at some Tools!

Like every cool thing, REST APIs have a lot of cool tools. Let’s show you some of them!

First, there’s Postman. This application helps you create, test, and document APIs. It has some cool features like team collaboration, testing, mock server ,… Postman is one of our default tools for any API, especially as a shared documentation and test tool for our devs.

Next, you can use RAML and/or Open API V3. These are tools that help you design the API. They each give you a set of tools to write down what your API can, must and will do. You can then add examples, json-schemas, etc. to it and create a nice HTML. And you can import these files in Postman to create your collection!

And finally, we’d like to present: the internet. Okay, this is not really a tool, but APIs have been around long enough to have generated a wide variety of great resources, articles, and tutorials – to help you create the best API ever. A few of our favourites:

And just like that, you know how to build REST APIs that everyone will love to connect with ;)