REST API: A friendly introduction
Have you ever wondered how different applications or devices can communicate with each other over the internet? How does your browser know what to display when you visit a website? How does your mobile app fetch data from a server?
The answer is: they use APIs.
An API, or application programming interface, is a set of rules that define how applications or devices can connect to and communicate with each other. An API acts as a bridge between the client (the application or device that requests data) and the server (the application or device that provides data).
There are different types of APIs, such as SOAP, XML-RPC, GraphQL, etc. But one of the most common and popular is REST API.
A REST API is an API that conforms to the design principles of the REST, or representational state transfer architectural style. REST was first defined in 2000 by computer scientist Dr. Roy Fielding in his doctoral dissertation.
REST provides a relatively high level of flexibility and freedom for developers. It can be developed using virtually any programming language and support a variety of data formats. It also enables scalability, performance, and reliability for web services.
The key principles of a REST API include: #
- Resource identification: Each resource should have a unique identifier, such as a URL.
- Resource manipulation: Clients should be able to manipulate resources through a limited set of methods, such as GET, POST, PUT, and DELETE.
- Stateless: The server should not store any client context between requests. Each request should be self-contained and include all the information needed to process the request.
- Representations: Resources can have multiple representations, such as XML, JSON, or HTML.
- Hypermedia: The server should provide links to related resources to enable clients to discover and navigate the API.
What are requests and responses? #
Requests and responses are the messages exchanged between the client, and the server using HTTP (hypertext transfer protocol). HTTP is a standard protocol that defines how messages are formatted and transmitted over the web.
A request is a message sent by the client to the server asking for some data or action. A request consists of:
- A method: The method indicates what kind of action the client wants to perform on the resource. The most common methods are GET (to retrieve data), POST (to create data), PUT (to update data), DELETE (to delete data), PATCH (to partially update data), etc.
- A URI: The URI identifies which resource the client wants to access or manipulate.
- A header: The header contains some additional information about the request, such as content type, authorization, etc.
- A body: The body contains some data that the client wants to send to the server, such as JSON, XML, etc. The body is optional and depends on the method.
Basic request:
GET / HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html
Basic response:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Mon, 27 Feb 2021 17:48:38 GMT
<html>
<head>
<title>Home Page</title>
</head>
<body>
<h1>Welcome to Example.com</h1>
<p>This is the home page of Example.com</p>
</body>
</html>
HTTP Headers #
HTTP headers are additional information that are sent with the request or response. They can provide information about the client, the server, the resource, the body, or other aspects of the message. Headers consist of a case-insensitive name followed by a colon and a value.
For example, these are some common headers:
- Host: The domain name of the server that hosts the resource.
- User-Agent: The name and version of the software that makes the request.
- Accept: The content types that the client can handle.
- Content-Type: The content type of the body of the message.
- Content-Length: The size of the body of the message in bytes.
- Date: The date and time when the message was sent or received.
HTTP headers can be divided into several categories, such as general headers, request headers, response headers, entity headers, etc.
Limitations of HTTP #
HTTP has some limitations, such as:
- It is stateless, which means it does not remember any information about previous requests or responses. This can make it difficult to implement features that require maintaining state, such as authentication, sessions, cookies, etc.
- It is plain text, which means it is not encrypted or compressed by default. This can make it vulnerable to eavesdropping, tampering, or interception by malicious parties. To overcome this limitation, HTTPS (HTTP Secure) can be used to encrypt and secure the communication using SSL/TLS certificates.
- It is verbose, which means it can have a lot of overhead and redundancy in the messages. This can affect the performance and efficiency of the communication. To overcome this limitation, HTTP/2 can be used to improve the speed and performance of HTTP by using binary framing, multiplexing, compression, etc.
HTTP methods and REST API #
Now, when we know how HTTP request is built, we can dive into REST API implementation, with resources identified using URLs, and the HTTP methods used to manipulate those resources. In real-life app, a REST API for managing products might have the following endpoints:
- GET /products: Get a list of all products
- POST /products: Create a new product
- GET /products/{id}: Get a specific product by ID
- PUT /products/{id}: Update a specific product by ID
- DELETE /products/{id}: Delete a specific product by ID
The API would return data in a standardized format, such as JSON or XML, and the client application would use the HTTP methods to manipulate the resources as needed.
GET, POST, PUT, and DELETE client implementation in JavaScript #
In JavaScript, you can use the fetch() API to make HTTP requests to a RESTful API. Here are examples of how to implement GET, PUT, and DELETE requests using fetch().
GET request:
fetch('https://example.com/products')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
This code fetches a list of products from the server and logs the data to the console. The response is parsed as JSON using the json() method, which returns a promise.
POST request:
const newProduct = { name: 'New Product', price: 9.49 };
fetch(`https://example.com/products`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(newProduct)
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
PUT request:
const productId = 123;
const newProduct = { name: 'New Product', price: 9.49 };
fetch(`https://example.com/products/${productId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(newProduct)
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
And DELETE:
const productId = 123;
fetch(`https://example.com/products/${productId}`, {
method: 'DELETE'
})
.then(response => console.log('Product deleted successfully.'))
.catch(error => console.error(error));
GET, POST, PUT, and DELETE server implementation in PHP #
I must say, that following examples are just representation of what's going on when backend receives request and might not work if you try to run it.
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
// Handle GET request
$product_list = getProducts();
// ...
// Return a response
http_response_code(200);
echo json_encode($product_list);
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Handle POST request
$input = file_get_contents('php://input');
parse_str($input, $post_params);
// Call method which adds product to the database
// Return a response
http_response_code(200);
echo json_encode(['message' => 'Product added']);
} elseif ($_SERVER['REQUEST_METHOD'] === 'PUT') {
// Handle PUT request
$input = file_get_contents('php://input');
parse_str($input, $put_params);
// Do something with the PUT data
$id = $put_params['id'];
$name = $put_params['name'];
// ...
// Return a response
http_response_code(200);
echo json_encode(['message' => 'Product updated']);
} elseif ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
// Handle DELETE request
$id = $_REQUEST['id'];
// Do something with the DELETE data
// ...
// Return a response
http_response_code(204);
}
In this example, we first check the value of $_SERVER['REQUEST_METHOD'] to determine the HTTP method of the request. For POST and PUT requests, we read the raw input data from the request body using file_get_contents('php://input'), then parse the input data into an associative array using parse_str(). We can then access the PUT, POST data in the $put_params variable or $post_params variable respectively.
For a DELETE request, we simply read the id parameter from the $_REQUEST superglobal variable.
After processing the request data, we can return a response using http_response_code() to set the HTTP status code and echo to output a response body. In the GET, POST, and PUT example, we return a JSON-encoded message indicating that some action has been done to the resource with a 200 OK status code. In the DELETE example, we return a 204 No Content status code with no response body.