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 function NestFactory 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, choose raw and set the format to JSON
  • 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 posts
  • GET http://localhost:3000/posts/:id: Get a single post by ID
  • PUT http://localhost:3000/posts/:id: Update a post by ID
  • DELETE 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!

Similar Posts