Introduction
Authentication is a core feature of modern web applications. This step-by-step guide will help you build a robust login and registration system using NestJS, TypeORM, and PostgreSQL. Whether you're a beginner or someone looking to solidify their understanding, this guide walks you through everything from project setup to implementing secure authentication with global database configurations.
What Will You Learn?
- Setting up a NestJS project
- Configuring TypeORM with a PostgreSQL database
- Building reusable entities and services
- Securing passwords with bcrypt
- Using JWT for token-based authentication
- Handling validation and error scenarios
Setting Up Your Environment
1. Install NestJS CLI
First, ensure you have the NestJS CLI installed:
npm install -g @nestjs/cli
2. Create a New NestJS Project
Generate a new project:
nest new nest-auth-system
Choose npm or yarn for package management during the setup.
3. Install Required Dependencies
npm install @nestjs/typeorm typeorm pg bcrypt jsonwebtoken @nestjs/jwt @nestjs/config class-validator class-transformer
Setting Up PostgreSQL with TypeORM
1. Create the Database
Using PostgreSQL, create a database for your project:
CREATE DATABASE nest_auth_db;
2. Configure TypeORM Globally
Update your app.module.ts
to configure TypeORM globally:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
type: 'postgres',
host: configService.get<string>('DB_HOST', 'localhost'),
port: +configService.get<number>('DB_PORT', 5432),
username: configService.get<string>('DB_USERNAME', 'postgres'),
password: configService.get<string>('DB_PASSWORD', 'password'),
database: configService.get<string>('DB_DATABASE', 'nest_auth_db'),
autoLoadEntities: true,
synchronize: true, // Disable in production
}),
}),
],
})
export class AppModule {}
Add your database credentials to a .env
file:
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=postgres
DB_PASSWORD=password
DB_DATABASE=nest_auth_db
JWT_SECRET=your_jwt_secret
Building the User Entity
1. Generate a User Module
nest g module users
nest g service users
nest g controller users
2. Create the User Entity
In users.entity.ts
:
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
username: string;
@Column({ unique: true })
email: string;
@Column()
password: string;
}
Creating Authentication Logic
1. Generate the Auth Module
nest g module auth
nest g service auth
nest g controller auth
2. Implement Registration Logic
In auth.service.ts
:
import { Injectable, BadRequestException } from '@nestjs/common';
import { UsersService } from '../users/users.service';
import * as bcrypt from 'bcrypt';
@Injectable()
export class AuthService {
constructor(private readonly usersService: UsersService) {}
async register(username: string, email: string, password: string) {
const existingUser = await this.usersService.findByEmail(email);
if (existingUser) {
throw new BadRequestException('Email already in use');
}
const hashedPassword = await bcrypt.hash(password, 10);
return this.usersService.create({ username, email, password: hashedPassword });
}
}
3. Add UserService Methods
In users.service.ts
:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {}
async create(data: Partial<User>): Promise<User> {
return this.userRepository.save(data);
}
async findByEmail(email: string): Promise<User | undefined> {
return this.userRepository.findOne({ where: { email } });
}
}
4. Create the Register Endpoint
In auth.controller.ts
:
import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('register')
async register(
@Body('username') username: string,
@Body('email') email: string,
@Body('password') password: string,
) {
return this.authService.register(username, email, password);
}
}
Adding Login Functionality
1. Implement Login Logic
In auth.service.ts
:
import { UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(
private readonly usersService: UsersService,
private readonly jwtService: JwtService,
) {}
async login(email: string, password: string) {
const user = await this.usersService.findByEmail(email);
if (!user) {
throw new UnauthorizedException('Invalid credentials');
}
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
throw new UnauthorizedException('Invalid credentials');
}
const token = this.jwtService.sign({ id: user.id });
return { token };
}
}
2. Create the Login Endpoint
In auth.controller.ts
:
@Post('login')
async login(
@Body('email') email: string,
@Body('password') password: string,
) {
return this.authService.login(email, password);
}
Securing Routes with JWT
1. Add JWT Strategy
Generate a guard:
nest g guard jwt
In jwt.strategy.ts
:
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(),
secretOrKey: process.env.JWT_SECRET,
});
}
async validate(payload: any) {
return { userId: payload.id };
}
}
2. Protect Routes
In your controller:
import { UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from './jwt.guard';
@Controller('profile')
@UseGuards(JwtAuthGuard)
export class ProfileController {
@Get()
getProfile(@Req() req) {
return req.user;
}
}
FAQs
Why Use TypeORM with NestJS?
TypeORM integrates seamlessly with NestJS and provides powerful features for database management.
How Secure Is JWT Authentication?
JWT is secure if implemented correctly with a strong secret and proper token expiration.
By following this comprehensive guide, you’ve built a robust login and registration system with NestJS, TypeORM, and PostgreSQL. This scalable solution can be enhanced further with features like password resets, user roles, and more!
Top comments (0)