RESTful API Design
Table of Contents
What is REST? #
REST is an acronym for REpresentational State Transfer and it is an architectural style that can be followed to implement web applications. REST came as part of early web architecture and HTTP Protocol.
What is RESTful API #
When a web application follows all the REST constraints to implement application program interface (API) that works over internet protocol like HTTP/HTTPS to access and use the web resources or services is called RESTful API.
Few of the constraints or principles that must be followed while implementing RESTful API are Client Server, Stateless, Cacheable, Uniform Interface, Layered System.
In this article we will not go into the details of constraints of REST but rather focus on designing API.
Client-Server Architecture #
When a client needs to access a specific resource at the server it crafts a HTTP Request and sends it to server and the server replies with a HTTP Response comprising of requested data or HTTP Error Response.
HTTP Request #
An HTTP request is made by a client, to a named host, which is located on a server. The aim of the request is to access a resource on the server. It is comprising of Request Line (HTTP Method, Resource Location and HTTP version), Headers and optional HTTP body. To make the request, the client uses components of a URL (Uniform Resource Locator), which includes the information needed to access the resource.
The structure of a URL looks like this:
Request line #
For example: GET /api/v1/products HTTP/1.1
is a request to get list of products stored in a database at server, using HTTP/1.1.
So basically the pattern of the request line is <HTTP METHOD> /api/<VERSION>/<TABLE OR COLLECTION NAME> <HTTP VERSION>
Here GET is the HTTP Method. All possible methods are: GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE and CONNECT. Some methods, like HEAD, GET, OPTIONS and TRACE don’t change data on the server. On the other hand, methods like POST, PUT, DELETE and PATCH, create permanent changes.
When it comes to accessing the Database Table
/Document
, Server Application maps the HTTP Method to Data Operations.
HTTP Method | Application Layer | Database |
---|---|---|
POST | maps to | CREATE |
GET | maps to | READ |
PUT | maps to | UPDATE |
DELETE | maps to | DELETE |
Location path component api/
separates all API collection from rest of the website/services and ‘v1/’ specifies that this is the version 1 of the products
API.
In later stages of development if we decide to create a new version of products API, we will simply create a new location named api/v2/products
.
At that time both api/v1/products
and api/v2/products
will be live, so both can be consumed by HTTP clients.
Request headers #
Headers specify more information, either about the sender, the intended recipient, the intended connection settings, what kind of data to send back, and so on. A few examples include:
Host
the internet host and port # of resource being requestedUser-agent
what type of browser is making requestAccept
what media types are acceptable for the response (text/html, etc.)Connection
whether the browser should keep the connection alive, etc.Authorization
credentials to authenticate a user-agent with a server
You can find more request headers here.
Request Body #
The body content of any HTTP message can be referred to as a request body. Message bodies are appropriate for some request methods and inappropriate for others. For example, a request with the POST method, which sends input data to the server, has a message body containing the data. A request with the GET method, which asks the server to send a resource, does not have a message body.
HTTP Response #
When server receives an HTTP request, it validates and process the request and returns requested resource in HTTP format. An HTTP Response is comprising of Status Line, Headers and Message body.
Status line #
Status line consists of HTTP version, status code and reason phrase, also known as status text.
For example: HTTP/1.1 200 OK
A status code of 200 means success. The status codes are classified by number range, with each class of codes having the same basic meaning.
Code | Description |
---|---|
1xx (Informational) | It means the request has been received and the process is continuing. |
2xx (Success) | It means the action was successfully received, understood, and accepted. |
3xx (Redirection) | It means further action must be taken in order to complete the request. |
4xx (Client Error) | It means the request contains incorrect syntax or cannot be fulfilled. |
5xx (Server Error) | It means the server failed to fulfill an apparently valid request. |
All status codes can be found here.
Response Headers #
Like HTTP requests, responses can also (optionally) include header fields, an empty line, and an optional message body.
Example response headers include:
Content-length
how many bytes of data that client should expectServer
info about server answering requestContent-type
format of data being returnedDate
timestamp of responseLast-modified
date doc was last changedIf-Modified-Since
can skip sending payload back if newer than date
You can find more response headers here.
Response Body #
Respond data is the requested data presented in a format that is specified by Content-type
header.
Constructing Request for various use-cases #
Let’s see how a client can access the web resource named product which is stored in the database table named products
.
All the APIs will use JSON for the Request/Response body data format, so the Content-Type
Header will be set to Media Type application/json
.
Id | Name | Category | Manufacturer | Price | Color | Stock |
---|---|---|---|---|---|---|
1 | Macbook Pro | Laptop | Apple | 800 | Grey | 3 |
2 | Galaxy Book | Laptop | Samsung | 1200 | Black | 45 |
3 | OnePlus 9 Pro | Mobile | OnePlus | 400 | Blue | 4 |
4 | iPhone 12 | Mobile | Apple | 500 | Black | 8 |
Create a product #
To create a product we construct a request with HTTP Method POST
as follows:
HTTP Request
POST api/v1/products
{
"name": "iPhone 12",
"category": "Mobile",
"manufacturer": "Apple",
"price": 500,
"color": "White",
"stock": 8
}
HTTP Response
200 OK
{
"id": 4
}
If server can’t store the product in the database for any reason related to server then server can send a error response like following.
500 Internal Server Error
{
"code": "1002", // code is just a application specific error code.
"reason" "..."
}
Update a product #
HTTP Request
PUT api/v1/products/4
{
"color": "Black",
}
HTTP Response
200 OK
Get all products #
HTTP Request
GET api/v1/products
HTTP Response
200 OK
[
{
"id": 4,
"name": "iPhone 12",
"category": "Mobile",
"manufacturer": "Apple",
"price": 500,
"color": "Black",
"stock": 8
}
]
Get a product with id
#
HTTP Request
GET api/v1/products/4
HTTP Response
200 OK
{
"id": 4,
"name": "iPhone 12",
"category": "Mobile",
"manufacturer": "Apple",
"price": 500,
"color": "Black",
"stock": 8
}
Filter products #
Filters are composed of following 3 parts.
- The property name
- The operator such as
equal to (=)
,not equal to (not)
,less than (lt)
,less than or equal to (lte)
,greater than (gt)
,regex
- The filter value
But URL query parameters has only two parts key=value
. So we need to append the operator in the key part or value part of the query parameter.
For Example:
- attached with value part
GET api/v1/products?price=gt:400&price=lt:800
- attached with key part
GET api/v1/products?price[gt]=400&price[lt]=800
More advanced and comprehensive filter mechanism can be implemented using JSON like filter query like following.
GET api/v1/products?filter={$and:[{price:{$gt:400}},{price:{$lt:800}}]}
Sort products #
Sorting enables you to return the list of elements in ascending or descending order by single or multiple fields.
For Example, following HTTP request will return list of products sorted by price in ascending order and stock is descending order.
GET api/v1/products?sort=asc:price&sort=desc:stock
Paginate products #
Pagination is a way to limit the size of response when you don’t want to retrieve all the results at once or the result is too big to be retrieved at once.
GET api/v1/products?page=2&items_per_page=10
or
GET api/v1/products?offset=5&limit=10
or
GET api/v1/products?skip=5&limit=10