乐闻世界logo
搜索文章和话题

Why do we need dtos and interfaces both in nestjs

3个答案

1
2
3

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:

typescript
import { 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.

typescript
interface 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.

2024年6月29日 12:07 回复

Let me explain the concept of DTO with a simple example. DTO stands for Data Transfer Object. DTOs are used to reduce code duplication by defining a pattern that is passed as an argument to functions, making it easy to retrieve the required data. Here's an example of a DTO:

typescript
export class AuthCredentialsDto { email: string; password: string; }

Now, consider a method to verify password correctness:

typescript
async passwordCheck(userCredentials: AuthCredentialsDto) { // Destructuring in JavaScript const { email } = userCredentials; // Database logic to find the email return user; }

Without DTOs, the code would look like this:

typescript
async passwordCheck(email: string, password: string) { // Database logic to find the email return user; }

Another key point is that functions in a framework often call multiple other functions, requiring parameters to be passed repeatedly. For instance, if a function requires 10 parameters, you must pass them multiple times across calls. Although it is technically possible to work without DTOs, this approach is not developer-friendly. Once you get used to DTOs, you'll appreciate how they save significant time and effort by reducing redundant code.

2024年6月29日 12:07 回复

Generally, in REST APIs, we have two types of operations: input and output. These are requests and responses.

During responses, we don't need to validate the returned data. We simply pass data according to the interface.

However, for requests, we need to validate the request body.

For example, if you want to create a user, the request body might look like this:

shell
const body = { name: "Test Name", email: "test@gmail.com", phone: "0393939", age: 25 }

Therefore, during requests, we need to validate whether email, phone number, or password match regular expressions, etc.

So in DTO, we can perform all the validations.

Here is one example of my DTO.

shell
import { IsEmail, IsNotEmpty, IsString, MinLength } from 'class-validator'; export class RegisterUserRequest { @IsString() @IsNotEmpty() name: string; @IsEmail() @IsNotEmpty() email: string; @IsNotEmpty() @MinLength(6) password: string; } export class LoginUserRequest { @IsEmail() @IsNotEmpty() email: string; @IsNotEmpty() @MinLength(6) password: string; }

Here is an example of the interface.

shell
import { UserRole } from './user.schema'; export interface UserType { _id?: string; email?: string; name?: string; role: UserRole; createdAt?: Date; updatedAt?: Date; }
2024年6月29日 12:07 回复

你的答案