Building RESTful APIs with NestJS

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?

  1. Modular Architecture: NestJS promotes a modular structure, making it easy to manage and scale your applications.

  2. TypeScript Support: Built with TypeScript, NestJS provides strong typing and modern JavaScript features.

  3. Built-in Tools: Features like dependency injection, middleware, and easy integration with other libraries enhance productivity.

  4. 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);
  }
}
Conclusion
NestJS provides a powerful and flexible framework for building RESTful APIs. By leveraging its modular architecture, strong typing with TypeScript, and robust set of built-in tools, you can create scalable and maintainable server-side applications. This guide has covered the basics of setting up a NestJS project, creating modules, controllers, and services, and connecting to a database using TypeORM. With this foundation, you are well on your way to building efficient and reliable RESTful APIs with NestJS.