DTOs
The query-graphql package leverages most decorators from @nestjs/graphql and TypeGraphQL, with the exception of FilterableField.
@FilterableField#
The FilterableField is very similar to the Field from
TypeGraphQL, however it allows you to specify the fields that should be filterable when querying.
note
If you use the @nestjs/graphql Field decorator it will not be exposed in the query type for the DTO.
Options#
In addition to the normal field options you can also specify the following options
allowedComparisons- An array of allowed comparisons. You can use this option to allow a subset of filter comparisons when querying through graphql.- This option is useful if the field is expensive to query on for certain operators, or your data source supports a limited set of comparisons.
filterRequired- When set totruethe field will be required whenever afilteris used. Thefilterrequirement applies to allread,update, anddeleteendpoints that use afilter.- The
filterRequiredoption is useful when your entity has an index that requires a subset of fields to be used to provide certain level of query performance. - NOTE: When a field is a required in a filter the default
filteroption is ignored.
- The
filterOnly- When set totrue, the field will only appear asfilterbut isn't included as field inside theObjectType.- This option is useful if you want to filter on foreign keys without resolving the relation but you don't want to have the foreign key show up as field in your query type for the DTO. This might be especially useful for federated relations
Example#
In the following example we allow id, title, and completed to be used in queries.
import { FilterableField, IDField } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
@ObjectType('TodoItem')export class TodoItemDTO { @IDField(() => ID) id!: string;
@FilterableField() title!: string;
@FilterableField() completed!: boolean;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}
Example - allowedComparisons#
In the following example the allowedComparisons option is demonstrated by restricting the comparisons that are allowed when filtering on certain fields.
For the id field only eq, neq, in, and notIn comparisons will be exposed in the schema.
The title field will only allow eq, like, and notLike.
import { FilterableField } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
@ObjectType('TodoItem')export class TodoItemDTO { @IDField(() => ID, { allowedComparisons: ['eq', 'neq', 'in', 'notIn'] }) id!: string;
@FilterableField({ allowedComparisons: ['eq', 'like', 'notLike'] }) title!: string;
@FilterableField() completed!: boolean;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}
Example - filterRequired#
In the following example the filterRequired option is applied to the completed field, ensuring that all endpoints that use a filter will require a comparison on the completed field.
import { FilterableField, IDField } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
@ObjectType('TodoItem')export class TodoItemDTO { @IDField(() => ID) id!: string;
@FilterableField() title!: string;
@FilterableField({ filterRequired: true }) completed!: boolean;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}Example - filterOnly#
In the following example the filterOnly option is applied to the assigneeId field, which makes a query filterable
by the id of an assigned user but won't return the assigneeId as field.
import { FilterableField, IDField } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
@ObjectType('TodoItem')@Relation('assignee', () => UserDTO)export class TodoItemDTO { @IDField(() => ID) id!: string;
@FilterableField() title!: string;
@FilterableField() completed!: boolean;
@FilterableField({ filterOnly: true }) assigneeId!: string;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}@IDField#
By default nestjs-query uses the default graphql ID scalar, if you need to use a different graphql scalar
type you can @IDField decorator. nestjs-query will scalar type passed to the @IDField for all auto-generated
query and mutation endpoints that rely on an input for the id (e.g. findById, updateOne, deleteOne).
note
The @IDField uses the same options as @FilterableField.
info
You may have seen @IDField in various examples throughout the docs, this is because we recommend using @IDField
by default. In the future if you need to change the type later on it should be a trivial change to find all fields
that use the @IDField decorator to update.
Example#
A common use case is to obscure an auto-incremented primary key.
In this example we'll do a simple version of that by declaring a new ID scalar that will base64 encode all ids.
import { Scalar, CustomScalar } from '@nestjs/graphql';import { Kind, ValueNode } from 'graphql';
@Scalar('CustomID')export class CustomIDScalar implements CustomScalar<string, number> { description = 'ID custom scalar type';
private idPrefix = 'id:';
parseValue(value: string): number { // parse a `base64` encoded id from the client when provided as a variable return parseInt(Buffer.from(value, 'base64').toString('utf8').replace(this.idPrefix, ''), 10); }
serialize(value: number): string { // serialize a number into the base64 representation return Buffer.from(`${this.idPrefix}${value}`, 'utf8').toString('base64'); }
parseLiteral(ast: ValueNode): number | null { // parse a `base64` encoded id from the client when hardcoded into the query if (ast.kind === Kind.STRING) { return this.parseValue(ast.value); } return null; }}
Now lets register our CustomID scalar with nestjs.
import { Module } from '@nestjs/common';import { CustomIDScalar } from './custom-id.scalar';
@Module({ providers: [CustomIDScalar],})export class CommonModule {}Once your CustomIDScalar is registered you can use it in your DTOS.
import { FilterableField, IDField } from '@nestjs-query/query-graphql';import { ObjectType, GraphQLISODateTime } from '@nestjs/graphql';import { CustomIDScalar } from '../../common/custom-id.scalar';
@ObjectType('TodoItem')export class TodoItemDTO { @IDField(() => CustomIDScalar) id!: string;
@FilterableField() title!: string;
@FilterableField({ nullable: true }) description?: string;
@FilterableField() completed!: boolean;
@FilterableField(() => GraphQLISODateTime, { filterOnly: true }) created!: Date;
@FilterableField(() => GraphQLISODateTime, { filterOnly: true }) updated!: Date;}Now all graphql endpoints that need to use an id to query or mutate a TodoItem will use the CustomIDScalar
type for the input.
Example - Disable Filtering#
If you want to disable filtering and sorting on the id field you can use the disableFilter option.
import { FilterableField, IDField } from '@nestjs-query/query-graphql';import { ObjectType, GraphQLISODateTime } from '@nestjs/graphql';import { CustomIDScalar } from '../../common/custom-id.scalar';
@ObjectType('TodoItem')export class TodoItemDTO { @IDField(() => CustomIDScalar, { disableFilter: true }) id!: string;
@FilterableField() title!: string;
@FilterableField({ nullable: true }) description?: string;
@FilterableField() completed!: boolean;
@FilterableField(() => GraphQLISODateTime, { filterOnly: true }) created!: Date;
@FilterableField(() => GraphQLISODateTime, { filterOnly: true }) updated!: Date;}@QueryOptions#
The @QueryOptions decorator can be used to override any defaults for querying functionality such as sorting,
filtering, paging strategy, etc.
Setting a default Filter#
When querying the default filter is empty. You can specify a default filter by using the QueryOptions decorator on
your DTO option.
note
The default filter is only used when a filter is not provided in a query.
In this example we specify the default filter to be completed IS TRUE.
import { FilterableField, IDField, QueryOptions } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
@ObjectType('TodoItem')@QueryOptions({ defaultFilter: { completed: { is: true } } })export class TodoItemDTO { @IDField(() => ID) id!: string;
@FilterableField() title!: string;
@FilterableField() completed!: boolean;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}
Result Page Size#
By default all results will be limited to 10 records.
To override the default you can override the default page size by setting the defaultResultSize option.
In this example we specify the defaultResultSize to 5 which means if a page size is not specified 5 results will be
returned.
import { FilterableField, IDField, QueryOptions } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
@ObjectType('TodoItem')@QueryOptions({ defaultResultSize: 5 })export class TodoItemDTO { @IDField(() => ID) id!: string;
@FilterableField() title!: string;
@FilterableField() completed!: boolean;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}
Limiting Results Size#
By default the max number records that can be returned is 50.
To override the default you can override the following options specifying the maxResultSize option.
note
You can disable maxResultSize by setting the option to -1.
In this example we specify limit the max number of records an end user can request to 20.
import { FilterableField, IDField, QueryOptions } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
@ObjectType('TodoItem')@QueryOptions({ maxResultsSize: 20 })export class TodoItemDTO { @IDField(() => ID) id!: string;
@FilterableField() title!: string;
@FilterableField() completed!: boolean;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}
Paging Strategy#
By default nestjs-query uses a cursor based paging strategy and returns a connection for all query many endpoints.
For a more in-depth overview of paging check out the paging docs
You can override the default pagingStrategy to one of the following alternatives
OFFSET- sets paging to allowlimitandoffsetfields, and returns anOffsetConnection.NONE- turn off all paging and always return anArrayConnection.
When using the OFFSET strategy your the paging arguments for a many query will accept a limit and/or offset.
This will also change the return type from a CursorConnection to an OffsetConnection.
import { FilterableField, IDField, QueryOptions, PagingStrategies } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
@ObjectType('TodoItem')@QueryOptions({ pagingStrategy: PagingStrategies.OFFSET })export class TodoItemDTO { @IDField(() => ID) id!: string;
@FilterableField() title!: string;
@FilterableField() completed!: boolean;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}
To disable paging entirely you can use the NONE pagingStrategy. When using NONE an ArrayConnection will be returned.
import { FilterableField, IDField, QueryOptions, PagingStrategies } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
@ObjectType('TodoItem')@QueryOptions({ pagingStrategy: PagingStrategies.NONE })export class TodoItemDTO { @IDField(() => ID) id!: string;
@FilterableField() title!: string;
@FilterableField() completed!: boolean;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}
Paging with Total Count#
note
This section ONLY applies to CURSOR and OFFSET connections.
warning
Enabling totalCount can be expensive. If your table is large the totalCount query may be expensive, use with caution.
info
The totalCount field is not eagerly fetched. It will only be executed if the field is queried from the client.
When using the CURSOR (the default) or OFFSET paging strategy you have the option to expose a totalCount field to
allow clients to fetch a total count of records in a connection.
To enable the totalCount field for connections set the enableTotalCount option to true using the
@QueryOptions decorator.
import { FilterableField, IDField, QueryOptions, PagingStrategies } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
@ObjectType('TodoItem')@QueryOptions({ enableTotalCount: true })export class TodoItemDTO { @IDField(() => ID) id!: string;
@FilterableField() title!: string;
@FilterableField() completed!: boolean;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}
When setting enableTotalCount to true you will be able to query for totalCount on cursor or offset connections
- GraphQL
- Response
{ todoItems { totalCount pageInfo { hasNextPage hasPreviousPage startCursor endCursor } edges { node { id title description } } }}
{ "data": { "todoItems": { "totalCount": 5, "pageInfo": { "hasNextPage": false, "hasPreviousPage": false, "startCursor": "YXJyYXljb25uZWN0aW9uOjA=", "endCursor": "YXJyYXljb25uZWN0aW9uOjQ=" }, "edges": [ { "node": { "id": "1", "title": "Create Nest App", "description": null } }, { "node": { "id": "2", "title": "Create Entity", "description": null } }, { "node": { "id": "3", "title": "Create Entity Service", "description": null } }, { "node": { "id": "4", "title": "Add Todo Item Resolver", "description": null } }, { "node": { "id": "5", "title": "How to create item With Sub Tasks", "description": null } } ] } }}Default Sort#
When querying the default sort is based on the persistence layer. You can override the default by providing the defaultSort option.
In this example we specify the default sort to be by title.
import { FilterableField, IDField, QueryOptions, PagingStrategies } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
@ObjectType('TodoItem')@QueryOptions({ defaultSort: [{ field: 'title', direction: SortDirection.ASC }] })export class TodoItemDTO { @IDField(() => ID) id!: string;
@FilterableField() title!: string;
@FilterableField() completed!: boolean;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}
Allowed Boolean Expressions#
When filtering you can provide and and or expressions to provide advanced filtering. You can turn off either by
using the allowedBooleanExpressions option.
In this example we will only allow and expressions.
import { FilterableField, IDField, QueryOptions, PagingStrategies } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
@ObjectType('TodoItem')@QueryOptions({ allowedBooleanExpressions: ['and'] })export class TodoItemDTO { @IDField(() => ID) id!: string;
@FilterableField() title!: string;
@FilterableField() completed!: boolean;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}
To turn off all boolean expressions you can set allowedBooleanExpressions to an empty array. This is useful if you
only allow filtering on certain fields and you want to disable all complex filtering.
In this example we will only allow eq comparisons on the id field and disable all and/or boolean expressions.
import { FilterableField, IDField, QueryOptions, PagingStrategies } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
@ObjectType('TodoItem')@QueryOptions({ allowedBooleanExpressions: ['and'] })export class TodoItemDTO { @IDField(() => ID, { allowedComparisons: ['eq'] }) id!: string;
@Field() title!: string;
@Field() completed!: boolean;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}