REST APIs demystified

Learn about the well-known though often misunderstood concepts behind REST principles and RESTful APIs.

REST APIs demystified
Lauren Pelesky / Unsplash

Working as a web developer, the day necessarily came when you faced the need to exchange data between browser and server, and heard about REST(ful) APIs.

REST stands for Representational state transfer and is a data-exchange architectural style strongly based on HTTP protocol.

It was originated in 2000 by Roy Fielding (co-author of HTTP specification and co-founder of Apache HTTP server) in a dissertation, which is still considered as of today like a major contribution to Web standards.

It's worth mentioning that REST is a collection of principles not published by any means as a standard or specification. This fact is probably the top reason why junior developers often struggle to efficiently grasps its concepts.

A RESTful API — or more commonly a REST API — is an API trying to follow REST principles as much as possible.

Philosophy / Concepts

REST defines data exchanges between 2 entities as an "interop". An interface allowing the parties to follow an established contract in a conventional thus predictable way.

The terminology used here involves :

  • Resources : similar to objects in OOP or tables in relational database : a group of data properties related to each other. Example : an Animal
  • Operations : similar to functions or methods in coding. Example : Creating an Animal
  • Ids : resource identifiers, can be of any type but has to be literal, and unique.

Key principles

Often when reading about REST, one land on [the well-known contraints](https://restfulapi.net/rest-architectural-constraints/) of REST architecture.

But I find those concepts hard to grasp, and way broader than API concepts. To keep things focused on our topic, I'll try here another approach, more focused on the contract between client and server.

Resource-identifiers URIs

In REST, the URI has to be meaningful, structured, and representing the resource we want to manipulate. In other words, the rule is to put the resource name in the URI.

Best conventions suggest resource names to be in kebab-case (hyphen separated lowercase), in their plural form.

One level down

The two basic shapes are single resources (identified by their Id) and resources lists :

Route Resource
/animals the list of Animals
/animals/23 the Animal with id 23

Two levels down

An extended shape can express nested resources. Similarly to database foreign keys :

Route Resource
/animals/23/toys the Toys of Animal 23
/animals/23/friends friends list (Animals) of Animal 23 [1]
/animals/23/owner the owner (Human) of Animal 23 [2]

  1. For this special case, the Animal resource is referencing itself. (just like self-referencing tables in database). A common pattern is to derogate from "express resources by names" and rather express linked ones by link name.
    (here, friends instead of animals) ↩︎

  2. For cases like this where the nested ressource is a single one (just like 1:1 relationship in database), we also derogate from the "plurals" rule.
    (here, owner instead of owners) ↩︎

More levels down ?

I personally like to limit "nested-patterns" to 2 levels down; because further digging is a nonsense to me.

Indeed, imagine such a resource identifier :

Route Resource
/animals/23/friends/21/toys The toys of Animal{23} friend Animal{21}

Coding such a call requires you to know the end Animal id (21)...
so why not simply requesting it's toys ?

Route Resource
/animals/21/toys The toys of Animal{21}

You can safely extrapolate that for any use-case : this kinda "resets" at any level. I personally never encountered a blocking use-case.
— But I'd love hearing in comments if you already did !

An exception ?

A decent exception use-case from what I just said are rare cases when you want to manipulate the association itself between 2 entities. In that case you need the 2 ids written down.

Route Resource
/animal/23/owner/1 the association (ownership relation) between Animal{23} and Human{1}
/animal/23/friends/25 the association (friendship relation) between Animal{23} and Animal{25}

(We'll cover this case in a second with examples)

HTTP methods

Meaningfulness doesn't stops at resources, but also expands to operations. As such, in REST they are expressed using HTTP method verbs.

  • GET is used to find or list
  • POST is used to create (or sometimes "associate", see below)
  • PATCH is used to update (partially)
  • PUT is used to update (totally replace)
  • DELETE is used to delete (or sometimes "dissociate", see below)

But with years, a common derogation arised

  • PATCH is nowadays rarely used
  • PUT is commonly used for partially updating instead

Let's review common use-cases by operation

GET use-cases :

Route Effect
GET /animals List Animals
GET /animals/23 Find Animal{23}
GET /animals/23/toys List Animal{23} Toys
GET /animals/23/owner Find Animal{23} owner (Human)

POST use-cases

Route Effect
POST /animals Create an Animal
POST /animals/23/toys Create an Toy and add it to Animal{23} Toys list
POST /animals/23/toys/10 Add Toy{10} to Animal{23} Toys list
POST /animals/23/owner Create a Human and set it as Animal[23] owner
POST /animals/23/owner/69 Set Human{69} as Animal{23} owner

PUT use-cases

Route Effect
PUT /animals/23 Modify Animal{23}
PUT /animals/23/owner Modify Animal{23} owner (Human)
PUT /animals/23/owner/70 Replace Animal{23} owner with Human{70}

DELETE use-cases

Route Effect
DELETE /animals/23 Delete Animal{23}
DELETE /animals/23/toys/10 Dissociate Toy{10} from Animal{23}
DELETE /animals/23/owner Dissociate owner Human from Animal{23}

Single-resource endpoints

As you already guessed, an important root principle of REST is that endpoints should manipulate a single given resource type. It means either a list of records of this resource type, or a single one record.

This applies to the body (both Response and Request) of operations :

GET /animals/23

{
  "id": 23,
  "breed": "dog",
  "name": "Skippy"
}
Response

This returns the requested Animal object.

GET /animals

[
  { "id": 23, "breed": "dog", "name": "Skippy" },
  { "id": 24, "breed": "cat", "name": "Peanut" }
]
Response

This returns the Animal list.

POST /animals

{
  "breed": "tiger",
  "name": "Sherkan"
}
Request
{
  "id": 25,
  "breed": "tiger",
  "name": "Sherkan"
}
Response

This takes an Animal — often without id if auto-generated
and returns the created Animal object.

PUT /animals/24

{
  "name": "Hazelnut"
}
Request
{
  "id": 24,
  "breed": "cat",
  "name": "Hazelnut"
}
Response

This takes an partial Animal — only properties to update
and returns the modified Animal object.

DELETE /animals/23

{
  "id": 23,
  "breed": "dog",
  "name": "Skippy"
}
Response

Returns the deleted Animal object.


(Same logic goes for linked objects — that I exclude here for simplicity.)

HTTP Status codes

Again with meaningfulness in mind, responses should include standard and semantic HTTP status codes. This applies to both success and errors.

Success

  • 201 - created : for every creation success (POST)
  • 200 - ok : for every other success (GET, PUT, DELETE)

Errors

  • 404 - not found : for inexisting objects (mainly targeted by resource/{id} routes, but also single linked objects resource/{id}/linked-object)
  • 400 - bad request : for invalid bodies in creations or updates (POST, PUT)
  • 409 - conflict
    • for already existing objects in rare cases when creating a resource directly with a forced id in body or URL (POST /resource/:id)
    • for already occupied association 1:1 when trying to created one (POST /resource/:id/linked-resource/:id)
  • 410 - gone : means "deleted", for rare cases of requesting deleted object on servers tracking deleted objects (soft delete mecanism)
  • 401 - unauthorized : for reserved resource when trying to access them without authentication
  • 403 - forbidden : for reserved resource when trying to access them with unsufficient permission
  • 500 - internal server error : for internal errors not to be shown to a user, or unknown errors
  • 503 - service unavailable : for cases when the route is temporarily unavailable (external resource down, overload, etc.)

Wrapping up

As a sumup, applying the "Don't Reinvent the Wheel" principle, I propose you further readings that inspired me white writing this post.

RESTful Services Quick Tips
RESTful API or Web Service Quick Tips. Want to learn the high points of creating a REST API? Here’s the Web Services in 60-seconds version.
REST APIs cheatsheet
REST Architectural Constraints
REST stands for Representational State Transfer, a term coined by Roy Fielding in 2000. It is an architecture style for designing loosely coupled applications over the network, that is often used in the development of web services. REST does not enforce any rule regarding how it should be implemente…
REST architectural constraints
What are HTTP Status Codes? List Of Important Status Codes
An HTTP status code is a server response to a browser’s request. When you visit a website, your browser sends a request to the site’s server, and the server then responds to the browser’s request with a three-digit code: the HTTP status code. Common codes are: 1xx Informational requests, 2xx…
Common HTTP status codes
Architectural Styles and the Design of Network-based Software Architectures
Roy Fielding's dissertation