NestJS & PostgreSQL: A Simple CRUD Example

Updated: January 2, 2024 By: Guest Contributor Post a comment

Introduction

NestJS is a progressive Node.js framework for building efficient and scalable server-side applications. Paired with PostgreSQL, an open-source relational database, it becomes a powerful tool for creating robust web services. This tutorial walks you through setting up a NestJS application with a PostgreSQL database to perform CRUD (Create, Read, Update, Delete) operations.

Before we dive into the code, ensure you have Node.js and PostgreSQL installed on your system. You will also need an understanding of TypeScript, as NestJS is built with it.

Setting Up the Project

Firstly, install the Nest CLI using npm:

npm i -g @nestjs/cli

Create a new project:

nest new nest-postgres-crud

Change directory into the newly created project:

cd nest-postgres-crud

Install PostgreSQL driver and the TypeORM module, which will handle the database interactions:

npm install --save @nestjs/typeorm typeorm pg

After installation, configure TypeORM by editing ‘ormconfig.json’ in the project root, inputting your PostgreSQL credentials:

{
   "type": "postgres",
   "host": "localhost",
   "port": 5432,
   "username": "your_username",
   "password": "your_password",
   "database": "your_db_name",
   "entities": ["dist/**/*.entity{.ts,.js}"],
   "synchronize": true
}

Notes:
– The ‘synchronize’ option is helpful in the development environment. It auto-creates the database structure based on entities, but it should be turned off in production.
– The ‘entities’ path tells TypeORM where to look for entity definitions.

Creating an Entity

Create a ‘Product’ entity which will be a representation of our data model:

@Entity()
export class Product {
   @PrimaryGeneratedColumn()
   id: number;
   @Column()
   title: string;
   @Column()
   description: string;
   @Column()
   price: number;
}

Creating a Module

NestJS is modular, and each feature can be encapsulated within its own module. For our CRUD operations, we’ll create a ‘ProductsModule’:

@Module({
    imports: [TypeORMModule.forFeature([Product])],
    controllers: [ProductsController],
    providers: [ProductsService],
})
export class ProductsModule {}

Creating a Service

Services are used to abstract logic and database interactions. Our ‘ProductsService’ will manage the CRUD functions:

@Injectable()
export class ProductsService {
   constructor(
       @InjectRepository(Product)
       private productsRepository: Repository<Product>,
   ) {}

   // CRUD methods will go here
}

Creating a Controller

The controller will handle HTTP requests and interact with the service to manage products:

@Controller('products')
export class ProductsController {
     constructor(private readonly productsService: ProductsService) {}

     // Route handlers will go here
}

The ‘@Controller’ decorator defines the route at which the controller will be available. In this case, the products controller will be accessible at ‘/products’.

Defining CRUD Operations

Within the ‘ProductsService’, we define methods to handle CRUD operations using TypeORM’s built-in methods for interaction with PostgreSQL.

// ProductsService.ts

async create(productData: CreateProductDto): Promise<Product> {
    const newProduct = this.productsRepository.create(productData);
    await this.productsRepository.save(newProduct);
    return newPost;
}

async findAll(): Promise<Product[]> {
    return this.productsRepository.find();
}

async findOne(id: number): Promise<Product> {
    return this.productsRepository.findOne(id);
}

async update(id: number, productData: UpdateProductDto): Promise<void> {
    await this.productsRepository.update(id, productData);
}

async delete(id: number): Promise<void> {
    await this.productsRepository.delete(id);
}

Following, in the controller, we define our endpoints that will call the corresponding service methods.

// ProductsController.ts

@Post()
async create(@Body() productData: CreateProductDto) {
    return this.productsService.create(productData);
}

@Get()
findAll() {
    return this.productsService.findAll();
}

@Get(':id')
findOne(@Param('id') id: number) {
    return this.productsService.findOne(+id);
}

@Patch(':id')
update(@Param('id') id: number, @Body() productData: UpdateProductDto) {
    return this.productsService.update(+id, productData);
}

@Delete(':id')
delete(@Param('id') id: number) {
    return this.productsService.delete(+id);
}

We use ‘@Get’, ‘@Post’, ‘@Patch’, and ‘@Delete’ to denote standard HTTP methods. The ‘@Body’ decorator extracts data from the request payload; ‘@Param’ extracts route parameters.

Testing the API

With all CRUD operations defined and endpoints set, it’s time to test the API. Use tools like Postman or cURL to send requests and see if responses are as expected.

Conclusion

This tutorial provided a step-by-step approach to developing a CRUD application with NestJS and PostgreSQL. As you follow similar principles and process flows, NestJS makes it intuitive to scale up applications, add new features, and maintain existing codebases with ease. Familiarizing yourself with the core concepts of NestJS will surely streamline your development and empower you to build high-quality server-side applications.