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.