Sling Academy
Home/Node.js/How to Structure Your First NestJS Application

How to Structure Your First NestJS Application

Last updated: December 31, 2023

Embarking on a new project with NestJS? This tutorial will guide you through structuring your first application effectively using modern TypeScript features.

Setting Up Your Environment

Before diving into the structure, ensure you have Node.js installed. Then, install the Nest CLI globally using npm:

npm i -g @nestjs/cli

Create a new project:

nest new your-app-name

Understanding NestJS Modules

NestJS applications are modular, with the Module being the core building block. Here’s how to define a basic module:

import { Module } from '@nestjs/common';

@Module({
  // module configuration
})
export class AppModule {}

Building Blocks of NestJS

Let’s explore the foundational elements: Controllers, Providers, and Services.

Controllers

import { Controller, Get } from '@nestjs/common';

@Controller('api/items')
export class ItemsController {
  @Get()
  findAll(): string {
    return 'This action returns all items';
  }
}

Providers and Services

Services handle business logic and are defined as providers.

import { Injectable } from '@nestjs/common';

@Injectable()
export class ItemsService {
  findAll(): string[] {
    return ['Item 1', 'Item 2'];
  }
}

Include them in your module:

@Module({
  controllers: [ItemsController],
  providers: [ItemsService],
})

Scalable File Structure

A scalable NestJS file structure organizes code by feature:

src
├── items
│   ├── dto
│   ├── entities
│   ├── items.controller.ts
│   ├── items.module.ts
│   └── items.service.ts
├── users
│   ├── dto
│   ├── entities
│   ├── users.controller.ts
│   ├── users.module.ts
│   └── users.service.ts
├── app.module.ts
└── main.ts

Advanced Patterns

As your application grows, consider patterns like interceptors, guards, and pipelines.

Using Interceptors

import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class TransformInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable {
    return next.handle().pipe(
      map(data => ({ data }))
    );
  }
}

Implementing Guards

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';

@Injectable()
export class RolesGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    // Add your authentication logic here
    return true;
  }
}

Pipelines and Custom Decorators

Using Pipes for Validation and Transformation

NestJS pipes are powerful tools for handling validation and data transformation. They can be used for tasks like validating request data or transforming input data to the desired form before it reaches the route handler.

Here’s an example of using a built-in validation pipe:

import { Controller, Get, Query, UsePipes, ValidationPipe } from '@nestjs/common';

@Controller('api/items')
export class ItemsController {
  @Get()
  @UsePipes(new ValidationPipe())
  findItem(@Query('id') id: number): string {
    return `This action returns item #${id}`;
  }
}

In this example, the ValidationPipe ensures that the id query parameter is of the expected type.

Creating Custom Decorators

Custom decorators in NestJS allow you to abstract common logic and reuse it across your application. For instance, you can create a decorator to extract and validate user information from a request.

Here’s an example of a custom decorator that retrieves a user’s role:

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const GetUserRole = createParamDecorator(
  (data: unknown, ctx: ExecutionContext): string => {
    const request = ctx.switchToHttp().getRequest();
    return request.user.role; // Assuming 'user' and 'role' are defined in the request
  }
);

You can then use this decorator in a controller:

import { Controller, Get } from '@nestjs/common';
import { GetUserRole } from './get-user-role.decorator';

@Controller('api/users')
export class UsersController {
  @Get()
  findUserRole(@GetUserRole() role: string): string {
    return `User role: ${role}`;
  }
}

Conclusion

Structuring your NestJS application with clear, modular components and leveraging advanced features ensures scalability and maintainability. Start with a solid foundation and build incrementally as your needs evolve.

Next Article: How to Use GraphQL with NestJS

Previous Article: How to Validate Data in NestJS

Series: Nest.js Tutorials: From Basics to Advanced

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates