Building RESTful APIs with NestJS
A Comprehensive Guide to Building RESTful APIs with NestJS
NestJS is a progressive Node.js framework that helps you build efficient and scalable server-side applications. One of its most powerful features is the ability to build robust RESTful APIs with ease. In this guide, we will explore how to create a RESTful API using NestJS, covering the essentials from setting up a project to creating controllers, services, and connecting to a database.
Why Choose NestJS for RESTful APIs?
Modular Architecture: NestJS promotes a modular structure, making it easy to manage and scale your applications.
TypeScript Support: Built with TypeScript, NestJS provides strong typing and modern JavaScript features.
Built-in Tools: Features like dependency injection, middleware, and easy integration with other libraries enhance productivity.
Extensibility: Easily integrates with databases, validation libraries, authentication mechanisms, and more.
Setting Up a NestJS Project
Step 1: Install NestJS CLI
First, you need to install the NestJS Command Line Interface (CLI):
npm install -g @nestjs/cli
Step 2: Create a New Project
Create a new NestJS project using the CLI:
nest new my-rest-api
Step 3: Run the Application
To start the application, run:
npm run start
By default, the application runs on http://localhost:3000
.
Creating Your First Module, Controller, and Service
Step 1: Generate a Module
Use the NestJS CLI to generate a new module. For this example, we'll create a users
module:
nest generate module users
Step 2: Generate a Controller and Service
Next, generate a controller and a service for the users
module:
nest generate controller users
nest generate service users
Step 3: Define User Data Transfer Object (DTO)
Create a data transfer object (DTO) for user data validation:
// src/users/dto/create-user.dto.ts
export class CreateUserDto {
readonly name: string;
readonly age: number;
readonly email: string;
}
Step 4: Implement the Users Service
Open the src/users/users.service.ts
file and implement basic CRUD operations:
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
interface User {
id: number;
name: string;
age: number;
email: string;
}
@Injectable()
export class UsersService {
private readonly users: User[] = [];
private idCounter = 1;
create(createUserDto: CreateUserDto): User {
const newUser: User = { id: this.idCounter++, ...createUserDto };
this.users.push(newUser);
return newUser;
}
findAll(): User[] {
return this.users;
}
findOne(id: number): User {
return this.users.find(user => user.id === id);
}
update(id: number, updateUserDto: CreateUserDto): User {
const userIndex = this.users.findIndex(user => user.id === id);
if (userIndex === -1) return null;
const updatedUser = { ...this.users[userIndex], ...updateUserDto };
this.users[userIndex] = updatedUser;
return updatedUser;
}
remove(id: number): boolean {
const userIndex = this.users.findIndex(user => user.id === id);
if (userIndex === -1) return false;
this.users.splice(userIndex, 1);
return true;
}
}
Step 5: Implement the Users Controller
Open the src/users/users.controller.ts
file and implement the controller methods:
import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@Get()
findAll() {
return this.usersService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.usersService.findOne(+id);
}
@Put(':id')
update(@Param('id') id: string, @Body() updateUserDto: CreateUserDto) {
return this.usersService.update(+id, updateUserDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.usersService.remove(+id);
}
}
Step 6: Testing Your API
Use a tool like Postman or cURL to test your API endpoints. For example:
Create a user:
curl -X POST http://localhost:3000/users -H "Content-Type: application/json" -d '{"name": "John Doe", "age": 30, "email": "john.doe@example.com"}'
Get all users:
curl http://localhost:3000/users
Update a user:
curl -X PUT http://localhost:3000/users/1 -H "Content-Type: application/json" -d '{"name": "John Doe", "age": 31, "email": "john.updated@example.com"}'
Delete a user:
curl -X DELETE http://localhost:3000/users/1
Connecting to a Database
Step 1: Install TypeORM and Database Driver
For this example, we'll use PostgreSQL. Install TypeORM and the PostgreSQL driver:
npm install --save @nestjs/typeorm typeorm pg
Step 2: Configure TypeORM
Configure TypeORM in the src/app.module.ts
file:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'your-username',
password: 'your-password',
database: 'your-database',
autoLoadEntities: true,
synchronize: true,
}),
UsersModule,
],
})
export class AppModule {}
Step 3: Create User Entity
Create a user entity in src/users/user.entity.ts
:
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
age: number;
@Column()
email: string;
}
Step 4: Update Users Module
Update the UsersModule
to include TypeORM:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { User } from './user.entity';
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
Step 5: Update Users Service
Update the UsersService
to use TypeORM:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateUserDto } from './dto/create-user.dto';
import { User } from './user.entity';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {}
create(createUserDto: CreateUserDto): Promise<User> {
const newUser = this.userRepository.create(createUserDto);
return this.userRepository.save(newUser);
}
findAll(): Promise<User[]> {
return this.userRepository.find();
}
findOne(id: number): Promise<User> {
return this.userRepository.findOneBy({ id });
}
async update(id: number, updateUserDto: CreateUserDto): Promise<User> {
await this.userRepository.update(id, updateUserDto);
return this.findOne(id);
}
async remove(id: number): Promise<void> {
await this.userRepository.delete(id);
}
}