DEV Community

Henrique Weiand
Henrique Weiand

Posted on

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

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.
Creating Smart Questions with NestJS and OpenAI

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
Documentation | NestJS - A progressive Node.js framework

First, we have to install the dependencies

npm i @nestjs/graphql @nestjs/apollo @apollo/server graphql
Enter fullscreen mode Exit fullscreen mode

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 { }
Enter fullscreen mode Exit fullscreen mode

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 & file structure

./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
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

(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

Again, I am not going through 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`];
}
Enter fullscreen mode Exit fullscreen mode

(code)

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
Enter fullscreen mode Exit fullscreen mode

and then you can access http://localhost:3000/graphql , where you can play with your query, mutations, etc.

Mutation

Query

Conclusion

The final code can be found in this like
GitHub - nestjsninja/nestjs-generate-questions-graphql

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.
GitHub - henriqueweiand/nestjs-account-graphql: NestJS and GraphQL based project simulating an…

Some good references
Ultimate Guide: How To Use Prisma With NestJS [2022]
NestJS GraphQL/Prisma/PostgreSQL Set Up

Top comments (1)

Collapse
 
nirjonnahuel1 profile image
Nirjon nahuel | Areon

🚀 Calling all tech enthusiasts! Areon Network is hosting a groundbreaking Hackathon with a whopping $500K prize pool. Dive into the coding adventure at hackathon.areon.network and let your innovation shine! 💻✨ #AreonHackathon #500KPrizePool