Sling Academy
Home/Node.js/NestJS & PostgreSQL: A Simple CRUD Example

NestJS & PostgreSQL: A Simple CRUD Example

Last updated: January 02, 2024

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.

Next Article: NestJS & SQLite: A Basic CRUD Example

Previous Article: NestJS & MongoDB: A Simple CRUD Example

Series: Nest.js Tutorials: From Basics to Advanced

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates