Hi! If you've ever wanted to build a RESTful API using NestJS and TypeORM with input validation through DTOs (Data Transfer Objects), you're in the right place. In this guide, we'll walk through creating a simple CRUD (Create, Read, Update, Delete) application step by step, ensuring clean and maintainable code. let's get started!
Project Installation
First, create a new NestJS
project.
shellnpm install -g @nestjs/cli nest new my-crud-app cd my-crud-app
Then, install the necessary dependencies:
shellnpm install --save @nestjs/typeorm typeorm mysql2 class-validator class-transformer
Setting Up TypeORM with Database
Ensure you have MySQL installed and create a database (e.g., my_crud_db
).
Next, configure TypeORM to connect to your database in app.module.ts
:
typescriptimport { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; @Module({ imports: [ TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3306, username: 'your_db_username', password: 'your_db_password', database: 'my_crud_db', entities: [__dirname + '/**/*.entity{.ts,.js}'], synchronize: true, // Don't use synchronize: true in production }), ], }) export class AppModule {}
Replace
'your_db_username'
and'your_db_password'
with your actual database credentials.
Creating a Module, Entity, and DTO
Let's generate a Users
module and a User
entity.
shellnest generate module users nest generate class users/user.entity --no-spec
Now, define the User entity in user.entity.ts
:
typescriptimport { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() email: string; }
Data Transfer Objects (DTOs) help validate and define the shape of data transferred over the network. Create DTOs for creating and updating users.
shellnest generate class users/dto/create-user.dto --no-spec nest generate class users/dto/update-user.dto --no-spec
In create-user.dto.ts
:
typescriptimport { IsString, IsEmail } from 'class-validator'; export class CreateUserDto { @IsString() name: string; @IsEmail() email: string; }
In update-user.dto.ts
:
typescriptimport { IsString, IsEmail, IsOptional } from 'class-validator'; export class UpdateUserDto { @IsString() @IsOptional() name?: string; @IsEmail() @IsOptional() email?: string; }
Creating a Repository
Update users.module.ts
:
typescriptimport { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './user.entity'; import { UsersService } from './users.service'; import { UsersController } from './users.controller'; @Module({ imports: [TypeOrmModule.forFeature([User])], providers: [UsersService], controllers: [UsersController], }) export class UsersModule {}
Creating Service and Controller
Here, we're going to generate the service and controller.
shellnest generate service users nest generate controller users
Update users.service.ts
:
typescriptimport { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { User } from './user.entity'; import { CreateUserDto } from './dto/create-user.dto'; import { UpdateUserDto } from './dto/update-user.dto'; @Injectable() export class UsersService { constructor( @InjectRepository(User) private usersRepository: Repository<User>, ) {} create(createUserDto: CreateUserDto): Promise<User> { const newUser = this.usersRepository.create(createUserDto); return this.usersRepository.save(newUser); } findAll(): Promise<User[]> { return this.usersRepository.find(); } findOne(id: number): Promise<User> { return this.usersRepository.findOneBy({ id }); } async update(id: number, updateUserDto: UpdateUserDto): Promise<User> { await this.usersRepository.update(id, updateUserDto); return this.usersRepository.findOneBy({ id }); } async remove(id: number): Promise<void> { await this.usersRepository.delete(id); } }
Update users.controller.ts
:
typescriptimport { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common'; import { UsersService } from './users.service'; import { CreateUserDto } from './dto/create-user.dto'; import { UpdateUserDto } from './dto/update-user.dto'; import { User } from './user.entity'; @Controller('users') export class UsersController { constructor(private usersService: UsersService) {} @Post() create(@Body() createUserDto: CreateUserDto): Promise<User> { return this.usersService.create(createUserDto); } @Get() findAll(): Promise<User[]> { return this.usersService.findAll(); } @Get(':id') findOne(@Param('id') id: number): Promise<User> { return this.usersService.findOne(id); } @Put(':id') update(@Param('id') id: number, @Body() updateUserDto: UpdateUserDto): Promise<User> { return this.usersService.update(id, updateUserDto); } @Delete(':id') remove(@Param('id') id: number): Promise<void> { return this.usersService.remove(id); } }
Testing the API
Start the application:
shellnpm run start
Create a User
- Method: POST
- URL:
http://localhost:3000/users
- Body:
json{ "name": "Mao Jinguang", "email": "maojinguang@kimkorngmao.com" }
Get All Users
- Method: GET
- URL:
http://localhost:3000/users
Get a Single User
- Method: GET
- URL:
http://localhost:3000/users/1
Update a User
- Method: PUT
- URL:
http://localhost:3000/users/1
- Body:
json{ "name": "Mao Kimkorng", "email": "me@kimkorngmao.com" }
Delete a User
- Method: DELETE
- URL:
http://localhost:3000/users/1