NestJS Resolvers FT GraphQL Code-First and Schema-First ๐Ÿš€

Posted October 7, 2023
NestJS Resolvers FT GraphQL Code-First and Schema-First

GraphQL uses Resolvers to determine how to retrieve or manipulate the data associated with each field in a schema. Resolvers connects a GraphQL schema with the underlying data sources.

Take an example of a Rest API. You use controllers to create HTTP Methods. Now, GraphQL canโ€™t use the same structure. Here, the Resolvers transform the raw data into the shape expected by the GraphQL schema. This way, your Resolvers are your controllers for GraphQL in ensuring you create methods for queries, subscriptions, and mutations.

To make Resolvers dynamic, you can choose between Code-First and Schema-First to expand the GraphQL ecosystem.

This guide dives deeper into GraphQL Resolvers and how to use Code-First and Schema-First to model your schema featuring NestJS.

Resolvers with Code-First GraphQL schema

This approach doesnโ€™t allow you to write GraphQL SDL manually. You must use Decorators; the schema (SDL) will be generated automatically.

Related: NestJS GraphQL API with TypeORM, Apollo Server and PostgreSQL

Think of this approach as a Typical database ORM where you use decorators to define how database table attributes are represented. You only use the Code approach to build your tables, attributes, and Relationships here. Then, the ORM automatically generates migrations and tables for you.

The same narrative applies to the Code-First GraphQL schema. Resolvers and types are discovered and processed automatically by the framework you are using.

Code-First Example NestJS GraphQL Resolver

Letโ€™s look at the basics of Code First implementation. Code-First uses TypeScript decorators @ObjectType(), @Field() From GraphQL and NestJS. Now, you will go ahead and create GraphQL types and schema directly in TypeScript classes as follows:

import { ObjectType, Field } from '@nestjs/graphql';

@ObjectType()
export class Task {
  @Field()
  id: string;

  @Field()
  title: string;

  @Field()
  completed: boolean;
}

Now you will go ahead and create your Resolvers:

import { Query, Resolver, Args, Mutation } from '@nestjs/graphql';
import { Task } from './task.entity';
import { TaskService } from './task.service';

@Resolver((of) => Task)
export class TaskResolver {
  constructor(private readonly taskService: TaskService) {}

  @Query((returns) => [Task])
  tasks(): Task[] {
    return this.taskService.findAll();
  }

  // Other resolvers
}

You then tell the NestJS entry point to use GraphQLModule and autoSchemaFile. At runtime, NestJS will generate your schema. This means the Schema is being generated based on how you create your types and Resolvers.

Resolvers with Schema-First GraphQL Schema

Code-First doesnโ€™t allow GraphQL to autogenerate the schema. The GraphQL schema is defined using the GraphQL Schema Definition Language (SDL) in a separate file. Here, you implement Resolvers to match the schema structure you defined in the SDL.

Again, take the ORM approach. With it, you can explicitly create your tables and related attributes. And Schema-First isnโ€™t different. Resolvers explicitly connect with the schema, specifying how each field should be resolved.

Example Resolver

Go directly and create your schema.gql and add your schema. For example:

type Task {
  id: ID!
  title: String!
  completed: Boolean!
}

type Query {
  tasks: [Task]
  task(id: ID!): Task
}

type Mutation {
  createTask(title: String!): Task
  updateTask(id: ID!, title: String!): Task
  deleteTask(id: ID!): Task
}

This is the exact Schema you want your application to follow. Likewise, the resolvers you create must adhere to it without any compromise.

How to Create NestJS Resolvers FT GraphQL Code-First

Letโ€™s dive into the actual implementation of Resolvers FT GraphQL Code-First. You will use NestJS.

Ensure you have NestJS CLI ready and create your app:

npm i -g @nestjs/cli
nest new nestjs-graphql

This should create a NestJS application that you cd to:

cd nestjs-graphql

You first need to install GraphQL and its related packages:

npm install graphql @nestjs/graphql @nestjs/apollo apollo-server-express

To create any GraphQL-related app with NestJS, use the following command as a resource:

nest g resource task --no-spec

This command scaffolds the fundamental building blocks of GraphQL. So, make sure you select your Schema Modelling. Here, you are working with the Code First approach as follows:

NestJS Resolvers FT GraphQL Code-First and Schema-First

Also, Would you like to generate CRUD entry points? make it yes:

NestJS Resolvers FT GraphQL Code-First and Schema-First

Look at your file:

NestJS Resolvers FT GraphQL Code-First and Schema-First

You donโ€™t have a schema.gql file. It is generated during runtime. However, you have an entities/task.entity.ts file. Itโ€™s here you define the types of your schema using @ObjectType() and @Field() from GraphQL decorators as follows:

import { ObjectType, Field } from '@nestjs/graphql';

@ObjectType()
export class Task {
  @Field()
  id: string;

  @Field()
  title: string;

  @Field()
  completed: boolean;
}

Still, on your code, you will create your resolvers in the task/task.resolver.ts file. This includes all mutations, queries, and subscriptions you need to have in your GraphQL API.

However, you must have the right Providers using the NestJS Service file to abstract the login of your resolvers. In task/task.service.ts add Providers using @Injectable decorator:

// src/task/task.service.ts
import { Injectable } from '@nestjs/common';
import { Task } from './entities/task.entity';

@Injectable()
export class TaskService {
  private tasks: Task[] = [];

  create(task: Task): Task {
    task.id = (this.tasks.length + 1).toString();
    this.tasks.push(task);
    return task;
  }

 findAll(): Task[] {
    return this.tasks;
  }
}

Based on this Provider, you will create a Mutation and Query resolvers in task/task.resolver.ts as follows:

// src/task/task.resolver.ts
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { Task } from './entities/task.entity';
import { TaskService } from './task.service';

@Resolver((of) => Task)
export class TaskResolver {
  constructor(private readonly taskService: TaskService) {}

  @Query((returns) => [Task])
  tasks(): Task[] {
    return this.taskService.findAll();
  }

  @Mutation((returns) => Task)
  createTask(@Args('title') title: string): Task {
    const newTask: Task = { id: '', title, completed: false };
    return this.taskService.create(newTask);
  }

}

Still, your application entry point must know when and where to generate your SDL Based on the resolvers and type you have created. In your app.module.ts include the GraphQLModule to load the resolvers using Apollo drivers:

// src/app.module.ts
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { TaskModule } from './task/task.module';
import { join } from 'path';
import { ApolloDriverConfig, ApolloDriver } from '@nestjs/apollo';


@Module({
  imports: [
    TaskModule,
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
    }),
  ],
})
export class AppModule {}

Go run your app:

npm run start:dev

NestJS Resolvers FT GraphQL Code-First and Schema-First

Based on this Code First Schema generation, a src/schema.gql will be created at runtime with autogenerated SDL for your app:

NestJS Resolvers FT GraphQL Code-First and Schema-First

Have you understood how Code First Schema works with your Resolver? I have created this Comprehensive NestJS GraphQL API with TypeORM, Apollo Server, and Postgres Guide. It perfectly extends this topic to creating a full-fledged GraphQL API to include your database.

Creating NestJS Resolvers FT GraphQL Schema First

To use this approach, scaffold the fundamental building blocks of GraphQL using your command as follows:

nest g resource post --no-spec

Make sure you select your Schema Modelling. Here, you are working with the schema-first approach as follows:

NestJS Resolvers FT GraphQL Code-First and Schema-First

Also, Would you like to generate CRUD entry points? make it yes:

NestJS Resolvers FT GraphQL Code-First and Schema-First

Look at your file structure:

NestJS Resolvers FT GraphQL Code-First and Schema-First

You will have a post.graphql file to create your GraphQL Schema First SDL.

This approach looks like reverse engineering what you created using Code First. You explicitly create how each field should be resolved.

You Create a Schema to dictate the structure of your types and resolvers. Update the post.graphql file.

Using NestJS Resolvers with Schema-First approach

If you want to use this method, NestJS has a great ready code you can refer to. Itโ€™s a long process that will extend this post to another guide.

I will leave this NestJS GitHub Repo as a perfect reference to GraphQL Schema-First Apollo sample.

Choosing Between Code-First and Schema-First NestJS Resolvers

Code-First is perfectly great if you need:

  • Direct integration with TypeScript and type checking.
  • Less boilerplate codes.
  • When you prefer a more code-centric approach.
  • Your GraphQL schema is tightly integrated with your TypeScript types.

When and why should you choose to use Schema-First Resolvers? Here are my opinions:

  • If you want a clear separation of concerns between schema and implementation.
  • When you have a well-defined schema and want to keep it separate from the implementation.
  • You have teams working with separate roles for frontend and backend development.

Conclusion

GraphQL ecosystem is diverse and dynamic. And Iโ€™m sure you chose it due to its advantages over REST APIs. I hope you learned something when working on your GraphQL NestJS resolvers.

NestJS Resolvers FT GraphQL Code-First and Schema-First ๐Ÿš€

Written By:

Joseph Chege