Skip to main content

Example

Let's create a simple todo-item graphql example.

Set up a new nest app

npm i -g @nestjs/cli
nest new nestjs-query-getting-started

Install Dependencies

Install your packages.

note

Be sure to install the correct ORM package!

Install extra dependencies for the example.

npm i pg apollo-server-express

Generate the Module

From the root of your project run:

npx nest g mo todo-item

Create the Entity

From the root of your project run:

npx nest g cl todo-item.entity todo-item --flat

Now lets fill out the entity.

Add the following to src/todo-item/todo-item.entity.ts.

todo-item/todo-item.entity.ts
import {
Column,
CreateDateColumn,
Entity,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';

@Entity()
export class TodoItemEntity {
@PrimaryGeneratedColumn()
id!: string;

@Column()
title!: string;

@Column()
completed!: boolean;

@CreateDateColumn()
created!: Date;

@UpdateDateColumn()
updated!: Date;
}

Create the DTO

The DTO (Data Transfer Object) is used by the resolver to represent incoming requests and outgoing responses.

The DTO is where you can:

  • Define fields that should be rendered by graphql.
  • Define fields that should be filterable using the @FilterableField decorator.
  • Define validation that will be used by mutations.

In this example the DTO and entity are two different classes to clearly demonstrate what is required for graphql vs the persistence layer. However, you can combine the two into a single class.

From the root of your project run:

npx nest g cl todo-item.dto todo-item --flat

Now lets fill out the DTO. Add the following to src/todo-item/todo-item.dto.ts.

todo-item/todo-item.dto.ts
import { FilterableField, IDField } from '@ptc-org/nestjs-query-graphql';
import { ObjectType, GraphQLISODateTime, Field, ID } from '@nestjs/graphql';

@ObjectType('TodoItem')
export class TodoItemDTO {
@IDField(() => ID)
id!: number;

@FilterableField()
title!: string;

@FilterableField()
completed!: boolean;

@Field(() => GraphQLISODateTime)
created!: Date;

@Field(() => GraphQLISODateTime)
updated!: Date;
}


Notice the use of @FilterableField this will let @ptc-org/nestjs-query-graphql know to allow filtering on the corresponding field. If you just use @Field then you will not be able to filter on the corresponding field.

Create the create DTO class.

From the previously created DTO, @ptc-org/nestjs-query-graphql will automatically create a CreateTodoItem graphql type:

input CreateTodoItem {
id: ID!
title: String!
completed: Boolean!
created: DateTime!
updated: DateTime!
}

But in our case, the fields id, created and updated are actually not required when creating a TodoItem: they will be autogenerated. We only need to provide title and completed. To create a DTO that does not require these fields, we can create a custom create DTO:

npx nest g cl todo-item.create.dto todo-item --flat
todo-item/todo-item.create.dto.ts
import { IsBoolean, IsString } from 'class-validator';

@InputType('CreateTodoItem')
export class TodoItemCreateDTO {
@IsString()
@Field()
title!: string;

@IsBoolean()
@Field()
completed!: boolean;
}

Wire everything up.

Update the todo-item.module to set up the NestjsQueryGraphQLModule and the entities to provide a QueryService.

The NestjsQueryGraphQLModule will automatically create a Resolver that will expose the following queries and mutations:

Queries

  • todoItems - find multiple TodoItems.
  • todoItem - find one TodoItem.

Mutations

  • createManyTodoItems - create multiple TodoItems.
  • createOneTodoItems - create one TodoItem.
  • updateManyTodoItems - update multiple TodoItems.
  • updateOneTodoItems - update one TodoItem.
  • deleteManyTodoItems - delete multiple TodoItemss.
  • deleteOneTodoItems - delete one TodoItem.
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { Module } from '@nestjs/common';
import { TodoItemCreateDTO } from './todo-item.create.dto';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';

@Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
// import the NestjsQueryTypeOrmModule to register the entity with typeorm
// and provide a QueryService
imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],
// describe the resolvers you want to expose
resolvers: [
{
EntityClass: TodoItemEntity,
DTOClass: TodoItemDTO,
CreateDTOClass: TodoItemCreateDTO,
},
],
}),
],
})
export class TodoItemModule {}

Next update app.module to set up your db connection and the graphql nest modules.

import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TodoItemModule } from './todo-item/todo-item.module';

@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
database: 'gettingstarted',
username: 'gettingstarted',
autoLoadEntities: true,
synchronize: true,
logging: true,
}),
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
// set to true to automatically generate schema
autoSchemaFile: true,
}),
TodoItemModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}

Create a docker-compose.yml file in the root of the project

version: "3"

services:
postgres:
image: "postgres:11.5"
environment:
- "POSTGRES_USER=gettingstarted"
- "POSTGRES_DB=gettingstarted"
expose:
- "5432"
ports:
- "5432:5432"
# only needed if using mongoose
mongo:
image: "mongo:4.4"
restart: always
ports:
- "27017:27017"
mongo-express:
image: "mongo-express:latest"
restart: always
ports:
- 8081:8081


Running the Example

Start the backing services

docker compose up -d

Start the app

npm start

Visit http://localhost:3000/graphql where you should see the playground

Example playground

Exploring The GraphQL Endpoint

Create a TodoItem

mutation {
createOneTodoItem(
input: { todoItem: { title: "Create One Todo Item", completed: false } }
) {
id
title
completed
created
updated
}
}

Create Multiple TodoItems

mutation {
createManyTodoItems(
input: {
todoItems: [
{ title: "Create Many Todo Items - 1", completed: false }
{ title: "Create Many Todo Items - 2", completed: true }
]
}
) {
id
title
completed
created
updated
}
}

Query For Multiple TodoItems

Query for all todo items

{
todoItems {
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
node {
id
title
completed
created
updated
}
cursor
}
}
}

Query for completed todo items

{
todoItems(filter: { completed: { is: true } }) {
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
node {
id
title
completed
created
updated
}
cursor
}
}
}

Query For One TodoItem

Query by id

{
todoItem(id: 1) {
id
title
completed
created
updated
}
}

Update a TodoItem

Lets update the completed TodoItem we created earlier to not be completed.

mutation {
updateOneTodoItem(input: { id: 3, update: { completed: false } }) {
id
title
completed
created
updated
}
}

Update Multiple TodoItems

Lets update the completed TodoItem we created earlier to not be completed.

mutation {
updateManyTodoItems(
input: { filter: { id: { in: [1, 2] } }, update: { completed: true } }
) {
updatedCount
}
}

You can check this by running the completed query from above.

Delete One TodoItem

Lets update delete the first TodoItem.

mutation {
deleteOneTodoItem(input: { id: 1 }) {
id
title
completed
created
updated
}
}

Delete Many TodoItems

Lets update delete the create many todo items TodoItem using a filter.

mutation {
deleteManyTodoItems(
input: { filter: { title: { like: "Create Many Todo Items%" } } }
) {
deletedCount
}
}