How to create an Authentication & Authorization feature in Nest JS GraphQL API?
Published: 2024-06-04 05:36:33

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**:
   ```bash
   npm install -g @nestjs/cli
   ```
2. **Create a New Project**:
   ```bash
   nest new project-name
   ```
3. **Navigate to the Project Directory**:
   ```bash
   cd project-name
   ```

### Step 2: Install Required Packages
1. **Install Necessary Packages**:
   ```bash
   npm install @nestjs/graphql graphql apollo-server-express @nestjs/jwt passport @nestjs/passport passport-jwt bcryptjs
   ```

### Step 3: Set Up the GraphQL Module
1. **Configure GraphQL Module**:
   Open `src/app.module.ts` and configure the GraphQL module:
   ```typescript
   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
1. **Create a Directory Structure**:
   ```bash
   mkdir -p src/users src/auth
   ```

2. **Create the User Entity**:
   Create `src/users/user.entity.ts`:
   ```typescript
   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
1. **Implement Service Logic**:
   Open `src/users/users.service.ts` and implement the service methods:
   ```typescript
   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
1. **Create the User Resolver**:
   Create `src/users/users.resolver.ts`:
   ```typescript
   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);
     }
   }
   ```

2. **Create DTO for User Input**:
   Create `src/users/dto/create-user.input.ts`:
   ```typescript
   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
1. **Create the User Module**:
   Open `src/users/users.module.ts` and update it:
   ```typescript
   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
1. **Create Auth Service**:
   Create `src/auth/auth.service.ts`:
   ```typescript
   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),
       };
     }
   }
   ```

2. **Create Auth Module**:
   Open `src/auth/auth.module.ts` and configure it:
   ```typescript
   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 {}
   ```

3. **Create JWT Strategy**:
   Create `src/auth/jwt.strategy.ts`:
   ```typescript
   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
1. **Create Auth Resolver**:
   Create `src/auth/auth.resolver.ts`:
   ```typescript
   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);
     }
   }
   ```

2. **Create DTOs for Auth Input and Response**:
   Create `src/auth/dto/auth.input.ts`:
   ```typescript
   import { InputType, Field } from '@nestjs/graphql';

   @InputType()
   export class AuthInput {
     @Field()
     username: string;

     @Field()
     password: string;
   }
   ```

   Create `src/auth/dto/auth.response.ts`:
   ```typescript
   import { ObjectType, Field } from '@nestjs/graphql';

   @ObjectType()
   export class AuthResponse {
     @Field()
     access_token: string;
   }
   ```

### Step

 10: Protect Routes with Auth Guard
1. **Create GQL Auth Guard**:
   Create `src/auth/gql-auth.guard.ts`:
   ```typescript
   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;
     }
   }
   ```

2. **Apply Guard to Resolvers**:
   Update `src/users/users.resolver.ts` to protect routes:
   ```typescript
   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
1. **Start the NestJS Application**:
   ```bash
   npm run start:dev
   ```

### 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**:
  ```graphql
  mutation {
    createUser(createUserInput: { username: "john", email: "john@example.com", password: "password" }) {
      id
      username
      email
    }
  }
  ```

- **Login and Get JWT**:
  ```graphql
  mutation {
    login(authInput: { username: "john", password: "password" }) {
      access_token
    }
  }
  ```

- **Query All Users (Protected)**:
  ```graphql
  {
    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.

Disclaimer: This content is generated by AI.