How to Build Web APIs with NestJS, Postgres, and Sequelize – A Beginner‘s Guide
Introduction
Building scalable and maintainable web APIs can be challenging, especially for beginners. Fortunately, NestJS provides an opinionated and structured approach to building server-side applications with Node.js. When combined with a powerful ORM like Sequelize and a robust database like Postgres, you have a solid foundation for creating APIs that are both easy to develop and efficient to run.
In this beginner‘s guide, we‘ll walk through the process of building a RESTful API with NestJS, Postgres, and Sequelize from start to finish. By the end, you‘ll have a fully functional API with CRUD endpoints, validation, error handling, and tests. Let‘s get started!
What is NestJS?
NestJS is a progressive Node.js framework for building efficient, scalable, and enterprise-grade server-side applications. It uses modern JavaScript, is built with TypeScript, and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
Some of the key benefits of using NestJS include:
- Provides an out-of-the-box application architecture
- Makes use of dependency injection for loose coupling
- Integrates well with other libraries like Sequelize, TypeORM, Mongoose, etc.
- Provides a powerful CLI for boosting productivity
- Has built-in support for input validation and error handling
Prerequisites
To follow along with this tutorial, you‘ll need to have the following installed on your machine:
- Node.js (v10+)
- npm (v6+)
- Postgres
- A REST client like Postman for testing the API endpoints
You should also have some familiarity with TypeScript, SQL databases, and ORMs. Let‘s dive in!
Getting Started
The first step is to install the NestJS CLI globally using npm:
$ npm install -g @nestjs/cli
Once installed, you can use the CLI to generate a new NestJS project:
$ nest new my-nest-api
Choose npm as the package manager and wait for the installation to complete. This will generate a barebones NestJS app in a folder called my-nest-api
.
Navigate into the project folder and open it up in your favorite code editor:
$ cd my-nest-api
$ code .
Here‘s a quick overview of the key files and folders:
-
src
: Contains the source code files.app.controller.ts
: A basic controller with a single route.app.module.ts
: The root module of the application.app.service.ts
: A basic service with a single method.main.ts
: The entry point of the application which uses the core functionNestFactory
to create a Nest application instance.
-
test
: Contains the end-to-end tests. -
nest-cli.json
: Configuration file for the NestJS CLI. -
package.json
: The npm configuration file that contains the project dependencies and scripts. -
tsconfig.json
: The TypeScript configuration file.
Configuring the Database
Next, let‘s set up the Postgres database and configure Sequelize. Install the required packages:
$ npm install --save @nestjs/sequelize sequelize sequelize-typescript pg pg-hstore
Create a new file called ormconfig.json
in the project root with the following:
{
"type": "postgres",
"host": "localhost",
"port": 5432,
"username": "your_db_username",
"password": "your_db_password",
"database": "your_db_name",
"entities": ["dist/**/*.entity{.ts,.js}"],
"synchronize": true
}
Make sure to replace the placeholders with your actual Postgres database credentials.
Next, update the app.module.ts
file to import the Sequelize module:
import { Module } from ‘@nestjs/common‘;
import { SequelizeModule } from ‘@nestjs/sequelize‘;
@Module({
imports: [
SequelizeModule.forRoot({
dialect: ‘postgres‘,
host: ‘localhost‘,
port: 5432,
username: ‘your_db_username‘,
password: ‘your_db_password‘,
database: ‘your_db_name‘,
autoLoadModels: true,
synchronize: true,
}),
]
})
export class AppModule {}
The Sequelize module will handle creating the database connection and syncing the entities.
Building the API
With the database configured, we can start building out our API. Let‘s create a simple CRUD application for managing blog posts.
Generating Files
Use the NestJS CLI to generate a new module, service, and controller for posts:
$ nest g module posts
$ nest g service posts
$ nest g controller posts
This will create a posts
folder with the following files:
src/
posts/
dto/
entities/
posts.controller.ts
posts.service.ts
posts.module.ts
dto/
: Will contain our DTO (Data Transfer Object) classes for input validation.entities/
: Will contain our Sequelize entity classes which represent database tables.posts.controller.ts
: The controller class that will define our API routes and endpoints.posts.service.ts
: The service class that will contain our business logic and database operations.posts.module.ts
: The module class that will tie everything together.
Defining Entities
Create a new file called post.entity.ts
inside the posts/entities
folder:
import { Table, Column, Model, DataType } from ‘sequelize-typescript‘;
@Table
export class Post extends Model<Post> {
@Column({
type: DataType.STRING,
allowNull: false,
})
title: string;
@Column({
type: DataType.TEXT,
allowNull: false,
})
body: string;
@Column({
type: DataType.BOOLEAN,
allowNull: false,
defaultValue: false,
})
published: boolean;
}
This defines a simple Post
entity with title
, body
, and published
properties. The @Table
decorator tells Sequelize that this class represents a database table, while the @Column
decorators define the table columns.
Update the posts.module.ts
file to import the entity:
import { Module } from ‘@nestjs/common‘;
import { SequelizeModule } from ‘@nestjs/sequelize‘;
import { Post } from ‘./entities/post.entity‘;
import { PostsController } from ‘./posts.controller‘;
import { PostsService } from ‘./posts.service‘;
@Module({
imports: [SequelizeModule.forFeature([Post])],
controllers: [PostsController],
providers: [PostsService],
})
export class PostsModule {}
Implementing CRUD Endpoints
Update the posts.service.ts
file with CRUD methods for interacting with the database:
import { Injectable } from ‘@nestjs/common‘;
import { InjectModel } from ‘@nestjs/sequelize‘;
import { CreatePostDto, UpdatePostDto } from ‘./dto‘;
import { Post } from ‘./entities/post.entity‘;
@Injectable()
export class PostsService {
constructor(
@InjectModel(Post)
private postModel: typeof Post,
) {}
create(createPostDto: CreatePostDto): Promise<Post> {
return this.postModel.create(createPostDto);
}
findAll(): Promise<Post[]> {
return this.postModel.findAll();
}
findOne(id: number): Promise<Post> {
return this.postModel.findOne({
where: {
id,
},
});
}
update(id: number, updatePostDto: UpdatePostDto): Promise<Post> {
return this.postModel.update(updatePostDto, {
where: {
id,
},
returning: true,
}).then(([, [updatedPost]]) => updatedPost);
}
async delete(id: number): Promise<void> {
const post = await this.findOne(id);
await post.destroy();
}
}
These methods perform the basic CRUD operations by making use of Sequelize‘s built-in methods like create()
, findAll()
, findOne()
, update()
and destroy()
. The @InjectModel(Post)
decorator injects an instance of the Post
entity into the service.
Next, create create-post.dto.ts
and update-post.dto.ts
files inside the posts/dto
folder:
// create-post.dto.ts
export class CreatePostDto {
title: string;
body: string;
published?: boolean;
}
// update-post.dto.ts
export class UpdatePostDto {
title?: string;
body?: string;
published?: boolean;
}
These DTO classes define the shape of the data that will be sent in the request bodies for creating and updating posts.
Finally, update the posts.controller.ts
file to implement the API endpoints:
import { Body, Controller, Delete, Get, Param, Post, Put } from ‘@nestjs/common‘;
import { PostsService } from ‘./posts.service‘;
import { CreatePostDto, UpdatePostDto } from ‘./dto‘;
import { Post as PostEntity } from ‘./entities/post.entity‘;
@Controller(‘posts‘)
export class PostsController {
constructor(private postsService: PostsService) {}
@Post()
create(@Body() createPostDto: CreatePostDto): Promise<PostEntity> {
return this.postsService.create(createPostDto);
}
@Get()
findAll(): Promise<PostEntity[]> {
return this.postsService.findAll();
}
@Get(‘:id‘)
findOne(@Param(‘id‘) id: string): Promise<PostEntity> {
return this.postsService.findOne(+id);
}
@Put(‘:id‘)
update(
@Param(‘id‘) id: string,
@Body() updatePostDto: UpdatePostDto,
): Promise<PostEntity> {
return this.postsService.update(+id, updatePostDto);
}
@Delete(‘:id‘)
delete(@Param(‘id‘) id: string): Promise<void> {
return this.postsService.delete(+id);
}
}
The controller defines routes for each CRUD operation and maps them to the corresponding service methods. The @Body()
decorator extracts the request body, while the @Param()
decorator extracts route parameters.
Testing the API
Let‘s test our API using Postman. Start the server by running:
$ npm run start:dev
This will start the server in watch mode. Open up Postman and create a new request:
- Set the HTTP method to
POST
- Set the URL to
http://localhost:3000/posts
- Select the
Body
tab, chooseraw
and set the format toJSON
- Enter the following request body:
{
"title": "My First Post",
"body": "This is the body of my first post.",
"published": true
}
Hit Send
and you should get back a 201 Created
response with the created post object.
You can test the other endpoints in a similar manner:
GET http://localhost:3000/posts
: Get all postsGET http://localhost:3000/posts/:id
: Get a single post by IDPUT http://localhost:3000/posts/:id
: Update a post by IDDELETE http://localhost:3000/posts/:id
: Delete a post by ID
Deployment
To deploy our app, we‘ll use Heroku which makes it easy to deploy Node.js apps.
First, create a Heroku account and install the Heroku CLI on your machine. Then, create a new Heroku app:
$ heroku create
Add the Postgres addon to the app:
$ heroku addons:create heroku-postgresql:hobby-dev
Next, update the ormconfig.json
file to use the Heroku database URL:
{
"type": "postgres",
"url": "process.env.DATABASE_URL",
"entities": ["dist/**/*.entity{.ts,.js}"],
"synchronize": true,
"ssl": true,
"extra": {
"ssl": {
"rejectUnauthorized": false
}
}
}
Commit your changes and push to Heroku:
$ git push heroku main
Your app should now be live on Heroku!
Conclusion
In this guide, we‘ve covered how to build a RESTful API with NestJS, Postgres, and Sequelize. We set up a new NestJS project, configured the database connection, generated modules/controllers/services, defined entities and DTO classes, implemented CRUD endpoints, and deployed our app to Heroku.
Of course, this just scratches the surface of what you can do with NestJS and Sequelize. For more information, be sure to check out the official NestJS and Sequelize documentation.
Some next steps to consider:
- Add authentication/authorization with JWT
- Implement pagination, sorting, filtering
- Write more comprehensive tests
- Set up logging and monitoring
I hope this guide was helpful for getting up and running with NestJS, Postgres, and Sequelize. The code for this tutorial is available on GitHub. Feel free to star the repo and experiment with the code yourself.
Happy coding!