DTO(Data Transfer Object) is used for parsing and validating data from request. DTOs always used with Controllers.
It's a common scenario when web server should validate data before processing. DTO can optimize and automate this process.
Let's imagine that we need to implement this API.
Accepts application/json
in following format:
{title: string,description: string}
With following limitations:
title
- required, max length - 80 chars.
description
- optional, max length - 255 chars.
Return id in response
10125
Let's start from DTO class
import { Data, IsOptional, MaxLength } from "odi/dto";​@Data()export class TodoDTO {​@MaxLength(80)title: string;​@IsOptional()@MaxLength(255)desctiption: string;}
And controller with some predefined service
import { Controller, IController, Post, Autowired } from "odi";import TodoService from "./todo.service";​@Controller('todo')export class TodoController extends IController {@Autowired()todoService: TodoService;@Post async index(payload: TodoDTO) {const { id } = await this.todoService.create(payload);return id;}​}
Data from request body will be automatically injected and validated. If validation failed - errors will be automatically sent to client.
Currently, there are two types of DTO, for querystring
and body
validation.
@Data()
- set DTO for handling body
@Query()
- set DTO for handling querystring
There are no additional actions required for including DTO in the handler (method of the controller). Just add DTO (or multiple) as an argument.
@Post async index(body: BodyDTO, query: QueryDTO) {//..code}
There are set of decorators for convenient data validation. If you have special needs on validation, you can always implement your own decorator (read more in advanced). But custom decorators can be implemented. Read in Advanced about it.
Decorators can be grouped by data type
This decorators can be applied to any type of data.
@IsOptional()
- set field optional (by default, all fields are required)
@Const(value: any)
- field is valid if it is deeply equal to the passed value
@Deafault(value: any)
- default value for field, if it not exists
@Enum(enumrable: any[])
- data is valid if it is deeply equal to one of items in the array
@Nested()
- validate nested DTO class, otherwise field will be ignored
Decorator @Nested
should be applied on field, which type is DTO class.
@MaxLength(maxLength: number)
- data to be valid should have length satisfying this rule
@MinLength(minLength: number)
- data to be valid should have length satisfying this rule
@Pattern(pattern: string | RegExp)
- data to be valid should match the regular expression
@Format(format: ValidatorFormat)
- data to be valid should match the format
@IsEmail()
- data to be valid should be email (Format
alias)
@IsUrl()
- data to be valid should be url (Format
alias)
Following formats for @Format
decorator available:
"date""time""date-time""uri""uri-reference""uri-template""url""email""hostname""ipv4""ipv6""regex""uuid""json-pointer""relative-json-pointer"
@Maximum(maximum: number)
- data to be valid should be <=
maximum
@Minimum(minimum: number)
- data to be valid should be >=
minumum
@ExclusiveMaximum(exclusiveMaximum: number)
- data to be valid should be <
maximum
​
@ExclusiveMinimum(exclusiveMinimum: number)
- data to be valid should be >
minimum
@MultipleOf(multipleOf: number)
- data to be valid should be a multiple of the multipleOf
@ArrayOf(targetClass: any)
- sets type of array items
@UniqueItems()
- array to be valid should have unique items
@MaxItems(maxItems: number)
- array to be valid should not have less items than the maxItems
​
@MinItems(minItems: number)
- array to be valid should not have more items than the minItems
@ArraoyOf
can be used only if array item type is another DTO class. As typescript does not provide reflection for complex types, type should be setted manually.
@CustomValidation(validate: ValidateFunction, params: any = true)
- define custom validation function for field. Validate function can be async.
Data transformation from plain object to DTO instance applied automatically. But don't forget about @ArraoyOf
decorator for DTOs arrays.
You can easily use inheritance (extends)
for DTOs.
import { Data, Format, MaxLength, IsOptional } from "odi/dto";​@Data()class BaseDTO {@Format('uuid')id: string;}​@Data()class TodoDTO extends BaseDTO {​@MaxLength(80)title: string;​@IsOptional()desctiption: string;}
TodoDTO
will inherit all properties from base class for both transformation and validation purposes.
Every controller method that contains DTO class as argument will be automatically provided with validated and mapped data. Validation will be performed before controller method call.
Only after transformation, DTO will be injected into method call. Under the hood ajv is used. Decorators is used to build schema.
​