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.
42
Subscribe to my newsletter

Read articles from ByteScrum Technologies directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

ByteScrum Technologies
ByteScrum Technologies

Our company comprises seasoned professionals, each an expert in their field. Customer satisfaction is our top priority, exceeding clients' needs. We ensure competitive pricing and quality in web and mobile development without compromise.