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:
- Installation and creating schema/todo.graphql
- Defining guard for private queries
- 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:
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:
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...
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:
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 😉.
Go Back.