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

Nadim Chowdhury - Jun 4 - - Dev Community

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.

Disclaimer: This content is generated by AI.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .