The first application in Express.js

Dominik Szczepaniak
8 min readJun 16, 2023

Despite its long-standing presence on the market, Express.js is still one of the most popular frameworks for Node.js. All signs in heaven and earth indicate that this is unlikely to change in the near future. In this article, I will show you a template that allows creating your first application in Express.js. You can use it for learning, prototyping, or even as a foundation for a business application.

I decided to bet on Express for several reasons. First, Express.js is now a dozen years old. During that time, the technology matured, resulting in a huge number of products and tools being built around it. The demand for familiarity with Express will be high for a long time, and this article will help you learn a little more about it.

Secondly, the overhead while learning Express is, in my opinion, very low. The Express documentation is easy to understand, simple, and concise. The docs even allow you to copy code snippets that, with a little customization, can be run as full-fledged applications.

Third, the low entry level, lifespan, and ubiquity of Express usage mean there is quite a large community built around Express. The large community and abundance of Express-related content mean if you run into a problem, there is a low chance that no one before you has stepped into it. The express tag on Stack Overflow is alive and well. In the popularity rankings of frameworks for Node.js, Express is still present. On the other hand, is the statement “millions of flies can’t be wrong” true in this case? I leave the topic for discussion in the comments 😉

Components of the application

The application template I’ve prepared allows you to quickly launch your project in Express.js. I decided to use TypeScript because of the typing capability and convenience of my work and yours. If for some reason you prefer to write in pure JavaScript, removing types, additional dependencies, and TypeScript configuration should not be a problem. You can find all the code described in this article in the repository on GitHub that I prepared for this post. To download the application, all you need to do is run git clone git@github.com:elszczepano/expressjs-boilerplate.git && cd expressjs-boilerplate

After downloading the project, you can run the application by calling docker-compose up --build

The command builds an image with the application and starts the container with the database and the application. Additionally, it configures the network on which the containers will run.

When you see confirmation in the command line that the application is running, you can visit http://localhost:8000/hello/:nameor send an HTTP request to the specified address using any HTTP client such as cURL, Postman, or Insomnia. In place of the :name parameter, you can enter your name. As a result, a JSON will be returned with a list of users who have queried the indicated address and the value passed.

Application infrastructure

In addition to the Express app itself, I used several additional components.

The first is the MySQL database. It will literally take you a while to replace MySQL with any other database solution, so you don’t have to get too attached to it. To keep the template simple, I also decided not to use ORMs. Instead of an ORM, I decided to use the node driver for MySQL directly. Of course, nothing prevents you from adding any ORM to the project.

The prepared application was adjusted to run in containers. For this purpose, I have prepared a Dockerfile and a docker-compose.yml file. With this, it is possible to run the prepared application using Docker. Before you run the application, make sure that Docker and Docker Compose are installed on your machine. The Dockerfile for prepared application looks as follows:

I used the Alpine Linux image for the Node.js environment as the base image. Feel free to swap it for any other, depending on your preferences. The advantage of the Alpine Linux-based image is its small size. The docker-compose.yml file, in addition to the definition of the application container, additionally contains the definition of the container for the MySQL database, and the configuration of the network in which the containers will run:

From the most interesting configuration options, I decided to implement a health check for the database. The application container in Express.js will only start when the health check for the database returns a healthy status. In addition, all environment variables come from the .env file, which contains the values of the environment variables. The use of environment variables avoids keeping secrets in the repository and allows dynamic injection of variable values. The structure of the .env file is based on the structure from the .env.example file:

Components of the application

The prepared application was prepared in the Minimum Viable Product approach. I was keen to deliver a working boilerplate in a short time. The application boilerplate is supposed to allow rapid prototyping of the application, skipping the overhead of preparing the application structure and low-level components. If the MVP approach is unfamiliar to you, you can learn more about it in my article about Minimum Viable Product. Using the MVP approach means that the application framework is far from perfect. Some changes and improvements will need to be implemented before the production launch of the application.

Service

The main component of the application is the Service class. The file contains the initialization of Express and the inclusion of the HTTP server. In addition, extra middleware code can be included in this file. For now, I have only added the body-parser. The Service class itself looks as follows:

Router

The concept of a router is necessary to understand in order to develop applications in Express.js. I decided to prepare an abstraction that allows you to conveniently define paths by defining the IRouteDefiniton interface. In addition, the custom abstraction allows for potential expansion with additional mechanisms as needed. For now, the main value of the router is the automatic handling of query body processing in PUT and POST requests. The code of the Router presents as follows:

Adding a Router to an application is handled in the Service class in the addRouter() method. The method allows you to define a path parameter, in case all of the paths should contain a specific prefix.

The initialization and addition of the router look as follows:

Of course, nothing prevents you from defining more than one router.

Handler

In the Handler class, the Request and Response objects known from Express, as well as the work with the HTTP protocol in Node.js itself, are available for use. The responsibility the Handler is to prepare the data received in the request into the expected format. The Handler is also responsible for returning the response to the client in the appropriate format. Each Handler implements the IHandler interface:

In the application, I prepared a sample implementation of the IHandler interface. HelloHandler can serve you as an example for further handlers:

Controller

In the Handler constructor, I passed a Controller instance, on which the execute() method is then called. Controllers contain the business logic of the application. The controller is defined by the IController interface:

The interface uses two generic types — TParams to type the object with parameters and TResult to type the response. The implementation of the example controller is shown in the following code:

The example controller uses the repository injected in the constructor to store the passed name. Then it retrieves the guest list from the repository and returns the result.

Repository

The repository classes will serve as components of the data access layer. By using repositories, the logic related to data access is isolated from the business logic contained in the controller. The controller receives a ready-made class that allows it to perform specific operations on the data, such as reading, writing, or updating. What’s more, the controller doesn’t even know what data source it is using or the structure of the stored data. Thanks to this, the MySQL database can be replaced by another solution, without the necessity of modifying the business logic code. An example repository is shown in the following code snippet:

In the case of repositories, there is no need to define an underlying interface common to all repositories. Methods defined in the repository will strictly depend on the data structure and business requirements.

Driver

The Driver class is an additional abstraction into which I have wrapped the driver for MySQL. With this, repositories using MySQL can ignore the implementation details of the external component. A developer gets a class with a convenient query method, which expects only a database query. A meticulous eye could see that the presented solution is vulnerable to SQL Injection attacks. In order to simplify the example, I decided to omit this aspect, but it is one of the necessary improvements that would need to be added to the framework before using it in production. In case you want to use ORM, Driver could be replaced with another solution. The MySQLDriver class looks as follows:

In addition to the query method for performing queries, the Driver also implements methods for establishing and terminating connections to the database. Based on the IDriver interface, other drivers could be prepared for different database solutions. However, this would require handling database queries in various dialects. It could be dynamically injected queries or generating queries depending on the dialect chosen.

Migrator

In order to use a database, it would be good to initialize its structure beforehand. ORMs take care of this automatically, but since I decided to abandon ORMs, it was necessary to prepare my own migrator. The migrator is a relatively simple class and uses the driver described earlier. The migrator’s only responsibility is to perform the migration:

The migrate method in a loop executes all migrations. The migrations are defined in .sql files in the queries directory. A field for improvement in the migrator is saving the state of migrations. Currently, running the application on an already migrated database can result in errors or inconsistent data. A separate table can be used for this purpose.

Tests

In order to make the boilerplate as complete as possible, I prepared a configuration to write tests. I used AVA as the test runner. The chosen test runner allows for easier writing of FIRST-compliant tests, especially Fast and Isolated rules due to the parallel running of tests. For preparing stubs, I used the Sinon library. I prepared some sample tests to demonstrate how tests can be written using AVA and Sinon. I used the node-mocks-http library to prepare mocks of the Request and Response classes. The unit tests for the HelloController class look as follows:

Integration and E2E tests can be prepared using AVA too.

Summary

I have already mentioned several times that the prepared framework needs a few improvements to be a base for production applications. Nevertheless, it provides a solid foundation on which you can build your first application in Express.js. In addition to the aforementioned improvements, an element worth adding is the input validation mechanism. When you launch the application, check what happens when you enter a name longer than 30 characters. It is worthwhile to protect the application from such situations.

Another useful component is a logger. You can use one of the ready-made solutions, such as Pino.js.

I strongly encourage you to check out the repository on GitHub, to clone and run the project. Remember that this template is meant to serve you first and foremost, so don’t be afraid to add, change or remove something.

I also encourage you to participate in the discussion about the solution and performed decisions in the comments. In particular, I encourage you to suggest changes and improvements. You can also do this directly on GitHub in the form of an issue or pull request 😁.

The original article has been posted on my Polish programming blog — Devszczepaniak.pl.

Sources and extra links

--

--

Dominik Szczepaniak

Professionally Software Engineer at CKSource. Privately a blogger, a fan of Italian cuisine, and a fan of cycling and weight training.