Looks quite RESTful, doesn't it?

REST API

ยท

4 min read

Looks quite RESTful, doesn't it?

My aim with this post is to explain building a plain REST API without using any fancy framework like Express.

Enjoy! โœŒ๏ธ


Prerequisites

The web follows a client-server model. It comprises two kinds of machines (or programs), namely, the service provider (server) and the service requester (client). The interaction between a client and a server is dictated by a protocol, a set of rules, called HTTP.

Some key points about the model:

  • A server listens for (awaits) requests from clients. When a client sends a request, the server processes it and sends an appropriate response back to the client. This is called a request-response cycle.

  • There cannot be multiple responses to a single request.

  • A response always comes with a status code that indicates whether the request has been fulfilled or not.

Bare-bones

Let's write a basic HTTP server program using Node.js.

First things first, import the built-in http module. The ES6 syntax to do so is:

import http from 'http'

const port = 8080;

http.createServer((req, res) => {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello there');
}).listen(port, () => 
    console.log('Server is running at', port)    
);

Comments

  • This server program "listens" for HTTP requests at port 8080.

  • Whenever a request is made, the callback within the createServer method is invoked.

  • The parameters, req and res, are built-in objects. They have methods and properties to analyze a request and send a response accordingly.

    • res.writeHead sends the status code (200) along with the "Content-Type" specification to the client. This metadata helps the client interpret the response data correctly; in our case, the response is displayed as plain text by a browser.

    • res.end is the function that actually sends the response data, which in our case is the string 'Hello there'.

Client

A client, such as a browser, uses a URI (Uniform Resource Identifier) to request specific data from a server. An example URI is shown below:

N.B.

  • req.url property holds the query string = pathname + query param.

  • To split the query string into more readable parts, we use the url module: url.parse(req.url, true) returns a parsed url object.


How to be RESTful?

An API is basically a set of rules that dictate how different programs interact with each other, whereas REST is an architectural style for building http-based services. So, a RESTful API is an API that follows the principles and constraints enunciated by REST.

๐Ÿ“Œ Key features of RESTful APIs :

  • Resources are exposed through distinct paths (routes or endpoints). Every resource has a unique URL.

  • Standard HTTP methods, such as GET and POST, are leveraged to operate on these resources.

  • Stateless - Once a request-response cycle completes, the server has no idea of the client. If the client initiates another cycle, both the machines need to share their info to each other anew.

  • Resources are represented in standard formats like JSON, so that data can be easily exchanged throughout the web.

BU!LD

We need 3 things, namely, middleware functions, a router, and a router handler.

Router and Middlewares

Middlewares are functions with access to the req and res objects, allowing them to handle a request-response cycle.

Now, let's look at a simple router.

In the above router object, the keys are based on pathname. The purpose of this data structure is to specify which middleware function will be called for a particular pathname and request method. Simply put, it "routes" (directs) the client-request to be processed by the correct middleware.

That being said, let's write a couple of middlewares. For simplicity, I'm focusing on two methods only, but you can have as many routes and methods as you need.

GET

Suppose you have some JSON data in your file system that needs to be delivered as a response whenever a client hits the /user/data endpoint using the GET method. The corresponding middleware is shown below:

At first, import { readFile } from 'fs/promises' โš ๏ธ

POST

Say we want our API to allow a client to send some JSON payload to be added to the data in our file system only if it hits the endpoint /user/register with the POST method. The corresponding middleware is shown below:

At first, ensure you have import { readFile, writeFile } from 'fs/promises'.

N.B. In both cases, any server-side error is handled by the catch block, and an error message is sent to the client with status code 500.

Finishing touch

The last piece of the puzzle is a component that analyzes a client's request, looks through the router object, and, if possible, triggers the appropriate middleware; otherwise, a 'Not Found' message is sent to the client.

That component is called the "Router Handler".

The if-block in the above code is the most crucial element because it checks whether the pathname and the request method exist in the router object. If yes, the corresponding middleware is invoked; otherwise, a 404 status code is sent with a JSON error message.

Use the above code block as the body of the callback within the http.createServer() method and...

WE DID IT! ๐Ÿฅณ


Grateful for your time!

See ya soon ๐Ÿ˜‰

ย