DEV Community

Nadim Chowdhury
Nadim Chowdhury

Posted on • Updated on

How to create an Authentication & Authorization feature in Nest JS GraphQL API?

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

  1. Install Nest CLI:
   npm install -g @nestjs/cli
Enter fullscreen mode Exit fullscreen mode
  1. Create a New Project:
   nest new project-name
Enter fullscreen mode Exit fullscreen mode
  1. Navigate to the Project Directory:
   cd project-name
Enter fullscreen mode Exit fullscreen mode

Step 2: Install Required Packages

  1. Install Necessary Packages:
   npm install @nestjs/graphql graphql apollo-server-express @nestjs/jwt passport @nestjs/passport passport-jwt bcryptjs
Enter fullscreen mode Exit fullscreen mode

Step 3: Set Up the GraphQL Module

  1. 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 {}
Enter fullscreen mode Exit fullscreen mode

Step 4: Create the User Entity

  1. Create a Directory Structure:
   mkdir -p src/users src/auth
Enter fullscreen mode Exit fullscreen mode
  1. 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;
   }
Enter fullscreen mode Exit fullscreen mode

Step 5: Create the User Service

  1. 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);
     }
   }
Enter fullscreen mode Exit fullscreen mode

Step 6: Create the User Resolver

  1. 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);
     }
   }
Enter fullscreen mode Exit fullscreen mode
  1. 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;
   }
Enter fullscreen mode Exit fullscreen mode

Step 7: Create the User Module

  1. 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 {}
Enter fullscreen mode Exit fullscreen mode

Step 8: Implement Authentication

  1. 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),
       };
     }
   }
Enter fullscreen mode Exit fullscreen mode
  1. 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 {}
Enter fullscreen mode Exit fullscreen mode
  1. 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 };
     }
   }
Enter fullscreen mode Exit fullscreen mode

Step 9: Create Auth Resolver

  1. 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);
     }
   }
Enter fullscreen mode Exit fullscreen mode
  1. 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;
   }
Enter fullscreen mode Exit fullscreen mode

Create src/auth/dto/auth.response.ts:

   import { ObjectType, Field } from '@nestjs/graphql';

   @ObjectType()
   export class AuthResponse {
     @Field()
     access_token: string;
   }
Enter fullscreen mode Exit fullscreen mode

Step

10: Protect Routes with Auth Guard

  1. 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;
     }
   }
Enter fullscreen mode Exit fullscreen mode
  1. 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);
     }
   }
Enter fullscreen mode Exit fullscreen mode

Step 11: Run the Application

  1. Start the NestJS Application:
   npm run start:dev
Enter fullscreen mode Exit fullscreen mode

Step 12: Test the GraphQL API

  1. 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
    }
  }
Enter fullscreen mode Exit fullscreen mode
  • Login and Get JWT:
  mutation {
    login(authInput: { username: "john", password: "password" }) {
      access_token
    }
  }
Enter fullscreen mode Exit fullscreen mode
  • Query All Users (Protected):
  {
    users {
      id
      username
      email
    }
  }
Enter fullscreen mode Exit fullscreen mode

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)