DEV Community

Cover image for NestJS GraphQL: Code First Approach
Subham
Subham

Posted on

NestJS GraphQL: Code First Approach

In the previous article, we learned about the Schema First Approach for setting up GraphQL with NestJS. Now, we will explore the Code First Approach.

In the Code First Approach, we define our GraphQL types and resolvers directly in TypeScript code instead of writing separate GraphQL schema files. This approach leverages the power of TypeScript and its type system to ensure consistency and type safety throughout our application.

Repo Link : https://github.com/Subham-Maity/scalable_server_architecture/tree/main/example/2.%20graphql_typeorm/code-first-approach

Step 1: Remove the GraphQL Schema File

First, let's remove the book.schema.graphql file that we created in the previous approach, as we won't be needing it anymore.

Step 2: Modify the app.module.ts

Next, we need to update the app.module.ts file to configure the Code First Approach. Here's the updated code:

import { ApolloDriverConfig, ApolloDriver } from '@nestjs/apollo';
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { BookResolver } from './book/book.resolver';
import { join } from 'path';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      playground: true, // Set to false in production
      autoSchemaFile: join(process.cwd(), 'src/schema.graphql'), // Automatically generate the GraphQL schema
      // ts-morph
      definitions: {
        path: join(process.cwd(), 'src/graphql.ts'), // Path to the generated file with TypeScript definitions
      },
    }),
    BookResolver,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

In this updated code, we've added two new properties to the GraphQLModule.forRoot configuration:

  • autoSchemaFile: This property specifies the path where the automatically generated GraphQL schema file will be saved. In this case, it's set to src/schema.graphql.
  • definitions: This property is specific to the ts-morph library, which is used to generate TypeScript definitions from the GraphQL schema. The path property inside definitions specifies the location where the TypeScript definitions will be saved. In this case, it's set to src/graphql.ts.

Step 3: Install ts-morph

To use the Code First Approach, we need to install the ts-morph library, which is responsible for generating TypeScript definitions from the GraphQL schema. Run the following command to install ts-morph:

npm install ts-morph
Enter fullscreen mode Exit fullscreen mode

Step 4: Define the GraphQL Types and Resolvers

Now, let's create the book.resolver.ts file in the book folder, where we'll define our GraphQL types and resolvers.

import { Query, Resolver } from '@nestjs/graphql';
import { Book } from './book.schema';
import { Book as BookModel } from '../graphql';

@Resolver(() => Book)
export class BookResolver {
  @Query(() => [Book], { name: 'books' }) // name set instead of getAllBooks it's now books
  getAllBooks() {
    const arr: BookModel[] = [
      {
        id: 1,
        title: 'Book 1',
        price: 10,
      },
      {
        id: 2,
        title: 'Book 2',
        price: 20,
      },
      {
        id: 3,
        title: 'Book 3',
        price: 30,
      },
    ];
    return arr;
  }
}
Enter fullscreen mode Exit fullscreen mode

In this file, we've defined a Book type and a books query resolver using the @Resolver and @Query decorators from @nestjs/graphql.

The Book type is imported from the book.schema.ts file, which we'll create shortly. The BookModel type is imported from the graphql.ts file, which will be automatically generated by the ts-morph library based on our GraphQL schema.

Step 5: Define the GraphQL Object Types

Next, we need to create the book.schema.ts file in the book folder, where we'll define our GraphQL object types.

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

@ObjectType()
export class Book {
  @Field(() => ID)
  id: number;

  @Field()
  title: string;

  @Field(() => Int)
  price: number;
}
Enter fullscreen mode Exit fullscreen mode

In this file, we've defined the Book object type using the @ObjectType decorator from @nestjs/graphql. Each field of the Book type is defined using the @Field decorator, along with the appropriate type decorator (ID for the id field, and Int for the price field).

Step 6: Start the NestJS Server

Now, we can start the NestJS server by running the following command:

npm run start:dev
Enter fullscreen mode Exit fullscreen mode

or

yarn start:dev
Enter fullscreen mode Exit fullscreen mode

When you start the server, it will automatically generate the schema.graphql file in the src directory and the graphql.ts file with the TypeScript definitions. The graphql.ts file can be imported and used in other parts of your application to ensure type safety when working with GraphQL types.

You can then navigate to http://localhost:3000/graphql and access the GraphQL Playground to test your queries and mutations.

In this blog post, we learned how to set up GraphQL with NestJS using the Code First Approach. This approach allows us to define our GraphQL types and resolvers directly in TypeScript code, leveraging the power of TypeScript's type system for better type safety and consistency.

The Code First Approach has a few advantages over the Schema First Approach:

  1. Type Safety: By defining our types in TypeScript, we get better type safety and can catch errors during development time, rather than at runtime.
  2. Colocation: With the Code First Approach, we can colocate our types, resolvers, and other related code, making it easier to maintain and understand the codebase.
  3. IDE Support: Most modern IDEs provide better tooling and code completion for TypeScript code, making it easier to work with GraphQL types and resolvers.

However, the Schema First Approach can still be preferred in certain scenarios, such as when working with an existing GraphQL API or when the schema is more stable and needs to be shared across different platforms or languages.

Overall, both approaches have their advantages and trade-offs, and the choice between them often depends on the specific requirements and preferences of the project and team.

Top comments (0)