How To Add JWT-Based Authentication in NestJS
How To Add JWT-Based Authentication in NestJS
Prerequisites
This tutorial is a hands-on demo. To follow along, you must have the
following:
MongoDB compass
$ npm i -g @nestjs/cli
You will see options about which package manager you prefer to
install. I used npm.
Go to src/app.module.ts file:
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost:27017/auth-workshop')
UsersModule,
AuthModule,
ConfigModule.forRoot({
envFilePath: '.env',
isGlobal: true,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
@Module({})
The imports array specifies the module that this module depends
on. We have the MongooseModule.forRoot('') for database
connection using mongoose, ConfigModule.forRoot import sets up
the configuration module to read from a .env file:
import { MongooseModule } from '@nestjs/mongoose';
import { UsersModule } from './users/users.module';
import { AuthModule } from './auth/auth.module';
import { ConfigModule } from '@nestjs/config';
imports: [
MongooseModule.forRoot('mongodb://localhost:27017/auth-workshop')
UsersModule,
AuthModule,
ConfigModule.forRoot({
envFilePath: '.env',
isGlobal: true,
}),
]
controllers: [AppController]
providers: [AppService]
@Prop()
password: string;
}
export const UserSchema = SchemaFactory.createForClass(User);
@Module({
imports: [MongooseModule.forFeature([{ name: User.name, schema:
UserSchema }])],
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
Lastly, define the DTO (data transfer object). This object transfers
data between systems. It is a simple object that contains only data
and has no behaviour.
@Injectable()
export class UsersService {
constructor(@InjectModel(User.name) private userModel:
Model<User>) {}
async createUsers(createUserDto: CreateUserDto) {
const user = await this.userModel.create(createUserDto);
return user.save();
}
async findAllUsers() {
const users = this.userModel.find();
return users;
}
async findUser(id: number) {
const user = await this.userModel.findById(id);
if (!user) throw new NotFoundException('could not find the user')
return user;
}
updateUser(id: number, updateUserDto: UpdateUserDto) {
return this.userModel.findByIdAndUpdate(id, updateUserDto, { new
});
}
removeUser(id: number) {
return this.userModel.findByIdAndDelete(id);
};
}
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
async create(@Body() createUserDto: CreateUserDto) {
return await this.usersService.createUsers(createUserDto);
}
@Get()
async findAll() {
return await this.usersService.findAllUsers();
}
@Get(':id')
async findOne(@Param('id') id: string) {
return await this.usersService.findOneUser(+id);
}
@Patch(':id')
async update(@Param('id') id: string, @Body() updateUserDto:
UpdateUserDto) {
return await this.usersService.updateUser(+id, updateUserDto);
}
@Delete(':id')
async remove(@Param('id') id: string) {
return await this.usersService.removeUser(+id);
}
}
We created RESTful APIs using mongoose . The methods include:
This service will have two methods: signup and login . The signup
method will take a signup request object containing name , email ,
and password ),and the login method will take a login request
object containing email and password . Both methods will return a
JWT.
Then define the DTO for both signup and login. Go to the dto folder
in the src/auth/dto folder:
signup.dto.ts:
login.dto.ts:
JWT_SECRET=secret
JWT_EXPIRES=3d
@Module({
imports: [
PassportModule.register({ defaultStrategy: 'jwt' }),
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => {
return {
secret: config.get<string>('JWT_SECRET'),
signOptions: {
expiresIn: config.get<string | number>('JWT_EXPIRES'),
},
};
},
}),
MongooseModule.forFeature([{ name: 'User', schema: UserSchema }])
],
controllers: [AuthController],
providers: [AuthService],
})
export class AuthModule {}
@Injectable()
export class AuthService {
constructor(
@InjectModel(User.name) private userModel: Model<User>,
private jwtService: JwtService,
private configService: ConfigService,
) {}
await user.save();
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('signup')
signUp(@Body() signupDto: SignUpDto) {
return this.authService.signUp(signupDto);
}
@Post('login')
signin(@Body() loginDto: LoginDto) {
return this.authService.login(loginDto);
}
}
With these steps, you’ve implemented a basic user login and signup
in your application. In the next sections, we’ll test the login and
signup routes.
Testing in Postman
Now that we've set up our endpoints, it's time to put them to the
test. For this example, I'll be using Postman as my API client, but feel
free to use any tool or client that suits your needs. Let's see our API
in action!
Conclusion
Congratulations, you've successfully implemented comprehensive
authentication using NestJS, Mongoose, and Passport. We designed
a secure signup and login process, and generated JSON Web Tokens
(JWTs).