Building GraphQL APIs with NestJS

@rnab
4 min readDec 29, 2024

--

In recent years, GraphQL has emerged as a powerful alternative to REST for building APIs, offering a flexible and efficient way to query data. When coupled with NestJS, a progressive Node.js framework for building efficient, reliable, and scalable server-side applications, you get a robust and maintainable solution for your backend needs. This article will guide you through building GraphQL APIs with NestJS using TypeScript.

Introduction to NestJS and GraphQL

NestJS: A framework for building scalable and maintainable server-side applications by leveraging TypeScript and incorporating modern JavaScript and Node.js features. It is inspired by Angular and incorporates a modular architecture to structure your codebase efficiently.

GraphQL: A query language for your API that provides a more efficient and powerful alternative to REST. It enables clients to request exactly the data they need and nothing more, avoiding over-fetching and under-fetching of information.

Getting Started

Let’s get started by creating a basic NestJS application with GraphQL support.

1. Setting Up the Project

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

Navigate to the project directory:

$ cd graphql-nestjs-api

2. Installing Dependencies

To add GraphQL support to your NestJS project, you need to install the necessary packages:

$ npm install @nestjs/graphql @nestjs/apollo graphql apollo-server-express
$ npm install @nestjs/typeorm typeorm sqlite3

3. Configuring GraphQL Module

Open the app.module.ts file and configure the GraphQL module:

import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { join } from 'path';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { TypeOrmModule } from '@nestjs/typeorm';
import { RecipesModule } from './recipes/recipes.module';

@Module({
imports: [
TypeOrmModule.forRoot({
type: 'sqlite',
database: 'db.sqlite',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
}),
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
}),
RecipesModule,
],
})
export class AppModule {}

4. Creating a Recipe Module

Generate a new module for recipes:

$ nest generate module recipes

5. Creating a Recipe Entity

Define an entity for recipes within the module (create a file named recipe.entity.ts in the recipes directory):

import { ObjectType, Field, ID } from '@nestjs/graphql';
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@ObjectType()
@Entity()
export class Recipe {
@Field(() => ID)
@PrimaryGeneratedColumn()
id: number;

@Field()
@Column()
title: string;

@Field()
@Column()
description: string;

@Field()
@Column()
ingredients: string;

@Field()
@Column()
instructions: string;
}

6. Creating a Recipe Service

Create a service to handle the business logic for recipes (create a file named recipes.service.ts in the recipes directory):

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Recipe } from './recipe.entity';

@Injectable()
export class RecipesService {
constructor(
@InjectRepository(Recipe)
private recipesRepository: Repository<Recipe>,
) {}

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

findOne(id: number): Promise<Recipe> {
return this.recipesRepository.findOneBy({id});
}

async create(recipe: Recipe): Promise<Recipe> {
return this.recipesRepository.save(recipe);
}

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

7. Creating a Recipe Resolver

Create a resolver to handle GraphQL queries and mutations (create a file named recipes.resolver.ts in the recipes directory):

import { Resolver, Query, Mutation, Args, Int } from '@nestjs/graphql';
import { RecipesService } from './recipes.service';
import { Recipe } from './recipe.entity';
import { CreateRecipeInput } from './create-recipe.input';

@Resolver(of => Recipe)
export class RecipesResolver {
constructor(private recipesService: RecipesService) {}

@Query(returns => [Recipe])
recipes(): Promise<Recipe[]> {
return this.recipesService.findAll();
}

@Query(returns => Recipe)
recipe(@Args('id', { type: () => Int }) id: number): Promise<Recipe> {
return this.recipesService.findOne(id);
}

@Mutation(returns => Recipe)
createRecipe(@Args('createRecipeInput') createRecipeInput: CreateRecipeInput): Promise<Recipe> {
const recipe = new Recipe();
recipe.title = createRecipeInput.title;
recipe.description = createRecipeInput.description;
recipe.ingredients = createRecipeInput.ingredients;
recipe.instructions = createRecipeInput.instructions;
return this.recipesService.create(recipe);
}

@Mutation(returns => Boolean)
async removeRecipe(@Args('id', { type: () => Int }) id: number): Promise<boolean> {
await this.recipesService.remove(id);
return true;
}
}

8. Defining Input Types

Create an input type for creating new recipes (create a file named create-recipe.input.ts in the recipes directory):

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

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

@Field()
description: string;

@Field()
ingredients: string;

@Field()
instructions: string;
}

9. Updating the Recipe Module

Finally, ensure that the service, resolver, and entity are properly integrated into the module (update recipes.module.ts):

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { RecipesService } from './recipes.service';
import { RecipesResolver } from './recipes.resolver';
import { Recipe } from './recipe.entity';

@Module({
imports: [TypeOrmModule.forFeature([Recipe])],
providers: [RecipesService, RecipesResolver],
})
export class RecipesModule {}

Running the Application

With everything set up, you can run your NestJS application:

$ npm run start:dev

Navigate to http://localhost:3000/graphql in your browser, and you will find the GraphQL Playground where you can test your GraphQL queries and mutations. Here are some sample queries:

Querying All Recipes:

query {
recipes {
id
title
description
ingredients
instructions
}
}

Querying a Single Recipe by ID:

query {
recipe(id: 1) {
id
title
description
ingredients
instructions
}
}

Creating a New Recipe:

mutation {
createRecipe(createRecipeInput: {
title: "Chocolate Cake"
description: "Delicious homemade chocolate cake."
ingredients: "Flour, Sugar, Cocoa, Baking Powder, Eggs, Milk, Butter"
instructions: "Mix all ingredients, bake at 350°F for 30 minutes."
}) {
id
title
}
}

Conclusion

In this article, we have covered the basics of creating a GraphQL API with NestJS. We started by setting up the NestJS project, installing necessary dependencies, and then building the modules, services, and resolvers required to handle our data. NestJS’s elegant abstraction over underlying libraries makes it incredibly straightforward to build robust and maintainable GraphQL APIs.

Whether you are building a small project or a complex enterprise application, combining NestJS with GraphQL provides you with a flexible, powerful, and scalable solution.

--

--

@rnab
@rnab

Written by @rnab

Typescript, Devops, Kubernetes, AWS, AI/ML, Algo Trading

No responses yet