NestJS CRUD API with TypeORM and PostgreSQL

by Didin J. on May 23, 2025 NestJS CRUD API with TypeORM and PostgreSQL

Build a NestJS CRUD API with TypeORM and PostgreSQL. Full tutorial with setup, entity creation, REST endpoints, and input validation using DTOs


In this full tutorial, learn how to build a RESTful CRUD API using NestJS, TypeORM, and PostgreSQL. Follow step-by-step instructions to set up a scalable backend, create database models, and implement endpoints for creating, reading, updating, and deleting data. Perfect for modern web and mobile app development.


Setting Up NestJS, TypeORM, and PostgreSQL

To get started building a CRUD API with NestJS, TypeORM, and PostgreSQL, you’ll first need to set up your development environment. This includes installing the NestJS CLI, creating a new project, and configuring the database connection.

1. Prerequisites

Before we begin, make sure you have the following installed:

  • Node.js (v18 or above)
  • npm or Yarn
  • PostgreSQL (make sure the service is running)
  • NestJS CLI (install globally using the command below)
npm install -g @nestjs/cli

2. Create a New NestJS Project

Generate a new NestJS project using the CLI:

nest new nest-crud-api

Choose your preferred package manager (npm or yarn) when prompted.

3. Install Required Dependencies

Next, navigate into your project directory and install TypeORM and PostgreSQL driver:

cd nest-crud-api
npm install --save @nestjs/typeorm typeorm pg

4. Set Up TypeORM Configuration

Open src/app.module.ts and configure the TypeORM module with your PostgreSQL database credentials:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'your_postgres_username',
      password: 'your_postgres_password',
      database: 'nest_crud_db',
      autoLoadEntities: true,
      synchronize: true, // Disable in production
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

🔐 Note: The synchronize: true option automatically syncs your entity schemas with the database — convenient for development but not recommended for production.

5. Create the PostgreSQL Database

Ensure you’ve created the database nest_crud_db in your PostgreSQL instance. You can use psql or any database GUI like pgAdmin:

psql postgres -U djamware
CREATE DATABASE nest_crud_db;
\q


Creating the User Module and Entity

In this section, we'll generate the User module, controller, and service, and define the corresponding database entity using TypeORM.

1. Generate User Module, Controller, and Service

Use the NestJS CLI to quickly scaffold the required files:

nest generate module user
nest generate controller user
nest generate service user

This creates the following structure:

src/
├── user/
│   ├── user.module.ts
│   ├── user.controller.ts
│   ├── user.service.ts

2. Create the User Entity

Create a new file named user.entity.ts inside the user folder:

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @Column({ unique: true })
    email: string;

    @Column()
    password: string;
}

3. Register the Entity in UserModule

Update the user.module.ts file to include the TypeOrmModule and register the User entity:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UserService } from './user.service';
import { UserController } from './user.controller';

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  providers: [UserService],
  controllers: [UserController],
})
export class UserModule { }

4. Import UserModule in AppModule

Finally, update app.module.ts to import the UserModule:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserModule } from './user/user.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'your_postgres_username',
      password: 'your_postgres_password',
      database: 'nest_crud_db',
      autoLoadEntities: true,
      synchronize: true,
    }),
    UserModule,
  ],
})
export class AppModule {}


Implementing CRUD Operations for the User API

1. Implement UserService Methods

Open src/user/user.service.ts and add methods for Create, Read, Update, and Delete:

import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UserService {
    constructor(
        @InjectRepository(User)
        private readonly userRepository: Repository<User>,
    ) { }

    async create(userData: Partial<User>): Promise<User> {
        const user = this.userRepository.create(userData);
        return this.userRepository.save(user);
    }

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

    async findOne(id: number): Promise<User> {
        const user = await this.userRepository.findOneBy({ id });
        if (!user) {
            throw new NotFoundException(`User with ID ${id} not found`);
        }
        return user;
    }

    async update(id: number, userData: Partial<User>): Promise<User> {
        await this.userRepository.update(id, userData);
        return this.findOne(id);
    }

    async remove(id: number): Promise<void> {
        const result = await this.userRepository.delete(id);
        if (result.affected === 0) {
            throw new NotFoundException(`User with ID ${id} not found`);
        }
    }
}

2. Implement UserController Routes

Open src/user/user.controller.ts and define the REST endpoints:

import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common';
import { UserService } from './user.service';
import { User } from './user.entity';

@Controller('users')
export class UserController {
    constructor(private readonly userService: UserService) { }

    @Post()
    create(@Body() userData: Partial<User>): Promise<User> {
        return this.userService.create(userData);
    }

    @Get()
    findAll(): Promise<User[]> {
        return this.userService.findAll();
    }

    @Get(':id')
    findOne(@Param('id') id: string): Promise<User> {
        return this.userService.findOne(+id);
    }

    @Put(':id')
    update(@Param('id') id: string, @Body() userData: Partial<User>): Promise<User> {
        return this.userService.update(+id, userData);
    }

    @Delete(':id')
    remove(@Param('id') id: string): Promise<void> {
        return this.userService.remove(+id);
    }
}

3. Test the API

You can now test the API using tools like Postman or Insomnia.

npm run start

The available endpoints are:

  • POST /users – Create a user

NestJS CRUD API with TypeORM and PostgreSQL - post user

  • GET /users – Get all users

NestJS CRUD API with TypeORM and PostgreSQL - get all users

  • GET /users/:id – Get a single user by ID

NestJS CRUD API with TypeORM and PostgreSQL - get user by id

  • PUT /users/:id – Update a user by ID

NestJS CRUD API with TypeORM and PostgreSQL - update user

  • DELETE /users/:id – Delete a user by ID

NestJS CRUD API with TypeORM and PostgreSQL - delete user


Adding Input Validation with DTOs and class-validator

1. Install Validation Packages

NestJS uses class-validator and class-transformer for validation. If they’re not installed yet, add them:

npm install class-validator class-transformer

2. Create DTOs for User

Create a new file user.dto.ts inside the user folder and define two DTOs — one for creating a user and another for updating:

// src/user/user.dto.ts
import { IsEmail, IsNotEmpty, IsOptional, MinLength } from 'class-validator';

export class CreateUserDto {
    @IsNotEmpty()
    name: string;

    @IsEmail()
    email: string;

    @MinLength(6)
    password: string;
}

export class UpdateUserDto {
    @IsOptional()
    @IsNotEmpty()
    name?: string;

    @IsOptional()
    @IsEmail()
    email?: string;

    @IsOptional()
    @MinLength(6)
    password?: string;
}

3. Enable ValidationPipe Globally

Open main.ts and enable global validation:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

4. Update Controller to Use DTOs

Update user.controller.ts to use the DTOs:

import { Body, Controller, Delete, Get, Param, Post, Put } from '@nestjs/common';
import { UserService } from './user.service';
import { User } from './user.entity';
import { CreateUserDto, UpdateUserDto } from './user.dto';

@Controller('users')
export class UserController {
    constructor(private readonly userService: UserService) { }

    @Post()
    create(@Body() createUserDto: CreateUserDto): Promise<User> {
        return this.userService.create(createUserDto);
    }

    @Get()
    findAll(): Promise<User[]> {
        return this.userService.findAll();
    }

    @Get(':id')
    findOne(@Param('id') id: string): Promise<User> {
        return this.userService.findOne(+id);
    }

    @Put(':id')
    update(
        @Param('id') id: string,
        @Body() updateUserDto: UpdateUserDto,
    ): Promise<User> {
        return this.userService.update(+id, updateUserDto);
    }

    @Delete(':id')
    remove(@Param('id') id: string): Promise<void> {
        return this.userService.remove(+id);
    }
}

Now your API will:

  • Reject invalid emails or short passwords
  • Accept only defined and validated fields
  • Transform and sanitize input using DTOs


Conclusion

In this tutorial, you learned how to build a complete CRUD REST API using NestJS, TypeORM, and PostgreSQL. We covered setting up the project, creating a User module with entity and database integration, implementing CRUD operations, and adding input validation with DTOs and class-validator.

This foundation can be extended with features like authentication (JWT), pagination, filtering, or relational entities for real-world applications. NestJS’s modular structure and powerful tooling make it an excellent choice for building scalable and maintainable backend APIs.

You can find the full source code on our GitHub.

That just the basic. If you need more deep learning about Node, Express, Sequelize, PostgreSQL/MySQ or related you can take the following cheap course:

Thanks!