DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Secure Your API With JWT: Kong OpenID Connect
  • Navigating the API Seas: A Product Manager's Guide to Authentication
  • Securing RESTful Endpoints
  • What Is API-First?

Trending

  • Kubeflow: Driving Scalable and Intelligent Machine Learning Systems
  • Beyond Linguistics: Real-Time Domain Event Mapping with WebSocket and Spring Boot
  • Microsoft Azure Synapse Analytics: Scaling Hurdles and Limitations
  • A Guide to Developing Large Language Models Part 1: Pretraining
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. Securing REST APIs With Nest.js: A Step-by-Step Guide

Securing REST APIs With Nest.js: A Step-by-Step Guide

Learn to set up and secure a REST API with Nest.js. This guide covers user authentication, JWT management, and API security best practices.

By 
Vishal Diyora user avatar
Vishal Diyora
·
Dec. 05, 23 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
7.0K Views

Join the DZone community and get the full member experience.

Join For Free

This guide walks you through setting up a secure REST API using Nest.js in Node.js. We'll create a login system with JWTs and implement best practices for token management and API security.

Setting Up Nest.js

Prerequisites: Node.js installed.

  • Installation:
Shell
 
npm i -g @nestjs/cli


  • Creating a New Project:
Shell
 
nest new nestjs-secure-api


Building a Login API

  • Creating a User Module: Generate the module, controller, and service:

Shell
 
cd nestjs-secure-api
nest generate module users
nest generate controller users
nest generate service users


  • User Authentication with Passport.js: Install necessary packages:
Shell
 
npm install @nestjs/passport passport passport-local
npm install @nestjs/jwt passport-jwt
npm install @types/passport-local @types/passport-jwt --save-dev
npm install class-validator bcrypt express-rate-limit


In users.service.ts, implement the JWT strategy and authentication logic. (For brevity, this example assumes you have user retrieval and verification logic):

TypeScript
 
import { Injectable } from '@nestjs/common';
import * as bcrypt from 'bcrypt';

export type User = any;

@Injectable()
export class UsersService {
  private readonly users: User[];

  constructor() {
    this.users = [];
  }

  async findOne(username: string): Promise<User | undefined> {
    return this.users.find(user => user.username === username);
  }

  async create(username: string, pass: string): Promise<void> {
    const hashedPassword = await bcrypt.hash(pass, 10);
    this.users.push({ username, password: hashedPassword });
    console.log(`User is created with usernam ${username} and encrypted password ${hashedPassword}`)
  }
}


  • Creating Login and Registration Endpoints:
    In users.controller.ts:
TypeScript
 
import { Controller, Post, Body, BadRequestException } from '@nestjs/common';
import { UsersService } from './users.service';
import { AuthService } from './auth.service';
import * as bcrypt from 'bcrypt';
import { CreateUserDto } from './create-user.dto';

@Controller('users')
export class UsersController {
  constructor(
    private usersService: UsersService,
    private authService: AuthService
  ) {}

  @Post('register')
  async register(@Body() body: CreateUserDto) {
    const { username, password } = body;
    const userExists = await this.usersService.findOne(username);
    if (userExists) {
      throw new BadRequestException('Username already exists');
    }
    await this.usersService.create(username, password);
    return { message: 'User created successfully' };
  }

  @Post('login')
  async login(@Body()  body: CreateUserDto) {
    const { username, password } = body;
    const user = await this.usersService.findOne(username);
    if (!user) {
      throw new BadRequestException('Invalid credentials');
    }
    const isMatch = await bcrypt.compare(password, user.password);
    if (!isMatch) {
      throw new BadRequestException('Invalid credentials');
    }
    const jwtToken = this.authService.login(user);
    console.log(`User ${username} is succesfully loggedIn.`)
    return jwtToken;
  }
}


  • Token Storage and Encryption: 

In auth.service.ts, add token hashing:

TypeScript
 
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
  constructor(
    private jwtService: JwtService
  ) {}

  async login(user: any) {
    const payload = { username: user.username, sub: user.userId };
    return {
      // for now secret is set as 'secret' it should be env variable but to make things simple I'm adding a string here. 
      access_token: this.jwtService.sign(payload, { secret: `${process.env.JWT_SECRET}` || 'secret' }), 
    };
  }
}


  • Token Security Best Practices: Ensure you use HTTPS. Implement token expiration and refresh tokens.

Securing the API

  • LoggerMiddleware: In logger.middleware.ts implement the logging middleware:
TypeScript
 
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(`[${new Date().toISOString()}] ${req.method} ${req.originalUrl}`);
    next();
  }
}


  • Middleware and Guards: In app.module.ts, implement middleware for request logging:
TypeScript
 
import { MiddlewareConsumer, Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
import { LoggerMiddleware } from './logger.middleware';

@Module({
  imports: [UsersModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware) // assuming you want to add some some loggerMiddleware then you can add it here
      .forRoutes('*');
  }
}


  • Input Validation and Rate Limiting: Use DTOs for input validation. In create-user.dto.ts:
TypeScript
 
import { IsNotEmpty, IsString } from 'class-validator';
export class CreateUserDto {
    @IsNotEmpty()
    @IsString()
    username: string;
  
    @IsNotEmpty()
    @IsString()
    password: string;
  }
  


  • Set up rate limits in main.ts:
TypeScript
 
import * as rateLimit from 'express-rate-limit';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100, // limit each IP to 100 requests per windowMs
  }));
  await app.listen(3000);
}
bootstrap();


Conclusion

This guide has covered the essentials of setting up a secure REST API with Nest.js, from user authentication to token management and rate limiting. Following these steps will give you a robust foundation for building secure, scalable web applications.

Running the Application

To run the application, execute:

Shell
 
npm run start


Access the API at http://localhost:3000/ 

Example of API validation:
API Validation

API Validation

API Validation


The complete code is available in the GitHub repository.

Note: This article provides a starting framework. Additional security measures and functionalities may be necessary depending on your application's specific needs. Keep your dependencies updated and regularly review your security practices.

API REST authentication JWT (JSON Web Token) Middleware security

Opinions expressed by DZone contributors are their own.

Related

  • Secure Your API With JWT: Kong OpenID Connect
  • Navigating the API Seas: A Product Manager's Guide to Authentication
  • Securing RESTful Endpoints
  • What Is API-First?

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • [email protected]

Let's be friends: