Introduction
In this Nest JS tutorial, we will guide you through the process of building robust, scalable server-side applications using Nest JS. Nest JS is a progressive Node.js framework that leverages TypeScript to create efficient and reliable applications. Its modular architecture and strong typing capabilities make it an excellent choice for building enterprise-level applications.
Related Articles
Also, Read: Complete Next.js Tutorial: Mastering Full Stack Development
Overview of Nest JS
Nest JS is built on top of Express.js (or optionally Fastify) and provides an out-of-the-box application architecture. This architecture allows for the effortless creation of highly testable, scalable, loosely coupled, and easily maintainable applications. Learn more about Nest JS.
Importance and Benefits of Learning Nest JS
- TypeScript Support: Provides a strongly typed programming language which enhances code quality and maintainability.
- Modular Architecture: Encourages the separation of concerns and scalability.
- Extensive Ecosystem: Integrates seamlessly with other libraries and technologies like TypeORM, Mongoose, and GraphQL.
- Community and Documentation: Strong community support and comprehensive documentation make learning and using Nest JS easier.
Structure and Objectives of This Nest JS Tutorial
This Nest JS tutorial is designed for beginners and covers the following:
- Setting up the development environment.
- Creating a basic Nest JS application.
- Understanding core concepts like modules, controllers, and providers.
- Building a RESTful API.
- Connecting to a database.
- Implementing authentication and authorization.
- Handling errors and validation.
- Testing and deploying the application.
- Exploring advanced topics and integrations.
By the end of this tutorial, you will have a solid understanding of Nest JS and the ability to build and deploy your own applications. For more information, refer to the official Nest JS documentation.
Setting Up Your Development Environment
Before we dive into building our application, we need to set up our development environment. This step is crucial to ensure that everything runs smoothly as we follow along with this Nest JS tutorial.
Prerequisites
- A computer with a modern operating system (Windows, macOS, or Linux)
- A basic understanding of JavaScript and TypeScript
- Node.js and npm installed on your machine
Installing Node.js and npm
First, we need to install Node.js and npm (Node Package Manager). You can download the installer from the official Node.js website. Follow the instructions to complete the installation.
Installing Nest JS CLI
Next, we will install the Nest JS CLI, which will help us to generate and manage our Nest JS applications. Open your terminal and run the following command:
npm install -g @nestjs/cli
You can find more information about the Nest JS CLI on the official documentation.
Configuring Your IDE for Nest JS Development
For an optimal development experience, it’s recommended to use an IDE like Visual Studio Code (VS Code). Install the recommended extensions for JavaScript/TypeScript development, such as the ESLint and Prettier extensions, to help you maintain code quality and consistency. Here are some useful extensions:
- ESLint
- Prettier
You can configure your IDE settings according to the Nest JS style guide found in the Nest JS documentation.
Creating Your First Nest JS Application
In this section of the Nest JS tutorial, we will create our first Nest JS application and explore its structure.
Generating a New Project with Nest JS CLI
Open your terminal and run the following command to generate a new Nest JS project:
nest new my-nest-app
Follow the prompts to set up your project. Choose the package manager (npm or yarn) to install the necessary dependencies. Once the setup is complete, navigate to the project directory:
cd my-nest-app
Exploring the Project Structure
Nest JS applications have a modular architecture. Here’s a brief overview of the default project structure:
- src/: Contains the source code of the application
- app.controller.ts: Defines the application’s controller
- app.service.ts: Defines the application’s service
- app.module.ts: The root module of the application
- test/: Contains the test files
- main.ts: The entry point of the application
Each of these files plays a specific role in the application’s architecture:
- app.module.ts: Registers the application’s components.
- app.controller.ts: Handles incoming requests and returns responses.
- app.service.ts: Contains the business logic.
For a detailed explanation of the project structure, refer to the Nest JS documentation.
Running the Application
To run your Nest JS application, use the following command:
npm run start
Open your browser and navigate to http://localhost:3000
to see your application in action. You should see “Hello World!” as the response.
Understanding the Core Concepts of Nest JS
In this part of the Nest JS tutorial, we will dive deeper into the core concepts of Nest JS, which form the backbone of any application built using this framework.
Modules
Modules are the basic building blocks of a Nest JS application. They are used to organize the application into cohesive blocks of functionality. Each application has at least one module, the root module. Here is an example of a simple module:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
For more details on modules, refer to the Nest JS documentation on Modules.
Controllers
Controllers handle incoming requests and return responses to the client. They are decorated with the @Controller()
decorator. Here’s a basic example:
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
Controllers are explained in detail in the Nest JS documentation on Controllers.
Providers
Providers are used for encapsulating business logic. They are decorated with the @Injectable()
decorator and are typically used by controllers. Example:
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
Services
Services are a type of provider that contains business logic. They can be injected into controllers and other services to handle complex operations. The example above demonstrates a simple service.
Middleware
Middleware functions are executed before the route handler. They can be used for tasks like logging, authentication, and more. Here is an example of custom middleware:
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log('Request...');
next();
}
}
For more details, see the Nest JS documentation on Middleware.
Interceptors
Interceptors are used to add additional logic before or after method execution. They can modify the arguments passed to a method or the result returned by it. Example:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } 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 })));
}
}
Learn more in the Nest JS documentation on Interceptors.
Guards
Guards are used to determine whether a request will be handled by the route handler. They are useful for implementing authorization. Example:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise | Observable {
const request = context.switchToHttp().getRequest();
return validateRequest(request);
}
}
Refer to the Nest JS documentation on Guards for more information.
Pipes
Pipes are used for transforming and validating data. They can be used to validate input data and transform it into the desired format. Example:
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class ParseIntPipe implements PipeTransform {
transform(value: string, metadata: ArgumentMetadata): number {
const val = parseInt(value, 10);
if (isNaN(val)) {
throw new BadRequestException('Validation failed');
}
return val;
}
}
For more details, check the Nest JS documentation on Pipes.
Exception Filters
Exception filters are used to handle exceptions thrown by the application. They can catch exceptions and transform the response accordingly. Example:
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status = exception.getStatus();
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
For more information, see the Nest JS documentation on Exception Filters.
Building a Simple RESTful API with Nest JS
In this section of the Nest JS tutorial, we will build a simple RESTful API using Nest JS. This will involve creating a module, controller, and service, and implementing CRUD (Create, Read, Update, Delete) operations.
Creating a Module, Controller, and Service
Generate a new module, controller, and service using the Nest JS CLI:
nest generate module items
nest generate controller items
nest generate service items
This will create the necessary files for the items
module, controller, and service.
Implementing CRUD Operations
Controller: The controller will define routes and handle HTTP requests.
import { Controller, Get, Post, Body, Param, Delete, Put } from '@nestjs/common';
import { ItemsService } from './items.service';
import { CreateItemDto } from './dto/create-item.dto';
import { UpdateItemDto } from './dto/update-item.dto';
@Controller('items')
export class ItemsController {
constructor(private readonly itemsService: ItemsService) {}
@Post()
create(@Body() createItemDto: CreateItemDto) {
return this.itemsService.create(createItemDto);
}
@Get()
findAll() {
return this.itemsService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.itemsService.findOne(+id);
}
@Put(':id')
update(@Param('id') id: string, @Body() updateItemDto: UpdateItemDto) {
return this.itemsService.update(+id, updateItemDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.itemsService.remove(+id);
}
}
Service: The service will contain the business logic.
import { Injectable } from '@nestjs/common';
import { CreateItemDto } from './dto/create-item.dto';
import { UpdateItemDto } from './dto/update-item.dto';
@Injectable()
export class ItemsService {
private readonly items = [];
create(createItemDto: CreateItemDto) {
const newItem = { id: Date.now(), ...createItemDto };
this.items.push(newItem);
return newItem;
}
findAll() {
return this.items;
}
findOne(id: number) {
return this.items.find(item => item.id === id);
}
update(id: number, updateItemDto: UpdateItemDto) {
const itemIndex = this.items.findIndex(item => item.id === id);
if (itemIndex > -1) {
this.items[itemIndex] = { ...this.items[itemIndex], ...updateItemDto };
return this.items[itemIndex];
}
return null;
}
remove(id: number) {
const itemIndex = this.items.findIndex(item => item.id === id);
if (itemIndex > -1) {
return this.items.splice(itemIndex, 1);
}
return null;
}
}
Using DTOs (Data Transfer Objects)
DTOs are used to define the structure of the data transferred between the client and the server.
Create DTO:
export class CreateItemDto {
readonly name: string;
readonly description: string;
readonly price: number;
}
Update DTO:
export class UpdateItemDto {
readonly name?: string;
readonly description?: string;
readonly price?: number;
}
For more detailed information on controllers and services, refer to the Nest JS documentation on controllers.
Connecting to a Database
In this part of the Nest JS tutorial, we will connect our application to a PostgreSQL database using TypeORM. We will cover installing and configuring TypeORM, connecting to the database, creating entities and repositories, and performing database migrations.
Installing and Configuring TypeORM
First, install TypeORM and the PostgreSQL driver:
npm install @nestjs/typeorm typeorm pg
Next, configure TypeORM in the app.module.ts
file:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ItemsModule } from './items/items.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'your-username',
password: 'your-password',
database: 'your-database',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
}),
ItemsModule,
],
})
export class AppModule {}
Refer to the TypeORM documentation for more detailed configuration options.
Connecting to a PostgreSQL Database
Ensure PostgreSQL is installed and running on your machine. Create a database and user for your application. Update the TypeORM configuration with your PostgreSQL credentials.
Creating Entities and Repositories
Entity: Define an entity representing a database table.
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Item {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
description: string;
@Column('decimal')
price: number;
}
Repository: Inject the repository into the service to perform database operations.
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Item } from './item.entity';
import { CreateItemDto } from './dto/create-item.dto';
@Injectable()
export class ItemsService {
constructor(
@InjectRepository(Item)
private itemsRepository: Repository- ,
) {}
create(createItemDto: CreateItemDto): Promise
- {
const item = this.itemsRepository.create(createItemDto);
return this.itemsRepository.save(item);
}
findAll(): Promise
- {
return this.itemsRepository.find();
}
findOne(id: number): Promise
- {
return this.itemsRepository.findOne(id);
}
async remove(id: number): Promise
{
await this.itemsRepository.delete(id);
}
}
Performing Database Migrations
TypeORM provides powerful migration tools to manage database schema changes. Here’s a simple example of how to use migrations:
- Create a migration:
npm run typeorm migration:create -n CreateItems
- Write migration logic in the generated file.
- Run the migration:
npm run typeorm migration:run
For detailed information on migrations, refer to the TypeORM migration documentation.
Using Configuration and Environments
In this section of the Nest JS tutorial, we will manage configuration and environment variables using the ConfigModule.
Managing Configuration with the ConfigModule
Install the @nestjs/config
module:
npm install @nestjs/config
Configure it in the app.module.ts
file:
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { ItemsModule } from './items/items.module';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
ItemsModule,
],
})
export class AppModule {}
This makes the configuration globally available. For more details, refer to the ConfigModule documentation.
Setting Up Environment Variables
Create a .env
file in the root directory of your project:
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USER=myuser
DATABASE_PASSWORD=mypassword
DATABASE_NAME=mydatabase
Access these variables in your service or module:
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class AppService {
constructor(private configService: ConfigService) {}
getDatabaseConfig() {
return {
host: this.configService.get('DATABASE_HOST'),
port: this.configService.get('DATABASE_PORT'),
user: this.configService.get('DATABASE_USER'),
password: this.configService.get('DATABASE_PASSWORD'),
name: this.configService.get('DATABASE_NAME'),
};
}
}
Using environment variables allows you to manage different configurations for development, testing, and production environments.
Authentication and Authorization
In this part of the Nest JS tutorial, we will implement authentication and authorization using JWT (JSON Web Tokens).
Implementing JWT Authentication
First, install the necessary packages:
npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt
Create an auth
module, controller, and service:
nest generate module auth
nest generate controller auth
nest generate service auth
Auth Module: Configure JWT and Passport:
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { JwtStrategy } from './jwt.strategy';
import { UsersModule } from '../users/users.module';
@Module({
imports: [
UsersModule,
PassportModule,
JwtModule.register({
secret: 'SECRET_KEY', // use environment variable in production
signOptions: { expiresIn: '60m' },
}),
],
providers: [AuthService, JwtStrategy],
controllers: [AuthController],
})
export class AuthModule {}
Auth Service: Implement methods for validating users and generating tokens:
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UsersService } from '../users/users.service';
import { User } from '../users/user.entity';
@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService
) {}
async validateUser(username: string, pass: string): Promise {
const user = await this.usersService.findOne(username);
if (user && user.password === pass) {
const { password, ...result } = user;
return result;
}
return null;
}
async login(user: any) {
const payload = { username: user.username, sub: user.userId };
return {
access_token: this.jwtService.sign(payload),
};
}
}
JWT Strategy: Define JWT strategy for protecting routes:
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'SECRET_KEY',
});
}
async validate(payload: any) {
return { userId: payload.sub, username: payload.username };
}
}
Auth Controller: Implement endpoints for login:
import { Controller, Request, Post, UseGuards } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LocalAuthGuard } from './local-auth.guard';
import { JwtAuthGuard } from './jwt-auth.guard';
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
@UseGuards(LocalAuthGuard)
@Post('login')
async login(@Request() req) {
return this.authService.login(req.user);
}
@UseGuards(JwtAuthGuard)
@Post('profile')
getProfile(@Request() req) {
return req.user;
}
}
Setting Up User Roles and Permissions
Define user roles and set up guards to protect routes based on roles. Example:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const roles = this.reflector.get('roles', context.getHandler());
if (!roles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
return roles.includes(user.role);
}
}
Protecting Routes with Guards
Use the RolesGuard
to protect specific routes. Example:
import { Controller, Get, UseGuards } from '@nestjs/common';
import { RolesGuard } from './roles.guard';
import { Roles } from './roles.decorator';
@Controller('users')
@UseGuards(RolesGuard)
export class UsersController {
@Get()
@Roles('admin')
findAll() {
return [];
}
}
Error Handling and Validation
In this section of the Nest JS tutorial, we will cover error handling and data validation using Pipes and Exception Filters.
Using Pipes for Validation
Pipes in Nest JS are used to transform and validate data. We will use the class-validator
and class-transformer
packages to handle validation.
First, install the required packages:
npm install class-validator class-transformer
Next, create a DTO (Data Transfer Object) for validation:
import { IsString, IsInt, IsOptional } from 'class-validator';
export class CreateItemDto {
@IsString()
name: string;
@IsString()
description: string;
@IsInt()
price: number;
}
export class UpdateItemDto {
@IsString()
@IsOptional()
name?: string;
@IsString()
@IsOptional()
description?: string;
@IsInt()
@IsOptional()
price?: number;
}
Apply the DTO in the controller:
import { Body, Controller, Post, UsePipes, ValidationPipe } from '@nestjs/common';
import { ItemsService } from './items.service';
import { CreateItemDto } from './dto/create-item.dto';
@Controller('items')
export class ItemsController {
constructor(private readonly itemsService: ItemsService) {}
@Post()
@UsePipes(new ValidationPipe())
create(@Body() createItemDto: CreateItemDto) {
return this.itemsService.create(createItemDto);
}
}
For more information, refer to the Nest JS documentation on Pipes.
Creating Custom Validation Decorators
Custom validation decorators can be used for more complex validation logic. Here’s an example:
import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator';
export function IsPositive(validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator({
name: 'isPositive',
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
validator: {
validate(value: any, args: ValidationArguments) {
return typeof value === 'number' && value > 0;
},
},
});
};
}
Use the custom decorator in a DTO:
import { IsPositive } from './is-positive.decorator';
export class CreateItemDto {
@IsPositive({ message: 'Price must be a positive number' })
price: number;
}
Implementing Exception Filters
Exception filters handle exceptions and transform the response. Here’s an example of a custom exception filter:
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status = exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const message = exception instanceof HttpException
? exception.getResponse()
: { message: 'Internal server error' };
response.status(status).json({
statusCode: status,
message,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
Apply the filter in the main file:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AllExceptionsFilter } from './common/filters/all-exceptions.filter';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new AllExceptionsFilter());
await app.listen(3000);
}
bootstrap();
For more details, refer to the Nest JS documentation on Exception Filters.
Testing Your Nest JS Application
In this section of the Nest JS tutorial, we will focus on testing our Nest JS application. We’ll cover writing unit tests with Jest, writing end-to-end tests, and mocking dependencies.
Writing Unit Tests with Jest
Jest is the default testing framework used by Nest JS. To start, let’s ensure Jest is installed:
npm install --save-dev jest @types/jest ts-jest
Next, configure Jest in the package.json
file:
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
Create a sample unit test for the ItemsService
:
import { Test, TestingModule } from '@nestjs/testing';
import { ItemsService } from './items.service';
describe('ItemsService', () => {
let service: ItemsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [ItemsService],
}).compile();
service = module.get(ItemsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should create an item', () => {
const createItemDto = { name: 'Test item', description: 'Test description', price: 10 };
expect(service.create(createItemDto)).toEqual({
id: expect.any(Number),
...createItemDto,
});
});
});
Run the tests using the following command:
npm run test
Refer to the Jest documentation for more details on configuring and using Jest.
Writing End-to-End Tests
End-to-end (E2E) tests ensure that your application works as expected from a user’s perspective. Create an E2E test for the ItemsController
:
First, generate the E2E test directory:
mkdir test
Then create a sample E2E test file test/app.e2e-spec.ts
:
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';
describe('ItemsController (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/items (POST)', () => {
return request(app.getHttpServer())
.post('/items')
.send({ name: 'Test item', description: 'Test description', price: 10 })
.expect(201)
.expect({
id: expect.any(Number),
name: 'Test item',
description: 'Test description',
price: 10,
});
});
it('/items (GET)', () => {
return request(app.getHttpServer())
.get('/items')
.expect(200)
.expect([
{
id: expect.any(Number),
name: 'Test item',
description: 'Test description',
price: 10,
},
]);
});
});
Run E2E tests using the following command:
npm run test:e2e
Mocking Dependencies
Mocking dependencies is essential for isolating the unit of work you are testing. Here’s an example of how to mock the ItemsService
in a controller test:
import { Test, TestingModule } from '@nestjs/testing';
import { ItemsController } from './items.controller';
import { ItemsService } from './items.service';
describe('ItemsController', () => {
let controller: ItemsController;
let service: ItemsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [ItemsController],
providers: [
{
provide: ItemsService,
useValue: {
findAll: jest.fn().mockResolvedValue([{ name: 'Test item' }]),
},
},
],
}).compile();
controller = module.get(ItemsController);
service = module.get(ItemsService);
});
it('should return an array of items', async () => {
expect(await controller.findAll()).toEqual([{ name: 'Test item' }]);
});
});
For more details on testing and mocking, refer to the Nest JS testing documentation.
Deploying a Nest JS Application
In this section of the Nest JS tutorial, we will cover how to prepare and deploy your Nest JS application for production using Heroku and configure CI/CD pipelines.
Preparing the Application for Production
- Environment Variables: Use a
.env
file to manage environment-specific settings. - Production Build: Modify
package.json
to include a production build script:
"scripts": {
"start:prod": "node dist/main"
}
3. Logging and Monitoring: Use middleware to log and monitor application performance..
Deploying to Heroku
- Install Heroku CLI: Follow the instructions on the Heroku CLI documentation to install the Heroku CLI.
- Login to Heroku: Use the command:
heroku login
3. Create a Heroku Application: Inside your project directory, run:
heroku create
4. Deploy the Application: Push your code to Heroku:
git push heroku main
5. Configure Environment Variables: Set environment variables on Heroku using the CLI or dashboard.
6. Open the Application: After deployment, open your app using:
heroku open
For more details, refer to the Heroku Node.js documentation.
Configuring CI/CD Pipelines
- GitHub Actions: Create a GitHub Actions workflow file in
.github/workflows/main.yml
:
name: CI/CD Pipeline
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- run: npm install
- run: npm run build
- run: npm test
- name: Deploy to Heroku
env:
HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
run: |
git remote add heroku https://git.heroku.com/your-heroku-app.git
git push heroku main
2. Configure Secrets: Add HEROKU_API_KEY
to your GitHub repository secrets.
For detailed setup, refer to the GitHub Actions documentation.
Advanced Topics
In this part of the Nest JS tutorial, we will explore some advanced topics to enhance your understanding and capabilities with Nest JS, including WebSockets, GraphQL, and microservices.
WebSockets and Real-Time Communication
WebSockets enable real-time, bidirectional communication between clients and servers. Nest JS provides built-in support for WebSockets.
Installing Dependencies:
npm install @nestjs/websockets @nestjs/platform-socket.io
Creating a WebSocket Gateway:
import { WebSocketGateway, WebSocketServer, SubscribeMessage, MessageBody } from '@nestjs/websockets';
import { Server } from 'socket.io';
@WebSocketGateway()
export class AppGateway {
@WebSocketServer()
server: Server;
@SubscribeMessage('message')
handleMessage(@MessageBody() message: string): void {
this.server.emit('message', message);
}
}
For more details, refer to the Nest JS documentation on WebSockets.
GraphQL Integration
GraphQL is a query language for APIs that provides a more efficient and powerful alternative to REST.
Installing Dependencies:
npm install @nestjs/graphql graphql-tools graphql apollo-server-express
Setting Up GraphQL Module:
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { join } from 'path';
@Module({
imports: [
GraphQLModule.forRoot({
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
}),
],
})
export class AppModule {}
Creating a Resolver:
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { ItemsService } from './items.service';
import { CreateItemDto } from './dto/create-item.dto';
import { Item } from './item.entity';
@Resolver(of => Item)
export class ItemsResolver {
constructor(private itemsService: ItemsService) {}
@Query(returns => [Item])
items() {
return this.itemsService.findAll();
}
@Mutation(returns => Item)
createItem(@Args('createItemDto') createItemDto: CreateItemDto) {
return this.itemsService.create(createItemDto);
}
}
For more details, refer to the Nest JS documentation on GraphQL.
Microservices with Nest JS
Microservices architecture allows the development of highly scalable and maintainable applications by breaking down the system into smaller, independent services.
Installing Dependencies:
npm install @nestjs/microservices
Creating a Microservice:
import { NestFactory } from '@nestjs/core';
import { Transport, MicroserviceOptions } from '@nestjs/microservices';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.createMicroservice(AppModule, {
transport: Transport.TCP,
});
app.listen(() => console.log('Microservice is listening'));
}
bootstrap();
Defining a Microservice Controller:
import { Controller } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@MessagePattern({ cmd: 'sum' })
accumulate(data: number[]): number {
return this.appService.accumulate(data);
}
}
For more details, refer to the Nest JS documentation on Microservices.
Nest JS with Frontend Frameworks
In this section of the Nest JS tutorial, we will discuss how to integrate Nest JS with popular frontend frameworks like Angular, React, and Vue.
Integrating Nest JS with Angular
Angular and Nest JS can be seamlessly integrated due to their shared use of TypeScript.
- Set Up Angular Application: Create an Angular application using Angular CLI:
ng new angular-app
2. Nest JS Backend: Ensure your Nest JS application is running.
3. HTTP Client Setup: Use Angular’s HttpClientModule
to communicate with the Nest JS backend:
import { HttpClientModule } from '@angular/common/http';
Service to Fetch Data:
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class DataService {
constructor(private http: HttpClient) {}
getData() {
return this.http.get('http://localhost:3000/items');
}
}
For more detailed steps, refer to the Angular documentation.
Integrating Nest JS with React
React is a popular JavaScript library for building user interfaces.
- Set Up React Application: Create a React application using Create React App:
npx create-react-app react-app
2. Nest JS Backend: Ensure your Nest JS application is running.
3. Fetch Data from Nest JS:
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const App = () => {
const [items, setItems] = useState([]);
useEffect(() => {
axios.get('http://localhost:3000/items')
.then(response => {
setItems(response.data);
});
}, []);
return (
Items
{items.map(item => (
- {item.name}
))}
);
};
export default App;
For more detailed steps, refer to the React documentation.
Integrating Nest JS with Vue
Vue.js is a progressive JavaScript framework for building user interfaces.
- Set Up Vue Application: Create a Vue application using Vue CLI:
vue create vue-app
2. Nest JS Backend: Ensure your Nest JS application is running.
3. Fetch Data from Nest JS:
Items
- {{ item.name }}
For more detailed steps, refer to the Vue.js documentation.
Performance Optimization
In this section of the Nest JS tutorial, we will discuss various strategies to optimize the performance of your Nest JS applications, including caching, optimizing database queries, and implementing load balancing and scaling.
Caching Strategies
Caching can significantly improve the performance of your application by reducing the load on your database and speeding up response times.
- Install Cache Module:
npm install cache-manager
2. Configure Cache Module:
import { Module, CacheModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [CacheModule.register()],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
3. Use Cache in Service:
import { Injectable, Cacheable, CacheInterceptor, UseInterceptors } from '@nestjs/common';
@Injectable()
export class ItemsService {
@UseInterceptors(CacheInterceptor)
@Cacheable()
findAll() {
// Your logic here
}
}
For more details, refer to the Nest JS documentation on Caching.
Optimizing Database Queries
Efficient database queries can greatly enhance the performance of your application. Here are some tips:
- Indexes: Ensure that your database tables have appropriate indexes.
- Lazy Loading and Eager Loading: Use these techniques to load only the necessary data.
- Query Optimization: Use efficient SQL queries and avoid unnecessary joins.
Example using TypeORM:
import { Entity, PrimaryGeneratedColumn, Column, Index } from 'typeorm';
@Entity()
@Index(['name', 'price'])
export class Item {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column('decimal')
price: number;
}
Refer to the TypeORM documentation for more details on query optimization.
Load Balancing and Scaling
Load balancing and scaling ensure that your application can handle high traffic and provide high availability.
- Horizontal Scaling: Add more instances of your application.
- Load Balancer: Use a load balancer like NGINX or HAProxy to distribute traffic across multiple instances.
Example NGINX Configuration:
http {
upstream myapp {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
server {
listen 80;
location / {
proxy_pass http://myapp;
}
}
}
For more information on load balancing, refer to the NGINX documentation.
Security Best Practices
In this part of the Nest JS tutorial, we will discuss security best practices to protect your application against common web vulnerabilities.
Protecting Against Common Web Vulnerabilities
- SQL Injection: Use parameterized queries and ORM libraries like TypeORM to prevent SQL injection attacks.
- Cross-Site Scripting (XSS): Use libraries like
DOMPurify
to sanitize user inputs and outputs. - Cross-Site Request Forgery (CSRF): Implement CSRF protection mechanisms such as tokens.
Example of XSS Protection:
import * as DOMPurify from 'dompurify';
function sanitize(input: string): str
This post first appeared on IT Code Stuff - Insightful Programming Blogs And Tutorials, please read the originial post: here