Working with GraphQL in Nest JS
Tags: graphql, nestjs
Published: 2023-09-04 10:49:31

**Graph Query Language** or GraphQL is an API query language. 

It allows us to fetch precise data per request instead of fetching unneeded data and delivering it to us. GraphQL is an intuitive method of fetching data. 

An example scenario is when you are asked what your age is. The person asking only expects your age e.g. 44 years old. You don't begin by mentioning your birth date, where you were born, or even your parents. 

The precision in data fetching is the power of GraphQL.

Let us go over a few common terms you will come across when working with GraphQL.

- **Schema**: defines the structure of the API. GraphQL has its type system for this and the language used to write the schema is called Schema Definition Language(SDL).

- **Types**: are the entities we would like to have in our application. For example, in a notes app, the **note** will be a type. There are different types namely scalar, enum, union, object, and interface types.

- **Fields**: define the data of the types.

```type and field example
type Note {
  title: String!
  body: String!
}```

In the example above, `Post` is the type, and `title` is the field.

## Ways to build GraphQL APIs in Nest JS

**Schema-first** is a method where you manually define the GraphQL schema using SDL, then add the `typePaths` property to the GraphQL Module options to tell it where to look for the schema.

**Code-first** is a method where the GraphQL schema is generated from Typescript classes and decorators, you do not write the GraphQL schema yourself.

### Code-First method

For this tutorial, we shall use the code-first approach where we shall add `autoSchemaFile` to our GraphQL module options.

#### Installing dependencies

Install the following dependencies that we shall use in this project.

`npm install @nestjs/graphql graphql @nestjs/apollo @apollo/server typeorm @nestjs/typeorm pg @nestjs/config`

#### Configure TypeORM

We shall use Postgres as our database and TypeORM as our ORM of choice.

For the detailed method to configure Typeorm check out this [article](https://andisiambuku.hashnode.dev/user-authentication-with-passport-js-and-jwt-in-nest-js#heading-setting-up-typeorm) and then we can proceed to the next section.

#### Configure GraphQL in the application module

Since we are using the code-first method, we shall configure the `GraphQLModule` with the following options:

- **Driver**:  this is the driver of our GraphQL server.
- **autoSchemaFile**: this property's value indicates where the schema will be auto-generated.
- **sortSchema**: this property will sort the items in the schema according to the way they would be in a dictionary because when the types are generated as they are in the module files.

``` //app.module.ts
// in the imports array
GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
      sortSchema: true,
    }),
```

### Creating the CRUD feature

The beauty of Nest JS is that there is a simple way of building the GraphQL feature depending on the method you prefer whether code-first or schema-first.

#### Generating the feature using resource

We shall be creating all the CRUD features and as such we will make use of the resource generator.*resource generator* is a feature that auto-generates boilerplate for resources such as controllers, modules, and services. It helps save time and reduce the repetition of tasks.

Run the command below

`nest generate resource note`

You will then see a prompt like this asking which transport layer you want.

? What transport layer do you use?
  REST API
❯ GraphQL (code first)
  GraphQL (schema first)
  Microservice (non-HTTP)
  WebSockets

![GraphQL prompt](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fm6o2es6qy34ml8gbyd6.png)
  
Select the GraphQL (code-first) option.

#### Entity

The entity is where we define our database schema. The Nest JS docs refer to this as the model, but for simplicity let us can them the entity. The definition of our entity shall differ slightly from how it is defined in REST. We shall make use of the `@Field()` and `@ObjectType()` decorators.

``` //note.entity.ts
import { ObjectType, Field, Int } from '@nestjs/graphql';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@ObjectType()
@Entity()
export class Note {

  @Field(() => Int)
  @PrimaryGeneratedColumn()
  id: number;

  @Field()
  @Column('text', { nullable: false })
  title: string;

  @Field()
  @Column('text', { nullable: false })
  body: string;

}
```

Our schema for a note entity shall have two columns the title and body.

The ID shall have the `@PrimaryGeneratedColumn()` so that it auto-generates IDs for the data we key in and the `@Field(() => Int)` decorator with the type Int to ensure the type allowed is an integer.

We use the `@Column('text', { nullable: false })` on the body and title columns to validate that the input should not be null values.

#### Data Transfer Object (DTO)

We shall have data transfer object files for both the creation and updation of our files. This will help perform data transformations and validation before storage in the database.

```//create-note.input.ts
import { InputType, Field } from '@nestjs/graphql';

@InputType()
export class CreateNoteInput {
  @Field()
  title: string;

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

``` //update-note.input.ts
import { CreateNoteInput } from './create-note.input';
import { InputType, Field, PartialType } from '@nestjs/graphql';

@InputType()
export class UpdateNoteInput extends PartialType(CreateNoteInput) {
  @Field()
  title: string;

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

Unlike the REST format of DTOs here we use the `@Column()` decorator here we use the `@Field()` decorator for the database columns.

#### Service

The service is where we define our business logic functions. This helps keep the resolver modular.

```
// note.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Note } from './entities/note.entity';
import { CreateNoteInput } from './dto/create-note.input';
import { UpdateNoteInput } from './dto/update-note.input';

@Injectable()
export class NoteService {
  constructor(
    @InjectRepository(Note)
    private noteRepository: Repository<Note>,
  ) {}

  findAll(): Promise<Note[]> {
    return this.noteRepository.find();
  }

  findOne(id: number): Promise<Note> {
    return this.noteRepository.findOne({ where: { id } });
  }

  async create(createNoteInput: CreateNoteInput): Promise<Note> {
    const note = this.noteRepository.create(createNoteInput);
    return this.noteRepository.save(note);
  }

  async update(id: number, updateNoteInput: UpdateNoteInput) {
    const note = await this.findOne(id);
    this.noteRepository.merge(note, updateNoteInput);
    return this.noteRepository.save(note);
  }

  async remove(id: number): Promise<void> {
    await this.noteRepository.delete(id);
  }
}
```

- The `create` function is for creating a new note based on the data in the `createNoteInput` DTO and then it saves the note using the create and save methods.

- The `update` function is for updating a note based on the data provided in the `updateNoteInput` DTO. It retrieves the note using its id and then merges the changes and then updates it in the database.

#### Resolver

A **resolver** is a file that provides instructions for turning GraphQL operations into data. They use the schema to return the data as specified.

This is the equivalent of controllers in REST.

The GraphQL operations that can be performed on data include:

- **Mutation**: is used to make writes or updates to data in a GraphQL server. Operations like create, update, and delete are done using mutations.
- **Query**: is used to fetch data from a GraphQL server. It is a read operation that requests data without making any write operations in the database.
- **Subscriptions**: allow for real-time server updates. They enable the server to push data to clients when an event occurs. A use case for subscriptions is notifications.

```//note.resolver.ts
import { Resolver, Query, Mutation, Args, Int } from '@nestjs/graphql';
import { NoteService } from './note.service';
import { Note } from './entities/note.entity';
import { CreateNoteInput } from './dto/create-note.input';
import { UpdateNoteInput } from './dto/update-note.input';

@Resolver(() => Note)
export class NoteResolver {
  constructor(private readonly noteService: NoteService) {}

  @Mutation(() => Note)
  async createNote(@Args('createNoteInput') createNoteInput: CreateNoteInput) {
    return this.noteService.create(createNoteInput);
  }

  @Query(() => [Note], { name: 'notes' })
  findAll() {
    return this.noteService.findAll();
  }

  @Query(() => Note, { name: 'note' })
  findOne(@Args('id', { type: () => Int }) id: number) {
    return this.noteService.findOne(id);
  }

  @Mutation(() => Note)
  updateNote(@Args('updateNoteInput') updateNoteInput: UpdateNoteInput) {
    return this.noteService.update(updateNoteInput.id, updateNoteInput);
  }

  @Mutation(() => Note)
  removeNote(@Args('id', { type: () => Int }) id: number) {
    return this.noteService.remove(id);
  }
}

```

- `@Resolver(() => Note)`: this decorator shows NoteResolver class is a GraphQL resolver for the 'Note' entity telling NestJS and the GraphQL framework that this class handles GraphQL queries and mutations related to the Note type.

- `createNote` mutation: The @Args('createNoteInput') decorator specifies that the createNote method expects an argument named createNoteInput, which should be of type CreateNoteInput.
Inside the method, you call  `this.noteService.create(createNoteInput)` function to create a new note and return it.

- `findAll` query: This decorator marks the findAll method as a GraphQL query. Queries are used for retrieving data from the server.
The { name: 'notes' } option specifies the name of the query, which will be used in the GraphQL schema.
Inside the method, you call `this.noteService.findAll()` to retrieve a list of all notes and return them.

- `findOne` query: This decorator marks the findOne method as a GraphQL query. The { name: 'note' } option specifies the name of the query. The method expects an argument named id of type Int, and it uses `this.noteService.findOne(id)` to retrieve a single note by ID and return it.

- `updateNote` mutation: This decorator marks the updateNote method as a GraphQL mutation. The @Args('updateNoteInput') decorator specifies that the method expects an argument named updateNoteInput of type UpdateNoteInput. Inside the method, you call this.noteService.update(updateNoteInput.id, updateNoteInput) to update a note and return it.

- `removeNote` mutation: This decorator marks the removeNote method as a GraphQL mutation. The @Args('id', { type: () => Int }) decorator specifies that the method expects an argument named id of type Int. Inside the method, you call `this.noteService.remove(id)`` to delete a note.

#### Module

The module is where we define the metadata for libraries we use in our API.

```
//note.module.ts
import { Module } from '@nestjs/common';
import { NoteService } from './note.service';
import { NoteResolver } from './note.resolver';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Note } from './entities/note.entity';

@Module({
imports: [TypeOrmModule.forFeature([Note])],
providers: [NoteResolver, NoteService],
})
export class NoteModule {}


```

Here add the Typeorm module to the import or the note feature.

Ensure you run the migration to create the columns in our database. In the event you have used the configuration in the suggested article then run your migration as follows:

make migration: `npm run typeorm:generate-migration migrations/<name_of_migration>`

run migration: `npm run typeorm:run-migrations`

### Testing the Endpoint

One of the grand advantages of GraphQL is that we only make use of one endpoint as opposed to REST here we have multiple endpoints as per the operations you want to have for your data.

Now that the feature is complete let us proceed to test the endpoint. We shall make use of the GraphQL playground at `http://localhost:3000/graphql`

After running the `npm run start` command, you will see the schema auto-generated. It will look like the one below.


//schema.gql

------------------------------------------------------

THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)

------------------------------------------------------

input CreateNoteInput {
body: String!
title: String!
}

type Mutation {
createNote(createNoteInput: CreateNoteInput!): Note!
removeNote(id: Int!): Note!
updateNote(updateNoteInput: UpdateNoteInput!): Note!
}

type Note {
body: String!
id: Int!
title: String!
}

type Query {
note(id: Int!): Note!
notes: [Note!]!
}

input UpdateNoteInput {
body: String!
id: Float!
title: String!
}


#### Create note

To test the creation of a note ensure you check the schema for the format of the the request. According to our schema, the mutation should look like the one below. Add the mutation to the left panel and click the 'play' button.

```//create mutation
mutation {
  createNote(createNoteInput: {
    title: "Test",
    body: "Test body"
  }) {
    id
    title
    body
  }
}
```

![Create note](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/algnyh9ekpb9ku1f68xb.png)



#### Find one note

To test the fetching of a note ensure you check the schema for the format of the the request. According to our schema, the query should look like the one below. Add the query to the left panel and click the 'play' button.

```//find one query
query {
  note(id:1) {
    id,
    title,
    body
  }
}
```

![Find all](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/499mzp6d5rtyb1j5tdg2.png)


#### Find all notes

To test the fetching of all notes ensure you check the schema for the format of the the request. According to our schema, the query should look like the one below. Add the query to the left panel and click the 'play' button.

```//find all query
query {
  notes{
    id,
    title,
    body
  }
}
```

![All notes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lcbzujaqn3a3myc6s6ao.png)

#### Update note

To test the updating of a note ensure you check the schema for the format of the request. According to our schema, the mutation should look like the one below. Add the mutation to the left panel and click the 'play' button.

```// update mutation
mutation {
  updateNote(updateNoteInput: {
    id: 1,   
    title: "Updated Title",
    body: "Updated body"
  }) {
    id
    title
    body
  }
}
```

![Update notes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9jbiq80jdzhjww0tlb7u.png)

#### Delete note

To test the deletion of a note ensure you check the schema for the format of the the request. According to our schema, the mutation should look like the one below. Add the mutation to the left panel and click the 'play' button.

``` //delete mutation
mutation {
  removeNote(id: 1) {
    id
    title
    body
  }
}

```

![Remove notes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kucubacrypevy58ar4q7.png)

The link to the project used in this tutorial is [here](https://github.com/andisiambuku/GraphQL-CRUD-API). Feel free to try out and comment your thoughts. Until next time may the code be with you.

## References

- [Learn GraphQL](https://graphql.com/learn/what-is-graphql/)
- [GraphQL Organization](https://graphql.org/learn/)
- [How to GraphQL](https://www.howtographql.com/basics/1-graphql-is-the-better-rest/)
- [Nest JS Documentation](https://docs.nestjs.com/graphql/quick-start)