Resolvers
Defining the Resolver
Auto Generated Resolver
The easiest way to get started is to use the @NestjsQueryGraphQLModule
. The NestjsQueryGraphQLModule
will automatically create a CRUDResolver for you.
- TypeOrm
- Sequelize
- Mongoose
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { Module } from '@nestjs/common';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
@Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],
resolvers: [{ DTOClass: TodoItemDTO, EntityClass: TodoItemEntity }],
}),
],
})
export class TodoItemModule {}
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQuerySequelizeModule } from '@ptc-org/nestjs-query-sequelize';
import { Module } from '@nestjs/common';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
@Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQuerySequelizeModule.forFeature([TodoItemEntity])],
resolvers: [{ DTOClass: TodoItemDTO, EntityClass: TodoItemEntity }],
}),
],
})
export class TodoItemModule {}
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryMongooseModule } from '@ptc-org/nestjs-query-mongoose';
import { Module } from '@nestjs/common';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
@Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [
NestjsQueryMongooseModule.forFeature([
{ document: TodoItemEntity, name: TodoItemEntity.name, schema: TodoItemEntitySchema },
]),
],
resolvers: [{ DTOClass: TodoItemDTO, EntityClass: TodoItemEntity }],
}),
],
})
export class TodoItemModule {}
CRUDResolver
If you want to override auto generated queries or mutations you can use the CRUDResolver
to manually define your
resolver.
Resolvers work the same as they do in @nestjs/graphql
by annotating
your class with @Resolver
.
In this example the DTO and entity are the same shape, if you have a case where they are different or have computed fields check out Assemblers to understand how to convert to and from the DTO/Entity.
When using the @Resolver
decorator from @nestjs/graphql
you must use the following
@Resolver(() => DTOClass)
otherwise relations will not work.
import { QueryService, InjectQueryService } from '@ptc-org/nestjs-query-core';
import { CRUDResolver } from '@ptc-org/nestjs-query-graphql';
import { Resolver, Query, Args } from '@nestjs/graphql';
import { TodoItemDTO } from './dto/todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
@Resolver(() => TodoItemDTO)
export class TodoItemResolver extends CRUDResolver(TodoItemDTO) {
constructor(
@InjectQueryService(TodoItemEntity) readonly service: QueryService<TodoItemEntity>
) {
super(service);
}
}
To ensure that all the correct providers are setup (e.g. hooks, assemblers, and authorizers) you also need to
register your DTOs with the NestjsQueryGraphQLModule
.
Notice how the dtos
property is specified instead of the resolvers, this allows you to specify your DTOClass
,
CreateDTOClass
, and UpdateDTOClass
without creating an auto-generated resolver.
- TypeOrm
- Sequelize
- Mongoose
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { Module } from '@nestjs/common';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
import { TodoItemResolver } from './todo-item.resolver'
@Module({
providers: [TodoItemResolver]
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],
dtos: [{ DTOClass: TodoItemDTO }],
}),
],
})
export class TodoItemModule {}
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQuerySequelizeModule } from '@ptc-org/nestjs-query-sequelize';
import { Module } from '@nestjs/common';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
import { TodoItemResolver } from './todo-item.resolver'
@Module({
providers: [TodoItemResolver]
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQuerySequelizeModule.forFeature([TodoItemEntity])],
dtos: [{ DTOClass: TodoItemDTO }],
}),
],
})
export class TodoItemModule {}
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryMongooseModule } from '@ptc-org/nestjs-query-mongoose';
import { Module } from '@nestjs/common';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
import { TodoItemResolver } from './todo-item.resolver'
@Module({
providers: [TodoItemResolver]
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [
NestjsQueryMongooseModule.forFeature([
{ document: TodoItemEntity, name: TodoItemEntity.name, schema: TodoItemEntitySchema },
]),
],
dtos: [{ DTOClass: TodoItemDTO }],
}),
],
})
export class TodoItemModule {}
All of the subsequent examples omit the module definition for custom resolvers but you should still register your DTOs to ensure all the providers are set up properly.
Generated Endpoints
When using the auto-generated resolver or extending CRUDResolver
the methods that will be exposed for the TodoItemDTO
are:
todoItem
- Find a singleTodoItem
by id.todoItems
- Filter, sort, and pageTodoItems
createOneTodoItem
- Create a singleTodoItem
createManyTodoItems
- Create multipleTodoItems
.updateOneTodoItem
- Update a singleTodoItem
by id.updateManyTodoItems
- Update multipleTodoItems
using a filter.deleteOneTodoItem
- Delete a singleTodoItem
by id.deleteManyTodoItems
- Delete multipleTodoItems
using a filter.
You can read more about the methods in the Queries and Mutations docs.
Options
When using NestjsQueryGraphQLModule
or CRUDResolver
you can define a number of options to control your endpoints.
-
CreateDTOClass
- The input DTO to use for create mutations. See Create and Update DTOs -
UpdateDTOClass
- The input DTO to use for update mutations. See Create and Update DTOs -
enableSubscriptions?
- Set totrue
to enable graphql subscriptions. See Subscriptions. -
enableAggregate?
- When set to true an aggregate query will be enabled on the type and all relations (unless the explicitly disable it). See Aggregation -
create
- In addition toResolverOptions
you can also specify the followingCreateDTOClass
- The input DTO to use for create mutations.CreateOneInput
- TheInputType
to use for create one mutations.CreateManyInput
- TheInputType
to use for create many mutations.
-
read
- In addition toResolverOptions
you can also specify the followingQueryArgs
- Specify to override the auto-generatedArgsType
to use to filter records inqueryMany
endpoint.
-
update
- In addition toResolverOptions
you can also specify the followingUpdateDTOClass
- The input DTO to use for update mutations.UpdateOneInput
- TheInputType
to use for update one mutations.UpdateManyInput
- TheInputType
to use for update many mutations.
-
delete
- In addition toResolverOptions
you can also specify the followingDeleteOneInput
- TheInputType
to use for delete one mutations.DeleteManyInput
- TheInputType
to use for delete many mutations.
-
aggregate
- In addition toResolverOptions
you can also specify the followingenabled
- Set to true to enable aggregations. If this is used in place of the rootenableAggregate
option relations will not have aggregate queries exposed.
ResolverOptions
The create
, read
, update
, and delete
options above all accept the following options.
dtoName
- Set to override the default name (the name passed to@ObjectType
or the name of the class).disabled=false
- Set to true to disable all endpoints.guards=[]
- An array of guards to add to all endpoints.interceptors=[]
- An array of interceptors to add to all endpoints.pipes=[]
- An array of pipes to add to all endpoints.filters=[]
- An array of filters to add to all endpoints.decorators=[]
- An array of customPropertyDecorator
orMethodDecorators
to add to the endpoint.enableSubscriptions?
- Set totrue
to enable graphql subscriptions. See Subscriptions.one
,many
- Both theone
andmany
accept the following options:name?
- Override the endpoint name.disabled=false
- Set to true to disable the endpoint.enableSubscriptions?
- Set totrue
to enable graphql subscriptions. See Subscriptions.guards=[]
- An array of guards to add to the endpoint.interceptors=[]
- An array of interceptors to add to the endpoint.pipes=[]
- An array of pipes to add to the endpoint.filters=[]
- An array of filters to add to the endpoint.decorators=[]
- An array of customPropertyDecorator
orMethodDecorators
to add to the endpoint.
Examples
Create and Update DTOs.
There may be times when you want to specify certain validation or only allow certain fields when updating or creating records.
To allow for this you can specify the CreateDTOClass
and UpdateDTOClass
options.
Example
In this example we'll create a new TodoItemInputDTO
that adds validation and limits the fields you can modify.
Assume we have the following class todo-item.input.ts
which omits the id
, created
, and updated
fields from
the original DTO.
import { Field, InputType } from '@nestjs/graphql';
import { IsString, Length } from 'class-validator';
@InputType('TodoItemInput')
export class TodoItemInputDTO {
@Field()
// ensure it is a string field
@IsString()
// min length of 5 and max of 5 characters
@Length(5, 50)
title!: string;
@Field()
completed!: boolean;
}
We can then update our resolver to use the new TodoItemInputDTO
- NestjsQueryGraphQLModule
- CRUDResolver
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { Module } from '@nestjs/common';
import { TodoItemInputDTO } from './todo-item.input';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
@Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],
resolvers: [{
DTOClass: TodoItemDTO,
EntityClass: TodoItemEntity,
CreateDTOClass: TodoItemInputDTO,
UpdateDTOClass: TodoItemInputDTO,
}],
}),
],
})
export class TodoItemModule {}
import { QueryService, InjectQueryService } from '@ptc-org/nestjs-query-core';
import { CRUDResolver } from '@ptc-org/nestjs-query-graphql';
import { Resolver } from '@nestjs/graphql';
import { TodoItemInputDTO } from './todo-item.input';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
@Resolver()
export class TodoItemResolver extends CRUDResolver(TodoItemDTO, {
CreateDTOClass: TodoItemInputDTO,
UpdateDTOClass: TodoItemInputDTO,
}) {
constructor(
@InjectQueryService(TodoItemEntity) readonly service: QueryService<TodoItemEntity>
) {
super(service);
}
}
Disabling Endpoints.
There may be scenarios where you wish to disable certain methods.
Using the options describe above we can disable different actions.
In this example we disable all create
endpoints
- NestjsQueryGraphQLModule
- CRUDResolver
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { Module } from '@nestjs/common';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
@Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],
resolvers: [{
DTOClass: TodoItemDTO,
EntityClass: TodoItemEntity,
create: { disabled: true }
}],
}),
],
})
export class TodoItemModule {}
import { QueryService, InjectQueryService } from '@ptc-org/nestjs-query-core';
import { CRUDResolver } from '@ptc-org/nestjs-query-graphql';
import { Resolver } from '@nestjs/graphql';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
@Resolver()
export class TodoItemResolver extends CRUDResolver(TodoItemDTO, {
create: { disabled: true },
}) {
constructor(
@InjectQueryService(TodoItemEntity) readonly service: QueryService<TodoItemEntity>
) {
super(service);
}
}
You can also disable individual endpoints.
In this example we disable all many
mutations. This will prevent createManyTodoItems
, updateManyTodoItems
,
deleteManyTodoItems
from being exposed in the graphql schema.
NOTE The same pattern applies for disabling the one
endpoints.
- NestjsQueryGraphQLModule
- CRUDResolver
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { Module } from '@nestjs/common';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
@Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],
resolvers: [{
DTOClass: TodoItemDTO,
EntityClass: TodoItemEntity,
create: { many: { disabled: true } },
update: { many: { disabled: true } },
delete: { many: { disabled: true } },
}],
}),
],
})
export class TodoItemModule {}
import { QueryService, InjectQueryService } from '@ptc-org/nestjs-query-core';
import { CRUDResolver } from '@ptc-org/nestjs-query-graphql';
import { Resolver } from '@nestjs/graphql';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
@Resolver()
export class TodoItemResolver extends CRUDResolver(TodoItemDTO, {
create: { many: { disabled: true } },
update: { many: { disabled: true } },
delete: { many: { disabled: true } },
}) {
constructor(
@InjectQueryService(TodoItemEntity) readonly service: QueryService<TodoItemEntity>
) {
super(service);
}
}
Guards, Pipes, Filters, and Interceptors
In this section we'll just demonstrate using a guard
but the same pattern applies for pipes
, filters
and
interceptors
To set up a guard for endpoint you can use the guards
option.
Assume we have the following auth guard that checks for a certain header and value.
import {
CanActivate,
ExecutionContext,
Injectable,
Logger,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AUTH_HEADER_NAME } from './constants';
import { config } from './config';
@Injectable()
export class AuthGuard implements CanActivate {
private logger = new Logger(AuthGuard.name);
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const ctx = GqlExecutionContext.create(context);
const req = ctx.getContext().request;
this.logger.log(`Req = ${req.headers}`);
return req.headers[AUTH_HEADER_NAME] === config.auth.header;
}
}
We can then add it to each of our mutation endpoints
- NestjsQueryGraphQLModule
- CRUDResolver
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { Module } from '@nestjs/common';
import { AuthGuard } from '../auth.guard';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
const guards = [AuthGuard];
@Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],
resolvers: [{
DTOClass: TodoItemDTO,
EntityClass: TodoItemEntity,
create: { guards },
update: { guards },
delete: { guards },
}],
}),
],
})
export class TodoItemModule {}
import { QueryService, InjectQueryService } from '@ptc-org/nestjs-query-core';
import { CRUDResolver } from '@ptc-org/nestjs-query-graphql';
import { Resolver } from '@nestjs/graphql';
import { AuthGuard } from '../auth.guard';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
const guards = [AuthGuard];
@Resolver()
export class TodoItemResolver extends CRUDResolver(TodoItemDTO, {
create: { guards },
update: { guards },
delete: { guards },
}) {
constructor(
@InjectQueryService(TodoItemEntity) readonly service: QueryService<TodoItemEntity>
) {
super(service);
}
}
Now any requests that go to a create
, update
or delete
method will require the guard.
You can also apply to individual methods using the one
and many
fields. For example lets put a guard on all many
mutations.
- NestjsQueryGraphQLModule
- CRUDResolver
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { Module } from '@nestjs/common';
import { AuthGuard } from '../auth.guard';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
const guards = [AuthGuard];
@Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],
resolvers: [{
DTOClass: TodoItemDTO,
EntityClass: TodoItemEntity,
create: { many: { guards } },
update: { many: { guards } },
delete: { many: { guards } },
}],
}),
],
})
export class TodoItemModule {}
import { QueryService, InjectQueryService } from '@ptc-org/nestjs-query-core';
import { CRUDResolver } from '@ptc-org/nestjs-query-graphql';
import { Resolver } from '@nestjs/graphql';
import { AuthGuard } from '../auth.guard';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
const guards = [AuthGuard];
@Resolver()
export class TodoItemResolver extends CRUDResolver(TodoItemDTO, {
create: { many: { guards } },
update: { many: { guards } },
delete: { many: { guards } },
}) {
constructor(
@InjectQueryService(TodoItemEntity) readonly service: QueryService<TodoItemEntity>
) {
super(service);
}
}
Override Endpoint Name
If you find yourself in a situation where you want to override an endpoint name you can use the one.name
or many.name
options to override
These options are available for the create
, read
, update
, and 'delete' endpoints.
In this example we'll change the todoItem
query to findTodoItem
and the todoItems
endpoint to queryForTodoItems
.
- NestjsQueryGraphQLModule
- CRUDResolver
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { Module } from '@nestjs/common';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
@Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],
resolvers: [{
DTOClass: TodoItemDTO,
EntityClass: TodoItemEntity,
read: { one: { name: 'findTodoItem' }, many: { name: 'queryForTodoItems' } },
}],
}),
],
})
export class TodoItemModule {}
import { QueryService, InjectQueryService } from '@ptc-org/nestjs-query-core';
import { CRUDResolver } from '@ptc-org/nestjs-query-graphql';
import { Resolver } from '@nestjs/graphql';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
@Resolver()
export class TodoItemResolver extends CRUDResolver(TodoItemDTO, {
read: { one: { name: 'findTodoItem' }, many: { name: 'queryForTodoItems' } },
}) {
constructor(
@InjectQueryService(TodoItemEntity) readonly service: QueryService<TodoItemEntity>
) {
super(service);
}
}
Individual Resolvers
The @ptc-org/nestjs-query-graphql
package exposes each part of CRUD
into individual mixins and resolvers allowing you
to pick and choose what functionality you want to expose.
This is advanced usage of the resolvers API and is subject to change!
- All examples below can be achieved with resolver options.
- The following resolvers do not expose relations options, to add relations options see Relateable
CreateResolver
The CreateResolver
will only expose the createOne
and createMany
endpoints. The options described for
create
can be passed to the CreateResolver
For example the following resolver will expose the createOneTodoItem
and createManyTodoItems
mutations.
import { QueryService, InjectQueryService } from '@ptc-org/nestjs-query-core';
import { CreateResolver } from '@ptc-org/nestjs-query-graphql';
import { Resolver } from '@nestjs/graphql';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
@Resolver()
export class TodoItemResolver extends CreateResolver(TodoItemDTO) {
constructor(
@InjectQueryService(TodoItemEntity) readonly service: QueryService<TodoItemEntity>
) {
super(service);
}
}
ReadResolver
The ReadResolver
will only expose the query
and findById
endpoints. The options described for read
can be passed to the ReadResolver
For example the following resolver will expose the todoItems
and todoItem
queries.
import { QueryService, InjectQueryService } from '@ptc-org/nestjs-query-core';
import { ReadResolver } from '@ptc-org/nestjs-query-graphql';
import { Resolver } from '@nestjs/graphql';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
@Resolver()
export class TodoItemResolver extends ReadResolver(TodoItemDTO) {
constructor(
@InjectQueryService(TodoItemEntity) readonly service: QueryService<TodoItemEntity>
) {
super(service);
}
}
UpdateResolver
The UpdateResolver
will only expose the updateOne
and updateMany
endpoints. The options described for
update
can be passed to the UpdateResolver
For example the following resolver will expose the updateOneTodoItem
and updateManyTodoItems
mutations.
import { QueryService, InjectQueryService } from '@ptc-org/nestjs-query-core';
import { UpdateResolver } from '@ptc-org/nestjs-query-graphql';
import { Resolver } from '@nestjs/graphql';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
@Resolver()
export class TodoItemResolver extends UpdateResolver(TodoItemDTO) {
constructor(
@InjectQueryService(TodoItemEntity) readonly service: QueryService<TodoItemEntity>
) {
super(service);
}
}
DeleteResolver
The DeleteResolver
will only expose the deleteOne
and deleteMany
endpoints. The options described for
delete
can be passed to the DeleteResolver
For example the following resolver will expose the updateOneTodoItem
and updateManyTodoItems
mutations.
import { QueryService, InjectQueryService } from '@ptc-org/nestjs-query-core';
import { DeleteResolver } from '@ptc-org/nestjs-query-graphql';
import { Resolver } from '@nestjs/graphql';
import { TodoItemDTO } from './todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';
@Resolver()
export class TodoItemResolver extends DeleteResolver(TodoItemDTO) {
constructor(
@InjectQueryService(TodoItemEntity) readonly service: QueryService<TodoItemEntity>
) {
super(service);
}
}
Custom Endpoints
You can also create custom methods.
Unless you are overriding an endpoint you DO NOT need to extend the crud resolver directly, instead you can create a
new resolver for your type and add the new endpoint. @nestjs/graphql
will handle merging the two resolver into one.
Lets create a new query endpoint that only returns completed TodoItems
.
First create a file named types.ts
. And add the following.
import { QueryArgsType } from '@ptc-org/nestjs-query-graphql';
import { ArgsType } from '@nestjs/graphql';
import { TodoItemDTO } from './dto/todo-item.dto';
@ArgsType()
export class TodoItemQuery extends QueryArgsType(TodoItemDTO) {}
export const TodoItemConnection = TodoItemQuery.ConnectionType;
In the code above we export two types. TodoItemConnection
and TodoItemQuery
. Because of the way @nestjs/graphql
and
nest
work we need to extend the QueryArgsType
so that it will know the type to serialize into.
In your resolver you can now create a new completedTodoItems
method with the following:
import { Filter, InjectAssemblerQueryService, QueryService } from '@ptc-org/nestjs-query-core';
import { ConnectionType } from '@ptc-org/nestjs-query-graphql';
import { Args, Query, Resolver } from '@nestjs/graphql';
import { TodoItemDTO } from './dto/todo-item.dto';
import { TodoItemAssembler } from './todo-item.assembler';
import { TodoItemConnection, TodoItemQuery } from './types';
@Resolver(() => TodoItemDTO)
export class TodoItemResolver {
constructor(@InjectQueryService(TodoItemEntity) readonly service: QueryService<TodoItemEntity>) {}
// Set the return type to the TodoItemConnection
@Query(() => TodoItemConnection)
completedTodoItems(@Args() query: TodoItemQuery): Promise<ConnectionType<TodoItemDTO>> {
// add the completed filter the user provided filter
const filter: Filter<TodoItemDTO> = {
...query.filter,
...{ completed: { is: true } },
};
return TodoItemConnection.createFromPromise((q) => this.service.query(q), { ...query, ...{ filter } });
}
Lets break this down so you know what is going on.
In the above code we annotate the new method with
@Query(() => TodoItemConnection)
The return type passed to query lets graphql know what the generated schema type is.
The next piece to pay attention to is
completedTodoItems(@Args() query: TodoItemQuery)
We use the TodoItemQuery
we created for the arguments type and annotate with @Args
when you look at the generated
schema in it will look like.
completedTodoItems(
paging: CursorPaging = {}
filter: TodoItemFilter = {}
sorting: [TodoItemSort!] = []
): TodoItemConnection!
Notice how there is not a query arg but instead the you see the fields of TodoItemQuery
that is because we used
@Args
without a name and added the @ArgsType
decorator to the TodoItemQuery
.
The next piece is
// add the completed filter the user provided filter
const filter: Filter<TodoItemDTO> = {
...query.filter,
...{ completed: { is: true } },
};
Here we do a shallow copy of the filter
and add completed: { is: true }
. This will override any completed arguments
that an end user may have provided to ensure we always query for completed todos.
Finally we call create our connection response by using the createFromPromise
method on the connection.
// call the original queryMany method with the new query
return TodoItemConnection.createFromPromise((q) => this.service.query(q), { ...query, ...{ filter } });
The last step is to add the resolver to the module, by registering our resolver as a provider and importing the
NestjsQueryGraphQLModule
we will get both the auto generated resolver along with the custom endpoints from the
custom resolver.
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { Module } from '@nestjs/common';
import { TodoItemDTO } from './dto/todo-item.dto';
import { TodoItemAssembler } from './todo-item.assembler';
import { TodoItemEntity } from './todo-item.entity';
import { TodoItemResolver } from './todo-item.resolver';
@Module({
providers: [TodoItemResolver],
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],
assemblers: [TodoItemAssembler],
resolvers: [
{
DTOClass: TodoItemDTO,
EntityClass: TodoItemEntity,
},
],
}),
],
})
export class TodoItemModule {}