Create an API Rest With NestJS and PostgreSQL
In this tutorial, I will try to explain how to create an API Rest with NestJS framework.
Before start
We are going to talk about some topics such as TypeScript, Promises, asyc, await and Docker commands which we will not be going in depth about but we will share information related to them.
Introduction
NestJS is a framework for building NodeJS server-side applications, it uses progressive JavaScript, is built and support TypeScript but let developers to code in pure JavaScript, but in this case we use TypeScript.
NestJS combine OOP(Oriented Object Programming), FP(Functional Programing), and FRP(Functional Reactive Programing).
NestJS makes use of the Express library, so every technique for using the MVC (Model-View-Controller) pattern in Express applies to NestJS as well.
Prerequisites
Please make sure that Node.js (>= v18.17.1) is installed on your operating system.
Installation
To start working with NestJS we need to install Nest CLI, it’s a command-line interface tool that will help you to maintain your Nest Applications, it assists in different manners, for example, scaffolding the project, serving the apps in development mode, and building the app for production distributions.
So let's start to create our project with the next command.
$ npm install -g @nestjs/cli
To scaffold a project with NestJS CLI, run the following command, the command going to create a new directory with the initial core configuration.
$ nest new tours-api
We can find core files in the src/
directory.
- app.controller.ts: A basic controller with a simple route.
- app.service.ts: A simple service.
- app.module.ts: The root module of the application.
- main.ts: The entry file of the application which uses the core function
NestFactory
to create a Nest application instance.
To start the application type the nest commands.
$ cd tours-api
$ npm run start
Now you can open your browser and navigate to http://localhost:3000.
Controllers
The controllers are the most important in Api because they are in charge to handle requests and responses to the client and the routing mechanism controls which controller receives the request.
For our project we will use a module for each endpoint, it helps us to keep the code relevant to a specific feature organized, so type the following command which will create new directories above the module.
$ nest generate module country
The command adds the user module in the main application module.
The next step we goingto to create the country controller with the next command:
$ nest generate controller /country/country
The command will generate a basic controller with the next structure:
NestJS help us modifying the CountryModule adding the new controller.
A controller is a simple class with the decorator @Controller('country')
which is required to define a controller and specifies the prefix country it allows us to easily group a set of related routes, and minimize repetitive code.
To handle the different methods NestJS provides us methods: @Get
, @Post
, @Put()
, @Delete()
, @Patch()
and there is another decorator that handles all of them @All()
.
In the next piece of code, we defined two methods with @Get
, @Post
decorators.
Now you can use postman and test the methods.
Service
Services are important because it is in charge of data storage and retrieve it, the service is designed to be used by the controller so lets to create a basic service with the next command.
$ nest generate service /country/country
As you can see we have the decorator @Injectable()
marks a class as a provider, providers are a fundamental concept in nest, the main idea of a provider is that it can inject dependencies, this means that objects can create various relationships with each other and these instances be delegated to Nest runtime.
Providers can be injected into other classes via constructor parameter injection using Nest’s built-in Dependency Injection (DI) system.
The service was configured in UserModule.
To keep organized the data entries to our api we are going to use a DTO (Data Transfer Object) and for this we are going to create a class.
$ nest generate class /country/dto/country.dto
This DTO will help us to receive data in the post method of our api in each request, for this reason we need to add some validations, so we will use the class-validator libraty.
With install comman we will add the library in our project.
$ npm install class-validator
Now, for our case, to create a country we need two attributes iso and name.
The @IsNumber() and @IsString() decorators help us to validate the data type of the DTO, you can learn more about class-validator in the documentation.
We add two methods in our service, one returns the list of the countries, and the other add a new country.
As I mentioned we inject user service into CountryController’s constructor and use the service methods to get data.
As you can see we add a new decorator @Body()
to handle the body of the request, it extracts the entire body object from the request and populates the decorated parameter with the values of the body.
Now we can test our API with postman.
Now in the next sections, we will configure our database, entities, and repositories to execute some queries.
Database configuration
NestJS allowing you to easily integrate with any SQL or NoSQL database for this reason is considered agnostic. It is simply a matter of loading an appropriate Node.js driver for the database.
For convenience Nest provides integration with TypeORM, Sequalize, and Mongose, those integrations provide us Nest JS features such as model/repository injection and testability.
In our case we will use TypeORM and PostgreSQL so we need to install the next dependencies.
$ npm install --save @nestjs/typeorm typeorm pg
For our database we are going to use a Docker container, so for this task we need to install Docker, once installed we are going to load a container with PostgreSql with the following command.
$ docker run -d -p 5444:5432 --name toursdb -e POSTGRES_USER=toursapi POSTGRES_DB=toursdb POSTGRES_PASSWORD=toursapi postgres
The flag -d indicates that the container going to execute in detach mode in the background.
The flag -p 5444:5432 specifies that the container will configure the port 5432 and we can access it with the 5444 from localhost.
— name toursdb specify the name of our container.
-e POSTGRES_PASSWORD, -e POSTGRES_DB and -e POSTGRES_USER we spesify environment variables of our data base.
Once the dependencies are installed and our container was configure we can import the TypeORM into root module.
The TypeOrmModule.forRoot() methods supports all the configurations bellow describe. You can learn more about this subject in the official documentation.
type: 'postgres',
host: 'localhost',
port: 5444,
username: 'toursapi',
password: 'toursapi',
database: 'toursdb',
entities: ['**/entity/*.entity.ts'],
synchronize: true,
autoLoadEntities: true
In this case we used a basic configiuration in the app.module.ts file.
Repository pattern
Repositories are classes or components that encapsulate the logic required to access data sources, this pattern decoupling the infrastructure or technology used to access databases from the domain model layer.
NestJS support repository pattern, for this task we need to create entities, n entity is a class that let us map to a database table or collection if you use MongoDB, you can create an entity by defining a new class and mark it with @Entity()
decorator.
Each entity has its own repository that can be obtained from the database connection, to continue the example we create a class and configure a user entity, we create the class into entities directory.
$ nest generate class country/entity/country.entity
Once created the country class we add new attributes such as uuid, name, and iso.
We configured different decorators:
@Entity(): This decorator is used to mark classes that will be an entity (table or document depend on database type). Database schema will be created for all classes decorated with it, and Repository can be retrieved and used for it.
@PrimaryGeneratedColumn(“uuid”): A column decorator is used to marking a specific class property as a table column.
@Column(): Column decorator is used to marking a specific class property as a table column. Only properties decorated with this decorator will be persisted in the database when the entity be saved.
You could learn more in TypeORM’s documentation.
In our database configuration have the synchronize: true
value, this configuration let us automatically loaded models will be synchronized, so when we execute the command $ npm run start:dev
TypeORM will create the table in our database, don’t used for prod environment.
And other important configuration that we did is the next:
entities: ['**/entity/*.entity.ts'],
autoLoadEntities: true
The last configuration permit TypeOrm find our entities and load the entities creating the database.
The next step is to configure the repository to access the database in the service, we are going to modify the service and implement a class Repository which helps us to work with our entity objects. Find entities, insert, update, delete, etc.
Now we use the instance of the repository to insert data and find the registers.
We are going to configure forFeature()
a method in the CountryModule to define the repositories.
This module uses the forFeature()
method to define which repositories are registered in the current scope. With that in place, we can inject the CountryRepository
into the CountryService
using the @InjectRepository()
decorator:
As you can see we modified the types to return the service.
We used the async, await and promises in this part of the code, if you want to know more about these topics review the next post Introduction to async / await and the NestJS doumentation.
It’s time to test our API with Postman.
Conclusion
Let’s look at what we have learned.
- How to create an API Rest with NestJS using CLI.
- Some important NestJS decorators.
- How to configure controllers, services, and repositories.
- How to configure the database connection.
- How to configure PostgreSQL Docker container.
Thanks for reading and I hope this little tutorial will help you.
Kindest Regards.