Introduction

HTTP overview

AdonisJS is primarily a web framework to create applications that respond to HTTP requests. In this guide, we will learn how AdonisJS boots the HTTP server, handles the incoming requests, and the modules available at the HTTP layer.

The HTTP layer

The HTTP layer inside an AdonisJS application consists of the following modules. It is worth mentioning that the AdonisJS HTTP layer is built from scratch and does not use any microframework under the hood.

Router

The router module is responsible for defining the endpoints of your application, which are known as routes. A route should define a handler responsible for handling the request. The handler can be a closure or reference to a controller.

Controllers

Controllers are JavaScript classes that you bind to a route to handle the HTTP requests. Controllers act as an organization layer and help you divide the business logic of your application inside different files/classes.

HttpContext

AdonisJS creates an instance of the HttpContext class for every incoming HTTP request. The HttpContext (aka ctx) carries the information like the request body, headers, authenticated user, etc, for a given request.

Middleware

The middleware pipeline in AdonisJS is an implementation of Chain of Responsibility design pattern. You can use middleware to intercept HTTP requests and respond to them before they reach the route handler.

Global Exception handler

The global exception handler handles exceptions raised during an HTTP request at a central location. You can use the global exception handler to convert exceptions to an HTTP response or report them to an external logging service.

Server

The server module wires up the router, middleware, the global exception handler and exports a handle function you can bind to the Node.js HTTP server to handle requests.

How AdonisJS boots the HTTP server

The HTTP server is booted once you call the boot method on the Server class. Under the hood, this method performs the following actions.

  • Create the middleware pipeline
  • Compile routes
  • Import and instantiate the global exception handler

In a typical AdonisJS application, the boot method is called by the Ignitor module within the bin/server.ts file.

Also, it is essential to define the routes, middleware, and the global exception handler before the boot method is called, and AdonisJS achieves that using the start/routes.ts and start/kernel.ts preload files.

HTTP request lifecycle

Now that we have an HTTP server listening for incoming requests. Let's see how AdonisJS handles a given HTTP request.

Creating the HttpContext

As the first step, the server module creates an instance of the HttpContext class and passes it as a reference to the middleware, route handlers, and the global exception handler.

If you have enabled the AsyncLocalStorage, then the same instance is shared as the local storage state.

Executing server middleware stack

Next, the middleware from the server middleware stack are executed. These middleware can intercept and respond to the request before it reaches the route handler.

Also, every HTTP request goes through the server middleware stack, even if you have not defined any router for the given endpoint. This allows server middleware to add functionality to an app without relying on the routing system.

Finding the matching route

If a server middleware does not end the request, we look for a matching route for the req.url property. The request is aborted with a 404 - Not found exception when no matching route exists. Otherwise, we continue with the request.

Executing the route middleware

Once there is a matching route, we execute the router global middleware and the named middleware stack. Again, middleware can intercept the request before it reaches the route handler.

Executing the route handler

As the final step, the request reaches the route handler and returns to the client with a response.

Suppose an exception is raised during any step in the process. In that case, the request will be handed over to the global exception handler, who is responsible for converting the exception to a response.

Serializing response

Once you define the response body using the response.send method or by returning a value from the route handler, we begin the response serialization process and set the appropriate headers.

Learn more about response body serialization