← Back to list

Step-by-Step: User Authentication/Login and Register API Using NestJS (NodeJS), MongoDB, and…

NOTE: Focus on the code, not my grammar 😅💻 — the compiler doesn’t care, and neither should you!

Sourabh wadhwa · 2025-06-20 17:41 · 1 claps · 5.2 min read
#nestjs-tutorial #nextjs-authentication #nestjs-login-register-api
Open on Medium ↗
Wiki topics: 🌐 · Web Development ⏱️ · Productivity 🥊 · Combat Sports

Step-by-Step: User Authentication/Login and Register API Using NestJS (NodeJS), MongoDB, and Swagger ,JWT

NOTE: Focus on the code, not my grammar 😅💻 — the compiler doesn’t care, and neither should you!

  1. Install Node.js (with nvm for version management) Visit this link for detailed information:

[embed]Install nvm & Node.js Install nvmmedium.com

Install the NestJS Framework Visit this link for detailed information

[embed]Install Node Backend with Nest JS Getting Started with Node.js & NestJSmedium.com

Let’s start User Authentication API Using NestJS (NodeJS), MongoDB, and Swagger

3.1 Setup Mongodb Database 3.1 Create Mongodb Cluster: Visit this link for detailed information to make cloud cluster and get cluster url

[embed]Step-by-Step Guide to Setting Up MongoDB (Cloud) for Your Project Go to MongoDB Atlasmedium.com

3.2 Install dependencies

npm install @nestjs/mongoose mongoose
npm install @nestjs/config

3.3 Define Cluster Variable in ENV

Go to .env file and define the cluster variable

MONGO_URI=mongodb+srv://username:some-id-autogenerated.mongodb.net/

3.4 Configure MongoDB in AppModule

In src/app.module.ts: 3.4.1:

import { MongooseModule } from '@nestjs/mongoose'
import { ConfigModule } from '@nestjs/config';

3.4.2 Under Module Imports array add the db connection as an array item as given below:


@Module({imports: [
ConfigModule.forRoot({ isGlobal: true }),
MongooseModule.forRoot(process.env.MONGO_URI),
]

Database connection is almost done as per the above instructions, now let's start with the user auth api feature

4. Install required packages:

npm install class-validator class-transformer
npm install @nestjs/passport passport passport-local
npm install @nestjs/jwt passport-jwt
npm install bcrypt
npm install - save-dev @types/bcrypt
npm i @nestjs/swagger

Note use --force after package name, if any dependancy issue

4.1 Setup Swagger

Go to src/main.cs

import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; 
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // ✅ Enable Validation Globally (if not already)
  app.useGlobalPipes(new ValidationPipe({
    whitelist: true,
    forbidNonWhitelisted: true,
    transform: true,
    forbidUnknownValues: true,
  }));
 // ✅ Define the swagger
  const config = new DocumentBuilder()
    .setTitle('User Auth API')
    .setDescription('NestJS Auth Example with MongoDB')
    .setVersion('1.0')
    .addTag('Auth')
    .build();
  const documentFactory = () => SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, documentFactory);

  await app.listen(3000);
}
bootstrap();
Open http://localhost:3000/api to test Swagger.
  1. Create DTO file for validate fields 5.1 : src/users/dto/create-user.dto.ts
import {  IsEmail, IsNotEmpty,  IsOptional,  IsString,  IsDateString,  IsEnum,
  MinLength,  IsNumberString,} from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class CreateUserDto {
  @ApiProperty()
  @IsNotEmpty({ message: 'First name is required' })
  @MinLength(1)
  @IsString()
  firstName: string;

  @ApiProperty()
  @IsNotEmpty({ message: 'Last name is required' })
  @MinLength(1)
  @IsString()
  lastName: string;

  @ApiProperty()
  @IsNotEmpty({ message: 'Email is required' })
  @IsEmail({}, { message: 'Invalid email format' })
  email: string;

  @ApiProperty()
  @IsNotEmpty({ message: 'Phone is required' })
  @IsNumberString({}, { message: 'Phone must contain only numeric digits' }) // ✅ validate numeric only
  phone: string;


}

5.2 src/users/dto/login.dto.ts

import { IsEmail, IsNotEmpty } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class LoginDto {

  @ApiProperty()
  @IsEmail()
  email: string;

  @ApiProperty()
  @IsNotEmpty()
  password: string;
}

5.3 Create user schema : src/users/schemas/user.schema.ts

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
import { Types } from 'mongoose';

export type UserDocument = User & Document;

@Schema({ timestamps: true }) // Automatically adds createdAt and updatedAt
export class User {
/*   @Prop({ type: Types.ObjectId })
  _id: Types.ObjectId; */ // Comment this if using mongodb

  @Prop({ required: true })
  firstName: string;

  @Prop({ required: true })
  lastName: string;

  @Prop({ required: true, unique: true, lowercase: true, trim: true })
  email: string;

 @Prop({ required: true, unique: true })
  phone: string;

}
export const UserSchema = SchemaFactory.createForClass(User);

6. Creating an authentication module, but before creating the authentication module, the service and controller. First, we will create the User module and the Service

$ nest g module users $ nest g service users

(nest g module users command create new module for user under src/users/users.module.js to define schema Also, auto updated the src/app.module.ts to assign module to Module function as we did for Manual for MongooseModule in the previous step [3.4.2].)

6.1 src/users/users.module.ts

import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { User, UserSchema } from './schemas/user.schema'; // Import schema
import { MongooseModule } from '@nestjs/mongoose'; //  Import MongooseModule

@Module({
  imports: [MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])], // Define schema
  providers: [UsersService],
  exports: [UsersService], // ✅ Add Service for export 

})
export class UsersModule { }

6.2 Go to src/users/users.service.ts

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { User, UserDocument } from './schemas/user.schema';
import * as bcrypt from 'bcrypt';
import { CreateUserDto } from './dto/create-user.dto';

@Injectable()
export class UsersService {

    // ✅ Define constructor
    constructor(@InjectModel(User.name) private userModel: Model<UserDocument>) { }

    // ✅ Define find user function from database
    async findByEmail(email: string): Promise<User | undefined> {
        return this.userModel.findOne({ email });
    }

    // ✅ Define create user function to store register data in to the database
    async create(dto: CreateUserDto): Promise<User> {
        const passwordHash = await bcrypt.hash(dto.password, 10);

        const createdUser = new this.userModel({
            ...dto,
            passwordHash,
        });

        return createdUser.save();
    }
}
  1. We’ll start by generating an AuthModule and in it, an AuthService and an AuthController. We'll use the AuthService to implement the authentication logic, and the AuthController to expose the authentication endpoints.
$ nest g module auth
$ nest g controller auth
$ nest g service auth

7.1 : Let's create JWT_SECRET_KEY for the auth token handling. Run this in your terminal:

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

Output :

4a1d8f1a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234

Go to .env file:

JWT_SECRET_KEY=4a1d8f1a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234

7.2: Go to src/auth/auth.module.ts

import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';

import { UsersModule } from '../users/users.module';
import { JwtModule } from '@nestjs/jwt';
import { ConfigModule, ConfigService } from '@nestjs/config';

// import { JwtStrategy } from './jwt.strategy'; // Define later

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    UsersModule,
    JwtModule.registerAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => ({
        secret: configService.get<string>('JWT_SECRET_KEY'), // ✅ ENV key
        signOptions: { expiresIn: '1d' },
      }),
    }),
  ],
  controllers: [AuthController],
  providers: [AuthService]
})
export class AuthModule {}

7.3 Go to src/auth/auth.service.ts

import { Injectable, UnauthorizedException, BadRequestException } from '@nestjs/common';

import { UsersService } from '../users/users.service';
import * as bcrypt from 'bcrypt';
import { JwtService } from '@nestjs/jwt';
import { CreateUserDto } from '../users/dto/create-user.dto';

@Injectable()
export class AuthService {

    constructor(
        private usersService: UsersService,
        private jwtService: JwtService,
    ) { }

    async register(dto: CreateUserDto) {
        const existing = await this.usersService.findByEmail(dto.email);
        if (existing) {
            throw new BadRequestException('User already exists');
        }
        const user = await this.usersService.create(dto);
        return {
            message: 'Registration successful',

        };
    }

    private generateToken(user: any) {
        const payload = { sub: user._id, email: user.email };
        return this.jwtService.sign(payload);

    }

    async validateUser(email: string, password: string): Promise<any> {
        const user = await this.usersService.findByEmail(email);
        if (user && await bcrypt.compare(password, user.password)) {
            return user;
        }
        return null;
    }

    async login(email: string, password: string) {
        const user = await this.usersService.findByEmail(email);
        if (!user) {
            throw new UnauthorizedException('Invalid email');
        }

        const isMatch = await bcrypt.compare(password, user.passwordHash);
        if (!isMatch) {
            throw new UnauthorizedException('Invalid password');
        }
        return {
            message: 'Login successful',
            accessToken: this.generateToken(user),
        };

    }
}

7.4 Go to src/auth/auth.controller.ts

import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';
import { CreateUserDto } from '../users/dto/create-user.dto';
import { LoginDto } from '../users/dto/login.dto';

@Controller('auth')
export class AuthController {

    constructor(private authService: AuthService) { }
    @Post('register')
    register(@Body() dto: CreateUserDto) {
        return this.authService.register(dto);
    }

    @Post('login')
    login(@Body() dto: LoginDto) {
        return this.authService.login(dto.email, dto.password);
    }
}

7.5 Cross-check to make sure to imported the 'AuthModule' in app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MongooseModule } from '@nestjs/mongoose';
import { ConfigModule } from '@nestjs/config'; 
import { UsersModule } from './users/users.module';
import { AuthModule } from './auth/auth.module';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }), 
    MongooseModule.forRoot(process.env.MONGO_URI),
    UsersModule,
    AuthModule,// ✅ Make sure its added 
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule { }

✅ Done!

Now you can:

  • Visit [**http://localhost:3000/api](http://localhost:3000/api)**
  • Test your endpoints directly via Swagger UI
  • View DTOs, request bodies, and responses
  • Authorize using JWT token with the Authorize 🔒 button

If this code saved you time or effort, you can support me with a coffee :

[MY GPAY UPI ID: soruwww@okicici ]

[My Paypal : https://www.paypal.com/paypalme/SourabhWadhwa ]

💬 Have any questions or run into issues? Drop a comment — I’m happy to help you out!


메타데이터
post_id
31e4c86e46b3
slug
step-by-step-user-authentication-login-and-register-api-using-nestjs-nodejs-mongodb-and-31e4c86e46b3
url
https://medium.com/@soruwww/step-by-step-user-authentication-login-and-register-api-using-nestjs-nodejs-mongodb-and-31e4c86e46b3
canonical_url
https://medium.com/@soruwww/step-by-step-user-authentication-login-and-register-api-using-nestjs-nodejs-mongodb-and-31e4c86e46b3
author_url
https://medium.com/@soruwww
status
ok
fetched_at
2026-06-15 22:55:51