Skip to main content

Example

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

Set up a new nest app#

npm i -g @nestjs/clinest 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

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

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 '@nestjs-query/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 @nestjs-query/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.

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 '@nestjs-query/query-graphql';import { NestjsQueryTypeOrmModule } from '@nestjs-query/query-typeorm';import { Module } from '@nestjs/common';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: [{ DTOClass: TodoItemDTO, EntityClass: TodoItemEntity }],    }),  ],})export class TodoItemModule {}

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

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({      // 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  }}