NestJS offers Fastify and Mercurius support out of the box. It also supports both schema and code-first approaches to development. We'll be covering the code-first approach, as it is used more commonly.
Note: Support for NestJS can be found at their Discord server.
To get going, follow the first steps to install the Nest CLI and to create your Nest API project with the CLI.
Use whatever project name you'd like in the second command below:
npm i -g @nestjs/cli
nest new my-new-project
You'll be asked which package manager you'd like to use. Select the one you want and the CLI will install your initialized app with it.
In order to work with Fastify, you'll need to change NestJS' "platform" to Fastify, since NestJS works with Express as the default. To do so, follow these steps:
Remove the dependency for Express:
npm remove @nestjs/platform-express
Then install the NestJS Fastify platform package. Also install webpack. Webpack is needed as a dev dependency for the dev server with Fastify:
npm i --save @nestjs/platform-fastify
npm i --dev webpack
Note: Make sure to use the same package manager you selected in your project initialization.
Next, install the Nest GraphQL and Mercurius modules and the other needed dependencies.
npm i @nestjs/graphql @nestjs/mercurius graphql mercurius
Note: Again, make sure to use the same package manager you selected in your project initialization.
To use the GraphQL module, replace your AppModule file with the following code:
// app.module.ts
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { MercuriusDriver, MercuriusDriverConfig } from '@nestjs/mercurius';
import { RecipesModule } from './recipes/recipes.module';
@Module({
imports: [
GraphQLModule.forRoot<MercuriusDriverConfig>({
driver: MercuriusDriver,
graphiql: true,
autoSchemaFile: true,
}),
RecipesModule,
],
})
export class AppModule {}
The forRoot
method of the GraphQLModule
requires a config object as an argument. This object will be passed on to Mercurius for its configuration. The only extra option is the driver
property, which tells Nest to use Nest's Mercurius driver.
Note: you can safely remove the
app.controller
andapp.controller.spec
files in the/src
folder. They aren't needed.
Now you can start creating GraphQL Modules with Resolvers. Lets create a recipes
module. In the root folder, run this CLI command:
nest generate module recipes
You'll notice the module import is added to the AppModule
automatically and you should see something like below added your your project under a newly created recipes
folder:
// recipes.modules.ts
import { Module } from '@nestjs/common';
@Module({})
export class RecipesModule {}
This is your first module. Yay!
Now lets create a schema file for our recipes
GraphQL output object. Create a new file called recipe.model.ts
in the /recipes
folder with the following code:
// recipe.model.ts
import { Directive, Field, ID, ObjectType } from '@nestjs/graphql';
@ObjectType({ description: 'recipe ' })
export class Recipe {
@Field(type => ID)
id: string;
@Directive('@upper')
title: string;
@Field({ nullable: true })
description?: string;
@Field()
creationDate: Date;
@Field(type => [String])
ingredients: string[];
}
Let's generate a recipes resolver class too. Make sure you are back in your root folder and run this command:
nest generate resolver recipes
This command creates the recipes.resolver.ts
and recipes.resolver.spec.ts
files. It should also add the resolver file as a provider in your recipes
module automatically. Providers are a powerful part of Nest's dependency injection system.
Now you can alter the recipe.resolver.ts
file and add the code below to define your resolver methods:
// recipe.resolver.ts
import { NotFoundException } from '@nestjs/common';
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { RecipesArgs } from './recipe.args';
import { Recipe } from './recipe.model';
import { RecipesService } from './recipes.service';
@Resolver((of) => Recipe)
export class RecipesResolver {
constructor(private readonly recipesService: RecipesService) {}
@Query((returns) => Recipe)
async recipe(@Args('id') id: string): Promise<Recipe> {
const recipe = await this.recipesService.findOneById(id);
if (!recipe) {
throw new NotFoundException(id);
}
return recipe;
}
@Query((returns) => [Recipe])
recipes(@Args() recipesArgs: RecipesArgs): Promise<Recipe[]> {
return this.recipesService.findAll(recipesArgs);
}
@Mutation((returns) => Recipe)
async addRecipe(): /* @Args('newRecipeData') newRecipeData: NewRecipeInput,*/
Promise<Recipe> {
const recipe = await this.recipesService.create(/* newRecipeData */);
return recipe;
}
@Mutation((returns) => Boolean)
async removeRecipe(@Args('id') id: string) {
return this.recipesService.remove(id);
}
}
Lastly, you'll need to create a recipes.service.ts
file for your Recipes Service. Services are the classes which the resolver will call for the "business logic" of your resolvers, and in the end, your application. The service we are creating will also use the output object we created earlier:
// recipes.service.ts
import { Injectable } from '@nestjs/common';
import { NewRecipeInput } from './dto/new-recipe.input';
import { RecipesArgs } from './dto/recipes.args';
import { Recipe } from './models/recipe.model';
@Injectable()
export class RecipesService {
/**
* Note, this is just a MOCK
* Put some real business logic here
* Only for demonstration purposes
*/
async create(data: NewRecipeInput): Promise<Recipe> {
return {} as any;
}
async findOneById(id: string): Promise<Recipe> {
return {} as any;
}
async findAll(recipesArgs: RecipesArgs): Promise<Recipe[]> {
return [] as Recipe[];
}
async remove(id: string): Promise<boolean> {
return true;
}
}
To run the dev server and get going with more programming, run this command:
`npm run start:dev`
If all went well, you should see something like this from the dev server's compilation process:
[Nest] 61751 - 04/10/2022, 1:36:21 PM LOG [NestFactory] Starting Nest application...
[Nest] 61751 - 04/10/2022, 1:36:21 PM LOG [InstanceLoader] AppModule dependencies initialized +30ms
[Nest] 61751 - 04/10/2022, 1:36:21 PM LOG [InstanceLoader] RecipesModule dependencies initialized +1ms
[Nest] 61751 - 04/10/2022, 1:36:21 PM LOG [InstanceLoader] GraphQLSchemaBuilderModule dependencies initialized +0ms
[Nest] 61751 - 04/10/2022, 1:36:21 PM LOG [InstanceLoader] GraphQLModule dependencies initialized +0ms
[Nest] 61751 - 04/10/2022, 1:36:21 PM LOG [GraphQLModule] Mapped {/graphql, POST} route +42ms
[Nest] 61751 - 04/10/2022, 1:36:21 PM LOG [NestApplication] Nest application successfully started +1ms
And you should be able to see GraphiQL under http://localhost:3000/graphiql
.
Now you can continue adding more modules, models, resolvers and business logic in services.
This is just a short and rough example of how to get going with NestJS and Mercurius. There is a lot more to do and learn to get a fully running GraphQL API going. The code examples above, despite lacking some explanation, do show the potential of NestJS with Mecurius. Again, we've only barely scratched the surface.
If you'd like to continue with Nest and Mercurius and learn more about Nest, please do read the documention on the NestJS website. The two make a really great combination in terms of developer experience.
Should you need any help with Nest, they have a great support community. Please go there for support with NestJS questions or issues.