NestJS advocates the use of Data Transfer Objects (DTOs) and interfaces to achieve separation of application logic and type safety.
1. DTOs (Data Transfer Objects)
DTOs in NestJS are typically used to define data transmission formats. They enforce the structure of data sent from the client to the server, ensuring data consistency and validation. DTOs often include decorators that provide validation rules, ensuring only data conforming to these rules is accepted and processed.
Example:
Suppose we have a user creation API; we might define a CreateUserDto class to ensure the received data includes username and password, both of which are strings:
typescriptimport { IsString } from 'class-validator'; export class CreateUserDto { @IsString() readonly username: string; @IsString() readonly password: string; }
In the above example, the class-validator library provides the @IsString() decorator to validate incoming data types.
2. Interfaces
Interfaces in TypeScript and NestJS define object structures for compile-time type checking. They specify which properties an object can have and their types. Since they do not compile to JavaScript, they provide no runtime validation.
Example:
When sharing data structures between services or modules, we can define an interface to specify the data shape.
typescriptinterface User { id: number; username: string; password: string; }
In the above example, the User interface describes required properties and types for a user object. Any object implementing the User interface must include id, username, and password properties.
Why Both Are Needed?
Using DTOs and interfaces together provides the following advantages:
- Layered Data Validation: DTOs enforce strict validation on incoming data at the application layer, while interfaces provide compile-time type checking to ensure code correctness.
- Code Maintainability: Interfaces define a clear contract that services, controllers, and other classes can implement, making the code more modular and maintainable.
- Flexibility and Extensibility: DTOs define data formats for specific operations (e.g., creation or update), while interfaces define the application-level data model. Combining both simplifies extension and refactoring.
- Change Isolation: If external data requirements change, typically only the DTO needs adjustment, without modifying internal interfaces, minimizing system impact.
In summary, DTOs and interfaces together provide NestJS with a flexible, reliable, and maintainable data handling framework. By functioning at compile time and runtime respectively, they ensure type safety and data consistency while improving code readability and maintainability.