Graphql with Nest.js

Author : JaNakh Pon , August 21, 2021

Tags

Graphql with Nest.js

We are going to add graphql to the previous rest api in a few steps:

  1. Installation and creating schema/todo.graphql
  2. Defining guard for private queries
  3. Adding methods to resolver

Summary

In this article, we are going to add Graphql to our NEST.JS API . To do that, we need to create a todo.graphql file as a root schema file and add resolver file for it .

For authentication, we need to create a class with the help of nestjs AuthGuard and check if the bearer token is provided and the user is also valid, then we pass the data the the graphql context.

And to get the data from the graphql context, we need to create a decorator function with the help of createParamDecorator to extract user data from the GqlExecutionContext.


Installation & Schema

Firstly, let's install the required dependencies:

  > npm i @nestjs/graphql graphql --save
  > cd src/todo && touch todo.graphql && nest g resolver todo

Now, we have graphql dependencies installed, todo.graphql and todo.resolver.ts.

Let's get started by adding queries and mutations to our todo.graphql file:

todo.graphql
type Todo {
    id: ID!
    title: String
    text: String
    completed: Boolean
}

type SearchResponse {
    data: [Todo]
    count: Int
}

type Status {
    message: String
}

type User {
    userId: Int 
    email: String
}

type RowCount {
    count: Int
}

enum SortType {
    ASC,
    DESC
}

input SearchInput {
    page: Int 
    take: Int 
    title: String
    text: String
}

input CreateInput {
    title: String
    text: String
    completed: Boolean
}

type Query {
    list(page: Int, take: Int, sort: String, order: SortType): [Todo]
    searchList(input: SearchInput): SearchResponse
    todo(id: ID!): Todo!
    me: User
    count: RowCount
}

type Mutation {
    create(input: CreateInput!): Todo
    update(id: ID!, input: CreateInput!):Todo
    delete(id: ID!): Status
}

Authentication/AuthGuard

Before, we started adding methods in resolver file, there is something we need to work on first, "Authentication".

Let's create a new folder under /user/auth/guard and we will create two files.

For Authentication:

gql.guard.ts
import { AuthGuard } from '@nestjs/passport';
import { ExecutionContext, Injectable } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
 
@Injectable()
export class GraphqlJwtAuthGuard extends AuthGuard('jwt') {
  getRequest(context: ExecutionContext) {
    const ctx = GqlExecutionContext.create(context);
    return ctx.getContext().req;
  }
}

And to extract the authorized user data from the graphql context...

gql.user.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
export const GqlCurrentUser = createParamDecorator(
  (data: unknown, context: ExecutionContext) => {
    const ctx = GqlExecutionContext.create(context);
    return ctx.getContext().req.user;
  },
)

Resolver

Now, we have everything we need for resolver file, so let's add methods for queries and mutations to our todo.resolver.ts with the same names and properties that we have in our schema/todo.graphql:

todo.resolver.ts
import { Query, Resolver, Args, ID, Mutation } from '@nestjs/graphql';
import { RowCount, SearchResponse, SortType, Status, Todo } from '../interfaces/todo.interface'
import { CreateInput, SearchInput } from '../types/todo.gql'
import { TodoService } from '../todo.service';
import { UseGuards } from '@nestjs/common';
import { GraphqlJwtAuthGuard } from 'src/user/auth/guard/gql.guard';
import { GqlCurrentUser } from 'src/user/auth/guard/gql.user';
import { User } from 'src/user/interfaces/user.interface';

@Resolver('Todo')
export class TodoResolver {
    constructor(private todoService: TodoService) { }

    @Query()
    @UseGuards(GraphqlJwtAuthGuard)
    async me(@GqlCurrentUser() user: User,): Promise<User> {
        return user
    }

    @Query()
    async list(
        @Args('page') page = 1,
        @Args('take') take = 5,
        @Args('sort') sort = "updated_at",
        @Args('order') order = SortType.D
    ): Promise<Todo[]> {
        return await this.todoService.findAll(page, take, sort, order);
    }

    @Query()
    async searchList(@Args('input') input: SearchInput): Promise<SearchResponse> {
        const { page, take, title, text } = input
        return await this.todoService.search(page, take, title, text)
    }

    @Query()
    async todo(@Args('id', { type: () => ID }) id: number): Promise<Todo> {
        return await this.todoService.findOne(id);
    }

    @Query()
    @UseGuards(GraphqlJwtAuthGuard)
    async count(@GqlCurrentUser() user: any,): Promise<RowCount> {
        console.log("user", user)
        return await this.todoService.getCount()
    }

    @Mutation()
    async create(@Args('input') input: CreateInput): Promise<Todo> {
        return await this.todoService.create(input)
    }

    @Mutation()
    async update(@Args('id', { type: () => ID }) id: number, @Args('input') input: CreateInput): Promise<Todo> {
        return await this.todoService.update(id, input)
    }

    @Mutation()
    async delete(@Args('id', { type: () => ID }) id: number): Promise<Status> {
        return await this.todoService.delete(id);
    }
}

Now, we can go to localhost:3001/graphql and play with queries and mutations in Graphql Playground.

However, we need to have a bearer token to access query with @UseGuards(GraphqlJwtAuthGuard). So, to be able to access it, you should create an account via /user/register.

After that, login via /user/login with the same credentials that you have used for creating an account and grab the token.

Add an Authorization Header to our Graphql Playground, Authorization: Bear {token} and now we should be able to access the count query 😉.


Source Code.

Go Back.