Flying Santa
Kimkorng

Developing RESTful APIs with NestJS and TypeORM

15 SEP, 2024

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.

shell
npm install -g @nestjs/cli nest new my-crud-app cd my-crud-app

Then, install the necessary dependencies:

shell
npm 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:

TypeScript icon
typescript
import { 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.

shell
nest generate module users nest generate class users/user.entity --no-spec

Now, define the User entity in user.entity.ts:

TypeScript icon
typescript
import { 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.

shell
nest generate class users/dto/create-user.dto --no-spec nest generate class users/dto/update-user.dto --no-spec

In create-user.dto.ts:

TypeScript icon
typescript
import { IsString, IsEmail } from 'class-validator'; export class CreateUserDto { @IsString() name: string; @IsEmail() email: string; }

In update-user.dto.ts:

TypeScript icon
typescript
import { IsString, IsEmail, IsOptional } from 'class-validator'; export class UpdateUserDto { @IsString() @IsOptional() name?: string; @IsEmail() @IsOptional() email?: string; }

Creating a Repository

Update users.module.ts:

TypeScript icon
typescript
import { 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.

shell
nest generate service users nest generate controller users

Update users.service.ts:

TypeScript icon
typescript
import { 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:

TypeScript icon
typescript
import { 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:

shell
npm 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
SHARE