Unlocking the Power of GraphQL for Beginners: A Step-by-Step Guide to Integrating GraphQL into Your Existing Project
🤺

Unlocking the Power of GraphQL for Beginners: A Step-by-Step Guide to Integrating GraphQL into Your Existing Project

Tags
Typescript
SWC
Jest
NestJS
openAI
GraphQL
Published
November 15, 2023
Author
henrique weiand
Hello fellow coders!
I’ve been noticing an increase in the number of companies requiring GraphQL experience for a job opportunity, and that triggers me to create another useful technical blog post in one interesting way, I want to implement GraphQL inside an existent project! Cool, right? My idea initially, is to implement something not so deep but that works at the same time.
I am going to use my project, which is an AI question generator, that you can find in the link below.
 
Let’s set the scope of this project
  • It must cover the user module, by offering a GraphQL interface to handle the functionalities below
    • Create user
    • Get users
    • Get user
    • Update user
    • Delete user
    •  

Setting up the project to work with GraphQL

Let's use the official documentation to follow the correct setup
 
First, we have to install the dependencies
npm i @nestjs/graphql @nestjs/apollo @apollo/server graphql
 
Then is necessary to set up GraphQL and I'm going to do it by adding the module into the app.module.ts like this
import { EnvModule } from '@app/common'; import { Module } from '@nestjs/common'; import { DevtoolsModule } from '@nestjs/devtools-integration'; import { Modules } from './modules/module'; import { GraphQLModule } from '@nestjs/graphql'; import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; @Module({ imports: [ GraphQLModule.forRoot<ApolloDriverConfig>({ driver: ApolloDriver, playground: true, autoSchemaFile: true, }), DevtoolsModule.register({ http: process.env.NODE_ENV !== 'production', }), EnvModule, Modules, ], }) export class AppModule { }
 
if you keep playground as true, it will enable the visual playground to try the queries and mutations on the browser. We will use it later.
In my case, I also kept autoSchemaFile true and as I'm using the "code first approach”, the official documentation says
In the code first approach, you use decorators and TypeScript classes to generate the corresponding GraphQL schema.
 
Also, the doc mentioned the case of using true as a value
The autoSchemaFile property value is the path where your automatically generated schema will be created. Alternatively, the schema can be generated on-the-fly in memory. To enable this, set the autoSchemaFile property to true
 

Configuring the resolvers

Resolvers provide the instructions for turning a GraphQL operation (a query, mutation, or subscription) into data. They return the same shape of data we specify in our schema -- either synchronously or as a promise that resolves to a result of that shape. Typically, you create a resolver map manually. The @nestjs/graphql package, on the other hand, generates a resolver map automatically using the metadata provided by decorators you use to annotate classes. To demonstrate the process of using the package features to create a GraphQL API, we'll create a simple authors API.
In the code-first approach, we don't follow the typical process of creating our GraphQL schema by writing GraphQL SDL by hand. Instead, we use TypeScript decorators to generate the SDL from TypeScript class definitions. The @nestjs/graphql package reads the metadata defined through the decorators and automatically generates the schema for you.
 

Code on

Speaking about code, as we only need to apply GraphQL to the user module, I will start showing the files inside the folder /src/modules/user, just to show you some differences from the previous project. By the way, I will keep REST working at the same time.
The folder & files strcuture
./user ├── controllers │ ├── controllers... ├── dto │ ├── args │ │ └── get-user.args.ts │ ├── create-user.dto.ts │ ├── input │ │ ├── create-user.input.ts │ │ ├── delete-user.input.ts │ │ └── update-user.input.ts │ ├── update-user.dto.ts │ └── user.dto.ts ├── resolvers │ └── user.resolver.ts ├── use-case │ ├── use-cases... ├── user.model.ts └── user.module.ts
 
We are going to work on basically with:
  • user.model.ts;
  • resolvers folder;
  • dto/args folder;
  • dto/input folder;
 
Our resolver will provide an interface between the user and the use cases, which we have with the controllers when we use REST, right? So you can see that we are injecting the use cases as dependencies just because we need to call them with the required input for those use cases that need.
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; import { GetUserArgs } from '../dto/args/get-user.args'; import { CreateUserInput } from '../dto/input/create-user.input'; import { UpdateUserInput } from '../dto/input/update-user.input'; import { CreateUserUseCase } from '../use-case/create-user'; import { DeleteUserUseCase } from '../use-case/delete-user'; import { GetManyUsersUseCase } from '../use-case/get-many-users'; import { GetUserByIdUseCase } from '../use-case/get-user-by-id'; import { UpdateUserUseCase } from '../use-case/update-user'; import { User } from '../user.model'; @Resolver('User') export class UserResolver { constructor( private getManyUsersUseCase: GetManyUsersUseCase, private createUserUseCase: CreateUserUseCase, private updateUserUseCase: UpdateUserUseCase, private getUserByIdUseCase: GetUserByIdUseCase, private deleteUserUseCase: DeleteUserUseCase ) { } @Query(() => User, { name: 'user', nullable: false }) async getUser(@Args() getUserArgs: GetUserArgs) { return this.getUserByIdUseCase.execute(getUserArgs.id) } @Query(() => [User], { name: 'users', nullable: false }) async getUsers() { return await this.getManyUsersUseCase.execute(); } @Mutation(() => User) async createUser( @Args('createUserInput') createUserInput: CreateUserInput, ) { return await this.createUserUseCase.execute(createUserInput); } @Mutation(() => User) async updateUser( @Args('updateUserInput') updateUserInput: UpdateUserInput, ) { return await this.updateUserUseCase.execute(updateUserInput.id, updateUserInput); } @Mutation(() => User) async deleteUser( @Args('id') id: string ) { return await this.deleteUserUseCase.execute(id); } }
(If you prefer the same code can be accessed here)
I am not going to detail this file yet, let's do it later after having the complements of this file to work properly.
 
As you may notice looking at the resolvers, we changed the approach of using the DTO as body and get parameters to
  • Args
  • Inputs
Which is a more usual way of talking when we are working with GraphQL. They will represent the DTO as it is, and there we specify the props according to the necessity. This is very easy work to be honest, because as we have already the DTO for the REST protocol, we can copy and change just a few details. Loot at this example
notion image
 
Again, I am not going throuth each one of the inputs and args, so feel free to check all of them out.
 
Last but not least, user.model.ts
import { Field, ID, ObjectType } from '@nestjs/graphql'; import { User as UserDB } from '@prisma/client'; @ObjectType() export class User { @Field(() => ID) id: UserDB[`id`]; @Field(() => String) username: UserDB[`username`]; @Field(() => String) password: UserDB[`password`]; }
 
This model is ObjectType which represents our User schema on Prisma, and in this file, we are mapping the fields properly.
 

Running the application

After understanding and setting everything up, it's time to run, right?
npm run start:dev
and then you can access http://localhost:3000/graphql , where you can play with your query, mutations, etc.
 
Mutation
notion image
Query
notion image

Conclusion

The final code can be found in this like
nestjs-generate-questions-graphql
nestjsninja • Updated Nov 15, 2023
 
It was pretty simple, right? Now I hope you feel a bit more comfortable applying GraphQL to your project when it's necessary.
 

References

The repository below is an old project in which I implemented the authentication and authorization modules using GraphQL. The funny part is that it is still up-to-date.
nestjs-account-graphql
henriqueweiand • Updated Nov 18, 2023
 
Some good references
Â