DTOs
DTOs (Data Transfer Objects) define the structure of data transferred between client and server. In Type GraphQL Query, DTOs are TypeScript classes decorated with GraphQL decorators.
What You’ll Learn
Section intitulée « What You’ll Learn »This guide will help you understand:
- ✅ How to define DTOs using decorators
- ✅ The difference between
@EnhancedFieldand@Field - ✅ How to configure field options (filterable, sortable, searchable, localizable)
- ✅ How to use
@IDFieldfor ID fields - ✅ Best practices for DTO design
Overview
Section intitulée « Overview »The type-graphql-query package leverages most decorators from type-graphql, with the addition of EnhancedField and IDField decorators that provide enhanced capabilities.
@EnhancedField
Section intitulée « @EnhancedField »The EnhancedField decorator is similar to the Field decorator from TypeGraphQL, however it allows you to specify additional options for filtering, searching, sorting, and localization.
In addition to the normal field options from TypeGraphQL, you can also specify the following options:
| Option | Type | Description |
|---|---|---|
allowedComparisons |
string[] |
Array of allowed comparisons (e.g., ['eq', 'like', 'in']) |
filterOnly |
boolean |
Field appears only in filters, not in the ObjectType |
filterable |
boolean |
Include field in filter type (default: true) |
sortable |
boolean |
Field can be used for sorting (default: true) |
searchable |
boolean | SearchableFieldOptions |
Enable full-text search |
localizable |
boolean |
Enable multi-language support |
Basic Example
Section intitulée « Basic Example »In the following example we allow id, title, and completed to be used in queries with filtering, sorting, and searching capabilities.
import { EnhancedField, IDField } from '@lib/type-graphql-query';import { ObjectType, ID, GraphQLISODateTime, Field } from '@lib/type-graphql';
@ObjectType('TodoItem')export class TodoItemDTO { @IDField(() => ID) id!: string;
@EnhancedField() title!: string;
@EnhancedField() completed!: boolean;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}Example: allowedComparisons
Section intitulée « Example: allowedComparisons »Restrict 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 { EnhancedField, IDField } from '@lib/type-graphql-query';import { ObjectType, ID, GraphQLISODateTime, Field } from '@lib/type-graphql';
@ObjectType('TodoItem')export class TodoItemDTO { @IDField(() => ID, { allowedComparisons: ['eq', 'neq', 'in', 'notIn'] }) id!: string;
@EnhancedField({ allowedComparisons: ['eq', 'like', 'notLike'] }) title!: string;
@EnhancedField() completed!: boolean;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}Example: filterOnly
Section intitulée « Example: filterOnly »The filterOnly option makes a field filterable but doesn’t return it as a field in queries.
import { EnhancedField, IDField } from '@lib/type-graphql-query';import { ObjectType, ID, GraphQLISODateTime, Field } from '@lib/type-graphql';
@ObjectType('TodoItem')export class TodoItemDTO { @IDField(() => ID) id!: string;
@EnhancedField() title!: string;
@EnhancedField() completed!: boolean;
@EnhancedField({ filterOnly: true }) assigneeId!: string;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}Example: searchable
Section intitulée « Example: searchable »Enable full-text search on fields. The title field uses default search options, while description uses custom search options.
import { EnhancedField, IDField } from '@lib/type-graphql-query';import { ObjectType, ID, GraphQLISODateTime, Field } from '@lib/type-graphql';
@ObjectType('TodoItem')export class TodoItemDTO { @IDField(() => ID) id!: string;
@EnhancedField({ searchable: true }) title!: string;
@EnhancedField({ searchable: { unaccent: true, simplified: true, escapeHtml: true, op: 'ILIKE', }, }) description!: string;
@EnhancedField() completed!: boolean;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}Search Options:
| Option | Type | Default | Description |
|---|---|---|---|
unaccent |
boolean |
false |
Remove accents from search text |
simplified |
boolean |
false |
Use simplified text matching |
escapeHtml |
boolean |
false |
Escape HTML characters |
op |
'ILIKE' | 'EQUAL' |
'ILIKE' |
SQL operator to use |
When searchable is enabled, a search parameter will be automatically added to queries:
query { todoItems(search: "important task") { id title description }}Example: localizable
Section intitulée « Example: localizable »Enable multi-language support for fields. The name and description fields support multiple languages.
import { EnhancedField, IDField } from '@lib/type-graphql-query';import { ObjectType, ID, GraphQLISODateTime, Field } from '@lib/type-graphql';
@ObjectType('Product')export class ProductDTO { @IDField(() => ID) id!: string;
@EnhancedField({ localizable: true }) name!: string;
@EnhancedField({ localizable: true }) description!: string;
@EnhancedField() price!: number;
@Field(() => GraphQLISODateTime) created!: Date;
@Field(() => GraphQLISODateTime) updated!: Date;}When localizable is enabled, a locale parameter will be automatically added to queries and mutations:
query { products(locale: "fr") { id name # Returns French translation description # Returns French translation price }}Example: Combining Options
Section intitulée « Example: Combining Options »You can combine multiple options to create powerful field configurations:
import { EnhancedField, IDField } from '@lib/type-graphql-query';import { ObjectType, ID, GraphQLISODateTime, Field } from '@lib/type-graphql';
@ObjectType('Article')export class ArticleDTO { @IDField(() => ID) id!: string;
@EnhancedField({ searchable: { unaccent: true, op: 'ILIKE', }, localizable: true, sortable: true, allowedComparisons: ['eq', 'like', 'notLike'], }) title!: string;
@EnhancedField({ searchable: true, localizable: true, filterOnly: false, }) content!: string;
@Field(() => GraphQLISODateTime) publishedAt!: Date;}@IDField
Section intitulée « @IDField »By default type-graphql-query uses the default GraphQL ID scalar. If you need to use a different GraphQL scalar type, you can use the @IDField decorator.
Recommendation
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.
Basic Example
Section intitulée « Basic Example »import { IDField } from '@lib/type-graphql-query';import { ObjectType, ID } from '@lib/type-graphql';
@ObjectType('TodoItem')export class TodoItemDTO { @IDField(() => ID) id!: string;
@EnhancedField() title!: string;}Example: Disable Filtering
Section intitulée « Example: Disable Filtering »You can disable filtering for the ID field:
import { IDField } from '@lib/type-graphql-query';import { ObjectType, ID } from '@lib/type-graphql';
@ObjectType('TodoItem')export class TodoItemDTO { @IDField(() => ID, { filterable: false }) id!: string;
@EnhancedField() title!: string;}Input DTOs
Section intitulée « Input DTOs »Input DTOs are used for mutations. Always use @Field from type-graphql for input types, not @EnhancedField.
import { InputType, Field } from '@lib/type-graphql';
@InputType()export class CreateProductDTO { @Field() name!: string;
@Field() price!: number;
@Field({ nullable: true }) description?: string;}Important
Never use @EnhancedField in @InputType classes. Always use @Field from type-graphql.
Update DTOs
Section intitulée « Update DTOs »Update DTOs typically make fields optional:
import { InputType, Field } from '@lib/type-graphql';
@InputType()export class UpdateProductDTO { @Field({ nullable: true }) name?: string;
@Field({ nullable: true }) price?: number;}Quick Reference
Section intitulée « Quick Reference »| Decorator | Use Case | Output Type | Input Type |
|---|---|---|---|
@IDField |
ID fields | ✅ Yes | ❌ No |
@EnhancedField |
Fields needing filtering/sorting/search | ✅ Yes | ❌ No |
@Field |
Regular fields | ✅ Yes | ✅ Yes |
Best Practices
Section intitulée « Best Practices »1. Keep DTOs Separate from Domain Models
Section intitulée « 1. Keep DTOs Separate from Domain Models »DTOs should represent the API contract, not your internal domain models.
2. Use Validation Decorators
Section intitulée « 2. Use Validation Decorators »Add validation to ensure data integrity:
import { MinLength, MaxLength } from 'class-validator';
@EnhancedField(() => String)@MinLength(3)@MaxLength(100)name!: string;3. Document Fields
Section intitulée « 3. Document Fields »Add descriptions to help API consumers:
@EnhancedField(() => String, { description: "Product name" })name!: string;4. Choose the Right Decorator
Section intitulée « 4. Choose the Right Decorator »- Output Types: Use
@EnhancedFieldfor fields that need filtering, sorting, or searching - Input Types: Use
@Fieldfromtype-graphql(not@EnhancedField) - ID Fields: Use
@IDFieldfor consistency
5. Use @IDField for ID Fields
Section intitulée « 5. Use @IDField for ID Fields »This ensures consistency across the API and makes future changes easier.
Common Patterns
Section intitulée « Common Patterns »Pattern 1: Simple CRUD DTO
Section intitulée « Pattern 1: Simple CRUD DTO »@ObjectType()export class Product { @IDField(() => ID) id!: string;
@EnhancedField(() => String) name!: string;
@EnhancedField(() => Number) price!: number;}Pattern 2: Searchable and Localizable
Section intitulée « Pattern 2: Searchable and Localizable »@ObjectType()export class Article { @IDField(() => ID) id!: string;
@EnhancedField({ searchable: true, localizable: true }) title!: string;
@EnhancedField({ searchable: true, localizable: true }) content!: string;}Pattern 3: Filter-Only Foreign Key
Section intitulée « Pattern 3: Filter-Only Foreign Key »@ObjectType()export class OrderLine { @IDField(() => ID) id!: string;
@EnhancedField({ filterOnly: true }) orderId!: string;
@Relation('order', () => Order) order!: Order;}Next Steps
Section intitulée « Next Steps »Continue learning:
- 📊 Types - Learn about GraphQL type definitions
- ✏️ Mutations - Understand data modifications
- 🔍 Queries - Query operations with filtering and sorting
- 🔗 Relations - Manage relationships between entities
Related Topics
Section intitulée « Related Topics »- Type GraphQL Query Overview - Complete overview
- Getting Started - Quick start guide
- Code Structure - Understand module organization
- Code Style - Coding conventions