
Nestjs Graphql Part 2
- Atul
- Programming
- December 15, 2024
Table of Contents
Here we are using NestJS as a base. Upon which Apollo server is running for GraphQL to work.
This is what I am getting, so this is the part 2 of the previous blog, make sure to have a basic NestJS project with GraphQL in it. Get it from here.
- NestJS
- Apollo
- GraphQL
- Code First
Schematics available on @nestjs/schematics collection:
┌───────────────┬─────────────┬──────────────────────────────────────────────┐
│ name │ alias │ description │
│ application │ application │ Generate a new application workspace │
│ class │ cl │ Generate a new class │
│ configuration │ config │ Generate a CLI configuration file │
│ controller │ co │ Generate a controller declaration │
│ decorator │ d │ Generate a custom decorator │
│ filter │ f │ Generate a filter declaration │
│ gateway │ ga │ Generate a gateway declaration │
│ guard │ gu │ Generate a guard declaration │
│ interceptor │ itc │ Generate an interceptor declaration │
│ interface │ itf │ Generate an interface │
│ library │ lib │ Generate a new library within a monorepo │
│ middleware │ mi │ Generate a middleware declaration │
│ module │ mo │ Generate a module declaration │
│ pipe │ pi │ Generate a pipe declaration │
│ provider │ pr │ Generate a provider declaration │
│ resolver │ r │ Generate a GraphQL resolver declaration │
│ resource │ res │ Generate a new CRUD resource │
│ service │ s │ Generate a service declaration │
│ sub-app │ app │ Generate a new application within a monorepo │
└───────────────┴─────────────┴──────────────────────────────────────────────┘
Having your basic Nest-GraphQL server ready, let’s create a resource
nest g res
? What name would you like to use for this resource (plural, e.g., "users")? distros
? What transport layer do you use? GraphQL (code first)
? Would you like to generate CRUD entry points? Yes
CREATE src/distros/distros.module.ts (239 bytes)
CREATE src/distros/distros.resolver.spec.ts (545 bytes)
CREATE src/distros/distros.resolver.ts (1181 bytes)
CREATE src/distros/distros.service.spec.ts (467 bytes)
CREATE src/distros/distros.service.ts (653 bytes)
CREATE src/distros/dto/create-distro.input.ts (198 bytes)
CREATE src/distros/dto/update-distro.input.ts (251 bytes)
CREATE src/distros/entities/distro.entity.ts (189 bytes)
UPDATE src/app.module.ts (886 bytes)
From GraphQL site I got this course link from Hasura.
Requirement | REST | GraphQL |
---|---|---|
Fetching data objects | GET | query |
Writing data | POST | mutation |
Updating/deleting data | PUT/PATCH/DELETE | mutation |
Watching/subscribing to data | - | subscription |
NestJS Endpoint | Controller | Resolver |
So, Query, Mutation, Subscription these three are the keys for GraphQL.
src/distros
├── distros.module.ts
├── distros.resolver.spec.ts
├── distros.resolver.ts
├── distros.service.spec.ts
├── distros.service.ts
├── dto
│ ├── create-distro.input.ts
│ └── update-distro.input.ts
└── entities
└── distro.entity.ts
3 directories, 8 files
distros.module.ts: This is where all the parts of the module comes together. Resolver and Services.
distros.resolver.ts : This is the GraphQL server endpoint defining query, mutation, subscription.
distros.service.ts : This is where the defination of GraphQL endpoints resides, what each function will do and how.
dto : Data transfer Object, this defines how the data should look like. This is for creation and updation.
entities : This is the actual data, the entities for which the complete resource, module revolves around.
.spec.ts : These files are test files.
I am thinking instead of creating an array of distros and fetching those records, how about we setup the prisma with local database with it?
Let’s start with prisma integration!
Prisma to NestJS integration:
Here is my blog post for this.
yarn add prisma -D
npx prisma init
Add Config Module
npm i --save @nestjs/config
Adding a model, as we are working with distros:
model Distro {
id String @id @default(uuid())
name String
version String
releaseYear Int
description String
website String
logo String
}
Migrate the changes:
npx prisma migrate dev --name init
This also installs @prisma/client
if it doesn’t
npm install @prisma/client
Follow the blog, to create prisma.service.ts
Let’s try to make the naming convention same throughout.
This is how distro.service is using prisma
import { Injectable } from "@nestjs/common";
import { Distro, Prisma } from "@prisma/client";
import { PrismaService } from "src/prisma/prisma.service";
@Injectable()
export class DistrosService {
constructor(private prisma: PrismaService) {}
async create(createDistroInput: Prisma.DistroCreateInput): Promise<Distro> {
return this.prisma.distro.create({
data: createDistroInput,
});
}
async findAll(): Promise<Distro[]> {
return this.prisma.distro.findMany();
}
async findOne(id: string): Promise<Distro | null> {
return this.prisma.distro.findUnique({
where: { id },
});
}
async update(
id: string,
updateDistroInput: Prisma.DistroUpdateInput
): Promise<Distro> {
return this.prisma.distro.update({
where: { id },
data: updateDistroInput,
});
}
async remove(id: string): Promise<Distro> {
return this.prisma.distro.delete({
where: { id },
});
}
}
You will face this error quite a lot:
ERROR [ExceptionHandler] Nest can't resolve dependencies of the DistrosService (?). Please make sure that the argument PrismaService at index [0] is available in the DistrosModule context.
Potential solutions:
- Is DistrosModule a valid NestJS module?
- If PrismaService is a provider, is it part of the current DistrosModule?
- If PrismaService is exported from a separate @Module, is that module imported within DistrosModule?
@Module({
imports: [ /* the Module containing PrismaService */ ]
})
Error: Nest can't resolve dependencies of the DistrosService (?). Please make sure that the argument PrismaService at index [0] is available in the DistrosModule context.
Potential solutions:
- Is DistrosModule a valid NestJS module?
- If PrismaService is a provider, is it part of the current DistrosModule?
- If PrismaService is exported from a separate @Module, is that module imported within DistrosModule?
@Module({
imports: [ /* the Module containing PrismaService */ ]
})
at Injector.lookupComponentInParentModules (/Learning_Nestjs/GraphQL/nest-graphql/node_modules/@nestjs/core/injector/injector.js:262:19)
at async Injector.resolveComponentInstance (/Learning_Nestjs/GraphQL/nest-graphql/node_modules/@nestjs/core/injector/injector.js:215:33)
at async resolveParam (/Learning_Nestjs/GraphQL/nest-graphql/node_modules/@nestjs/core/injector/injector.js:129:38)
at async Promise.all (index 0)
at async Injector.resolveConstructorParams (/Learning_Nestjs/GraphQL/nest-graphql/node_modules/@nestjs/core/injector/injector.js:144:27)
at async Injector.loadInstance (/Learning_Nestjs/GraphQL/nest-graphql/node_modules/@nestjs/core/injector/injector.js:70:13)
at async Injector.loadProvider (/Learning_Nestjs/GraphQL/nest-graphql/node_modules/@nestjs/core/injector/injector.js:98:9)
at async /Learning_Nestjs/GraphQL/nest-graphql/node_modules/@nestjs/core/injector/instance-loader.js:56:13
at async Promise.all (index 4)
at async InstanceLoader.createInstancesOfProviders (/Learning_Nestjs/GraphQL/nest-graphql/node_modules/@nestjs/core/injector/instance-loader.js:55:9)
Solution:
Add PrismaService to DistroModule
import { Module } from "@nestjs/common";
import { DistrosService } from "./distros.service";
import { DistrosResolver } from "./distros.resolver";
import { PrismaService } from "src/prisma/prisma.service";
@Module({
providers: [DistrosResolver, DistrosService, PrismaService],
})
export class DistrosModule {}
I guess now the prisma is setup!!
GraphQL Playground
Creation:
The Mutation:
mutation CreateDistro($input: CreateDistroInput!) {
createDistro(createDistroInput: $input) {
name
version
releaseYear
description
website
logo
}
}
Query Variable:
{
"input": {
"name": "Ubuntu",
"version": "22.04 LTS",
"releaseYear": 2022,
"description": "A popular Linux distribution based on Debian",
"website": "https://ubuntu.com",
"logo": "https://example.com/ubuntu-logo.png"
}
}
And Wolaah!
The server returned this response:
{
"data": {
"createDistro": {
"name": "Ubuntu",
"version": "22.04 LTS",
"releaseYear": 2022,
"description": "A popular Linux distribution based on Debian",
"website": "https://ubuntu.com",
"logo": "https://example.com/ubuntu-logo.png"
}
}
}
Great!!!!
Comsumption:
The Query:
query{
distros{
name
description
releaseYear
}
}
The response:
{
"data": {
"distros": [
{
"name": "Ubuntu",
"description": "A popular Linux distribution based on Debian",
"releaseYear": 2022
},
{
"name": "Linux Mint",
"description": "A user-friendly Linux distribution based on Ubuntu",
"releaseYear": 2022
},
{
"name": "Debian",
"description": "A stable and secure Linux distribution",
"releaseYear": 2021
},
{
"name": "CentOS",
"description": "A free and open-source Linux distribution based on Red Hat Enterprise Linux",
"releaseYear": 2019
},
{
"name": "Fedora",
"description": "A community-driven Linux distribution sponsored by Red Hat",
"releaseYear": 2022
},
{
"name": "Arch Linux",
"description": "A flexible and lightweight Linux distribution",
"releaseYear": 2022
},
{
"name": "elementary OS",
"description": "A fast and open-source Linux distribution",
"releaseYear": 2021
}
]
}
}
Updation:
The Mutation:
mutation UpdateDistro($input: UpdateDistroInput!) {
updateDistro(updateDistroInput: $input) {
id
logo
}
}
Query Variable:
{
"input": {
"id": "69289d5b-80fb-4553-81a7-f48be19974d2",
"logo": "https://s3.amazonaws.com/media-p.slid.es/uploads/183933/images/1332684/elementary_logo.png"
}
}
Deletion:
The mutation:
mutation DeleteDistro($id: String!) {
removeDistro(id: $id) {
id
logo
}
}
The query variable:
{
"id": "a529c41f-6099-45d5-88e4-cb8cf4e65243"
}
Yup!! CentOS is not in the list.
The place where I felt unstable was where, I had created the entity, DTOs then I created prisma model. Here prisma has a type for each model.
Like: Prisma.DistroCreateInput
type Prisma.DistroCreateInput = {
id?: string;
name: string;
version: string;
releaseYear: number;
description: string;
website: string;
logo: string;
}
And I had defined this at src/distros/dto/create-distro.input.ts
as
import { InputType, Field } from "@nestjs/graphql";
@InputType()
export class CreateDistroInput {
@Field()
name: string;
@Field()
version: string;
@Field()
releaseYear: number;
@Field()
description: string;
@Field()
website: string;
@Field()
logo: string;
}
So there is double work I’ve done. I guess using the Prisma provided type will be better.
So, my repo is here.