Creating an authentication and authorization feature in a NestJS GraphQL API involves several steps. Here’s a step-by-step guide:
Step 1: Set Up a New NestJS Project
- Install Nest CLI:
npm install -g @nestjs/cli
- Create a New Project:
nest new project-name
- Navigate to the Project Directory:
cd project-name
Step 2: Install Required Packages
- Install Necessary Packages:
npm install @nestjs/graphql graphql apollo-server-express @nestjs/jwt passport @nestjs/passport passport-jwt bcryptjs
Step 3: Set Up the GraphQL Module
-
Configure GraphQL Module:
Open
src/app.module.ts
and configure the GraphQL module:
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { TypeOrmModule } from '@nestjs/typeorm';
import { join } from 'path';
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
@Module({
imports: [
GraphQLModule.forRoot({
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
}),
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'password',
database: 'test',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
}),
AuthModule,
UsersModule,
],
})
export class AppModule {}
Step 4: Create the User Entity
- Create a Directory Structure:
mkdir -p src/users src/auth
-
Create the User Entity:
Create
src/users/user.entity.ts
:
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
import { ObjectType, Field, ID } from '@nestjs/graphql';
@ObjectType()
@Entity()
export class User {
@Field(() => ID)
@PrimaryGeneratedColumn()
id: number;
@Field()
@Column()
username: string;
@Field()
@Column()
email: string;
@Column()
password: string;
}
Step 5: Create the User Service
-
Implement Service Logic:
Open
src/users/users.service.ts
and implement the service methods:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
import { CreateUserInput } from './dto/create-user.input';
import * as bcrypt from 'bcryptjs';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private usersRepository: Repository<User>,
) {}
async findOneByUsername(username: string): Promise<User | undefined> {
return this.usersRepository.findOne({ username });
}
async findOneByEmail(email: string): Promise<User | undefined> {
return this.usersRepository.findOne({ email });
}
async create(createUserInput: CreateUserInput): Promise<User> {
const hashedPassword = await bcrypt.hash(createUserInput.password, 10);
const user = this.usersRepository.create({ ...createUserInput, password: hashedPassword });
return this.usersRepository.save(user);
}
}
Step 6: Create the User Resolver
-
Create the User Resolver:
Create
src/users/users.resolver.ts
:
import { Resolver, Mutation, Args } from '@nestjs/graphql';
import { UsersService } from './users.service';
import { User } from './user.entity';
import { CreateUserInput } from './dto/create-user.input';
@Resolver(of => User)
export class UsersResolver {
constructor(private readonly usersService: UsersService) {}
@Mutation(() => User)
async createUser(@Args('createUserInput') createUserInput: CreateUserInput): Promise<User> {
return this.usersService.create(createUserInput);
}
}
-
Create DTO for User Input:
Create
src/users/dto/create-user.input.ts
:
import { InputType, Field } from '@nestjs/graphql';
@InputType()
export class CreateUserInput {
@Field()
username: string;
@Field()
email: string;
@Field()
password: string;
}
Step 7: Create the User Module
-
Create the User Module:
Open
src/users/users.module.ts
and update it:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersService } from './users.service';
import { UsersResolver } from './users.resolver';
import { User } from './user.entity';
@Module({
imports: [TypeOrmModule.forFeature([User])],
providers: [UsersService, UsersResolver],
exports: [UsersService],
})
export class UsersModule {}
Step 8: Implement Authentication
-
Create Auth Service:
Create
src/auth/auth.service.ts
:
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UsersService } from '../users/users.service';
import * as bcrypt from 'bcryptjs';
@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService,
) {}
async validateUser(username: string, pass: string): Promise<any> {
const user = await this.usersService.findOneByUsername(username);
if (user && await bcrypt.compare(pass, user.password)) {
const { password, ...result } = user;
return result;
}
return null;
}
async login(user: any) {
const payload = { username: user.username, sub: user.id };
return {
access_token: this.jwtService.sign(payload),
};
}
}
-
Create Auth Module:
Open
src/auth/auth.module.ts
and configure it:
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module';
import { JwtStrategy } from './jwt.strategy';
@Module({
imports: [
UsersModule,
PassportModule,
JwtModule.register({
secret: 'secretKey', // Replace with your own secret
signOptions: { expiresIn: '60m' },
}),
],
providers: [AuthService, JwtStrategy],
exports: [AuthService],
})
export class AuthModule {}
-
Create JWT Strategy:
Create
src/auth/jwt.strategy.ts
:
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { UsersService } from '../users/users.service';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private usersService: UsersService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'secretKey', // Replace with your own secret
});
}
async validate(payload: any) {
return { userId: payload.sub, username: payload.username };
}
}
Step 9: Create Auth Resolver
-
Create Auth Resolver:
Create
src/auth/auth.resolver.ts
:
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { AuthService } from './auth.service';
import { AuthInput } from './dto/auth.input';
import { AuthResponse } from './dto/auth.response';
@Resolver()
export class AuthResolver {
constructor(private readonly authService: AuthService) {}
@Mutation(() => AuthResponse)
async login(@Args('authInput') authInput: AuthInput) {
const user = await this.authService.validateUser(authInput.username, authInput.password);
if (!user) {
throw new Error('Invalid credentials');
}
return this.authService.login(user);
}
}
-
Create DTOs for Auth Input and Response:
Create
src/auth/dto/auth.input.ts
:
import { InputType, Field } from '@nestjs/graphql';
@InputType()
export class AuthInput {
@Field()
username: string;
@Field()
password: string;
}
Create src/auth/dto/auth.response.ts
:
import { ObjectType, Field } from '@nestjs/graphql';
@ObjectType()
export class AuthResponse {
@Field()
access_token: string;
}
Step
10: Protect Routes with Auth Guard
-
Create GQL Auth Guard:
Create
src/auth/gql-auth.guard.ts
:
import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { GqlExecutionContext } from '@nestjs/graphql';
@Injectable()
export class GqlAuthGuard extends AuthGuard('jwt') {
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req;
}
}
-
Apply Guard to Resolvers:
Update
src/users/users.resolver.ts
to protect routes:
import { UseGuards } from '@nestjs/common';
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { UsersService } from './users.service';
import { User } from './user.entity';
import { CreateUserInput } from './dto/create-user.input';
import { GqlAuthGuard } from '../auth/gql-auth.guard';
@Resolver(of => User)
export class UsersResolver {
constructor(private readonly usersService: UsersService) {}
@UseGuards(GqlAuthGuard)
@Query(() => [User])
async users(): Promise<User[]> {
return this.usersService.findAll();
}
@Mutation(() => User)
async createUser(@Args('createUserInput') createUserInput: CreateUserInput): Promise<User> {
return this.usersService.create(createUserInput);
}
}
Step 11: Run the Application
- Start the NestJS Application:
npm run start:dev
Step 12: Test the GraphQL API
-
Access the GraphQL Playground:
Navigate to
http://localhost:3000/graphql
to access the GraphQL playground and test your API by running queries and mutations.
Example GraphQL Mutations and Queries
- Create a New User:
mutation {
createUser(createUserInput: { username: "john", email: "john@example.com", password: "password" }) {
id
username
email
}
}
- Login and Get JWT:
mutation {
login(authInput: { username: "john", password: "password" }) {
access_token
}
}
- Query All Users (Protected):
{
users {
id
username
email
}
}
This guide provides a foundational approach to implementing authentication and authorization in a NestJS GraphQL API. You can further expand and customize it based on your application's requirements.
Support My Work ❤️
If you enjoy my content and find it valuable, consider supporting me by buying me a coffee. Your support helps me continue creating and sharing useful resources. Thank you!
Connect with Me 🌍
Let’s stay connected! You can follow me or reach out on these platforms:
🔹 YouTube – Tutorials, insights & tech content
🔹 LinkedIn – Professional updates & networking
🔹 GitHub – My open-source projects & contributions
🔹 Instagram – Behind-the-scenes & personal updates
🔹 X (formerly Twitter) – Quick thoughts & tech discussions
I’d love to hear from you—whether it’s feedback, collaboration ideas, or just a friendly hello!
Disclaimer
This content has been generated with the assistance of AI. While I strive for accuracy and quality, please verify critical information independently.
Top comments (0)