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.
If you enjoy my content and would like to support my work, you can buy me a coffee. Your support is greatly appreciated!
Disclaimer: This content is generated by AI.
Top comments (0)