gpt4 book ai didi

Why do we need DTOs and interfaces both in NestJS(为什么我们需要在NestJS中同时使用DTO和接口)

转载 作者:bug小助手 更新时间:2023-10-25 21:57:11 29 4
gpt4 key购买 nike



The NestJS documentation showcases how to add DTOs to use in Controllers to validate request objects by using class-validator package. DTOs described there are TypeScript classes. Now, while controllers deal with DTOs(TS Classes), NestJS providers (or services), on the other hand, makes use of TypeScript interfaces. These DTOs and interfaces are pretty much of the same shape.

NestJS文档展示了如何添加DTO,以便在控制器中使用类验证器包来验证请求对象。描述的DTO有打字脚本类。现在,控制器处理DTO(TS类),而NestJS提供程序(或服务)则使用类型脚本接口。这些DTO和接口的形状基本相同。


Now, I am seeing duplication of shape definition here. And wondering if the interfaces are needed at all?

现在,我在这里看到了形状定义的重复。想知道是否需要这些接口?


Can we not make DTOs source of truth for the shape and validations? One of the approaches we were considering (to make the DTO source of truth) was, to have an openapi generator take the DTOs as input and generate openapi definition and from there another codegen can generate a set of typescript interfaces to be consumed by NestJS itself and which can be shared with another set of consumer applications like Angular too.

我们不能让DTO成为形状和验证的真理来源吗?我们正在考虑的一种方法(使DTO成为真理的来源)是,让OpenAPI生成器将DTO作为输入并生成OpenAPI定义,然后另一个代码生成器可以从那里生成一组打字脚本接口,供NestJS本身使用,并可以与另一组消费者应用程序共享,如ANGLE。


Have any people come across a similar problem? What do you think of the above?

有没有人遇到过类似的问题?你对以上这些有什么看法?


更多回答
优秀答案推荐

According to the Nestjs docs:

根据雀巢的文件:



But first (if you use TypeScript), we need to determine the DTO (Data Transfer
Object) schema. A DTO is an object that defines how the data will be
sent over the network. We could determine the DTO schema by using
TypeScript interfaces, or by simple classes. Interestingly, we
recommend using classes here. Why? Classes are part of the JavaScript
ES6 standard, and therefore they are preserved as real entities in the
compiled JavaScript. On the other hand, since TypeScript interfaces
are removed during the transpilation, Nest can't refer to them at
runtime. This is important because features such as Pipes enable
additional possibilities when they have access to the metatype of the
variable at runtime.




I'm no expert but I'm not using DTO's at all. I really couldn't see a use for them. In each module I have a service, module, entity, and controller.

我不是专家,但我根本不用DTO。我真的看不出它们有什么用处。在每个模块中,我都有一个服务、模块、实体和控制器。



Reason for using DTO and Interface in NestJS


Basically, in rest API, we have two types of operation, One is Input and Another is Output. which is Request and Response

基本上,在rest API中,我们有两种操作,一种是输入,另一种是输出。这是请求和响应


During response, we don't need to validate the return value. We just need to pass data based on the interface

在响应期间,我们不需要验证返回值。我们只需要基于接口传递数据


But in request, we need to validate the body

但在请求中,我们需要验证身体


for example, you want to create a user. Then the request body might be something like this

例如,您希望创建一个用户。那么请求体可能是这样的


const body = {
name: "Test Name",
email: "[email protected]",
phone: "0393939",
age: 25
}

so during request we need to validate email, phone number or password is matched regex etc.

因此,在请求期间,我们需要验证电子邮件、电话号码或密码是否与正则表达式匹配等。


so in DTO we can do all validation

因此,在DTO中,我们可以进行所有验证


Here is one of my DTO example


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;
}

And here is the interface example


import { UserRole } from './user.schema';

export interface UserType {
_id?: string;
email?: string;
name?: string;
role: UserRole;
createdAt?: Date;
updatedAt?: Date;
}


Hope you understand.

希望你能理解。


Thanks

谢谢



I would like to explain the concept of DTO with the simplest example possible for your better understanding.
DTO stands for Data Transfer Object. Now DTO's are used to reduce code duplication. It simply defines a schema which are passed in the parameters of functions to make it easy to get the required data from them. Here is an example of a DTO

我想用最简单的例子来解释DTO的概念,以便您更好地理解。DTO代表数据传输对象。现在,DTO被用来减少代码重复。它只是定义了一个模式,该模式通过函数的参数传递,以便于从函数中获取所需的数据。以下是DTO的一个示例


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

Now if we make a method to check whether the password is correct or not

现在,如果我们创建一个方法来检查密码是否正确


async password_check(usercredentials: AuthCredentialsDTO)
{
//Destructuring in JAVASCRIPT
const {email} = usercredentials;
//Database Logic to find the email
return user;
}

Now if we didn't make use of the DTO, then the code would have looked like

现在,如果我们没有使用DTO,那么代码将如下所示


async password_check(email: string, password: string)
{
//Database Logic to find the email
return user;
}

also the point is that this is just one function, in a framework, Multiple function call multiple other functions which requires passing the parameters again and again. Just consider that a function requires 10 parameters. you would have to pass them multiple times. although it is possible to work without a DTO but it is not a development friendly practice. Once you get used to DTO you would love to use them as they save a lot of extra code and effort.
Regards

还有一点是,这只是一个函数,在一个框架中,多个函数调用多个其他函数,这需要反复传递参数。假设一个函数需要10个参数。你将不得不多次通过它们。虽然可以在没有DTO的情况下工作,但这不是一种对开发友好的做法。一旦您习惯了DTO,您就会喜欢使用它们,因为它们节省了大量额外的代码和工作。问候



TLDR

TLDR


The answer to your question is yes, you could use them for shape if you wanted to, but it might be unnecessary in some situations.

你的问题的答案是肯定的,如果你愿意的话,你可以用它们来塑造形状,但在某些情况下可能没有必要。




DTOs are a great solution for when you need to enforce some shape on your data(specially on the nestjs ecosystem where it interacts a lot with class-validator) or transform it somehow. Examples of that would be when you're recieving data from your client or from another service. In this case the DTOs are the way to go for setting contracts.

DTO是一种很好的解决方案,适用于需要对数据强制某些形状(特别是在与类验证器进行大量交互的nestjs生态系统上)或以某种方式转换数据的情况。例如,当您从您的客户端或其他服务接收数据时。在这种情况下,DTO是制定合同的方式。


However when you're sending data for example, between two layers of the same application -- for instance between your controller and your usecase or between your usecase and your repository -- you might want to use an interface there since you know your data is comming in the correct format in this scenarios.

但是,例如,当您在同一应用程序的两个层之间发送数据时--例如在您的控制器和用例之间,或者在您的用例和存储库之间--您可能希望在那里使用一个接口,因为您知道在这种情况下,您的数据以正确的格式出现。


One key difference to understand is that the interface serves as a development tool, it keeps you for making mistakes like passing an object lacking a certain required property between two classes, while the DTO affects the application itself, it's an object that's going to be instantiated at runtime and might be used for validation and data transformation purposes, that`s the idea, but of course it also has the capacities of an interface.

要理解的一个关键区别是,接口充当开发工具,它防止您在两个类之间传递缺少特定必需属性的对象等错误,而DTO影响应用程序本身,它是一个将在运行时实例化的对象,可能用于验证和数据转换目的,这是我们的想法,但当然它也具有接口的功能。


There might be exceptions to this rule of thumb depending on the architecture you're going for. For example, on a project I'm working on it's very common to have the contract between domain and presentation layers equal to the contract between frontend and the API. On this project, to avoid having to create two similar contracts, I`ve chosen to use a DTO to set the contract between the presentation and domain layers. Now in most cases I just extend the DTO when setting the contracts between API and clients.

根据您要采用的体系结构,这条经验法则可能会有例外。例如,在我正在处理的一个项目中,域和表示层之间的契约与前端和API之间的契约是非常常见的。在这个项目中,为了避免创建两个类似的约定,我选择使用DTO来设置表示层和域层之间的约定。现在,在大多数情况下,我只是在设置API和客户端之间的合同时扩展DTO。



To extend @Victor's answer regarding the DTO concept and its role, I'd like to point that interfaces allow us to set a contract which represents something meaningful in our app. We can then implement and/or extend this contract in other places where needed as well e.g. entity definition for database objects - DAOs, data transfer objects - DTOs, and business models definitions notably.

为了扩展@Victor关于DTO概念及其角色的回答,我想指出,接口允许我们设置一个契约,在我们的应用程序中代表一些有意义的东西。然后,我们可以在其他需要的地方实现和/或扩展该契约,例如数据库对象的实体定义-DAO、数据传输对象-DTO,特别是业务模型定义。



Also interfaces for DTOs can be shared across a backend and a front-end so that both projects can avoid code duplicate and differences between objects exchanged for ease of development and maintainability.

此外,DTO的接口可以跨后端和前端共享,这样两个项目都可以避免代码重复和交换对象之间的差异,从而简化开发和维护。



one thing that dto provides more than interface is. with dto and class validator you can make the validations quickly at request level. But when it comes to interface you cannot add class validator to it. dtos are class in general. that means you have more to do with that than a interface.

DTO提供的不仅仅是接口的一件事是。使用dto和类验证器,您可以在请求级快速进行验证。但当涉及到接口时,您不能向其添加类验证器。DTO一般都是类的。这意味着你与此有更多的关系而不是界面。



DTO has a little bit different mission. This is an additional abstraction for data transfer connection by the network between FE and BE layers of your application and at the same time, DTO gives a description of your data like it doing Interface. But the main mission is a distinct place for data connection and due to this you could implement many helpful things in your DTO layer it could be some transformation for DTO fields values, validation for them, checking with regular expression, etc. So you have a convenient place for attendant changes for data just on early receiving or sending to FE side

DTO的使命有点不同。这是通过网络在应用程序的FE层和BE层之间进行数据传输连接的额外抽象,同时,DTO给出了数据的描述,就像它正在做的接口一样。但主要任务是数据连接的独特位置,由于这一点,您可以在DTO层中实现许多有用的事情。它可以是对DTO字段值的一些转换、对它们的验证、使用正则表达式进行检查等。因此,在早期接收或发送到FE端时,您就有了一个方便的位置来随之而来的更改数据



In the nestjs-query packages there are two types of DTOs referenced. Read DTO - The DTO returned from queries and certain mutations, the read DTO does not typically define validation and is used as the basis for querying and filtering. Input DTOs - The DTO used when creating or updating records.

在nestjs-query包中,引用了两种类型的DTO。读取DTO -从查询和某些变化返回的DTO,读取DTO通常不定义验证,并用作查询和过滤的基础。输入DTO-创建或更新记录时使用的DTO。



I read through all of the answers, and none really seem to answer your question. I believe that yes, although DTOs and interfaces serve different purposes, I don't see a reason why you need to create an additional interface for typing purposes.

我通读了所有的答案,似乎没有一个真正回答你的问题。我相信是的,尽管DTO和接口用于不同的目的,但我看不出有什么理由需要为键入目的创建额外的接口。


Happy to be proven wrong here, but none of the answers really address the OP's point, which is that the DTO serves a validation purpose, but you also get the typing for free.

很高兴在这里被证明是错误的,但没有一个答案真正解决了OP的观点,即DTO用于验证目的,但您也可以免费获得输入。



Basically you can validate request input without Dto. But imagination, you have to work with body payload, route params, query params or even header values. Without Dto you have to put your validation code inside each controller's methods to handle the request.
With Class Validation and Class Transformer, you can use decorator to do that. Your mission is defining your Dto Classes and add the validation annotations for each property.
You can find out the details here How to validate request input in nestjs and How to use pipe in nestjs

基本上,您可以在没有DTO的情况下验证请求输入。但想象一下,您必须处理正文有效载荷、路径参数、查询参数甚至标头值。如果没有DTO,您必须将验证代码放入每个控制器的方法中以处理请求。使用类验证和类转换器,您可以使用装饰器来实现这一点。您的任务是定义DTO类并为每个属性添加验证注释。您可以在这里找到有关如何在nestjs中验证请求输入以及如何在nestjs中使用管道的详细信息



For example, if the Dto you created for the incoming request needs to be checked for the incoming data, the best way to do this is to create the Dto as a class. Because after typescript is compiled, they continue to exist in your javascript code. In this way, you can add various validations. For example "IsNotEmpy", "IsString" etc. If the data doesn't need to be validated, you can create Dto using interface. So here, rather than a single correct or correct method, it's about what you need.

例如,如果需要检查您为传入请求创建的DTO是否有传入数据,最好的方法是将DTO创建为一个类。因为在编译类型脚本之后,它们继续存在于您的Java代码中。通过这种方式,您可以添加各种验证。如IsNotEmpy、IsString等,如果数据不需要验证,可以使用接口创建DTO。因此,这里不是单一的正确或正确的方法,而是关于您需要的。



I think the NestJs documentation answered this precisely:

我认为NestJs的文档正好回答了这一点:



A DTO is an object that defines how the data will be sent over the network. We could determine the DTO schema by using TypeScript interfaces, or by simple classes. Interestingly, we recommend using classes here. Why? Classes are part of the JavaScript ES6 standard, and therefore they are preserved as real entities in the compiled JavaScript. On the other hand, since TypeScript interfaces are removed during the transpilation, Nest can't refer to them at runtime. This is important because features such as Pipes enable additional possibilities when they have access to the metatype of the variable at runtime.



Link to this pragraph: https://docs.nestjs.com/controllers#request-payloads

链接到此规划图:https://docs.nestjs.com/controllers#request-payloads



IMO, this has nothing to do with NestJS or any other framework. This is clean architecture proposed and nicely described in his books by Robert Martin.

这与NestJS或任何其他框架无关。这是罗伯特·马丁在他的书中提出并很好地描述的干净的架构。


Clean Architecture


DTOs sits in WEB/UI (outer most) layer, while entities (or like you are referring to them as Interfaces) are in the core layer of the clean architecture.

DTO位于Web/UI(最外层)层,而实体(或类似您将其称为接口)位于干净体系结构的核心层。


It is a coincidence that you have them in pretty similar shape, most likely you have a small application and at this stage you will hardly see the value from this separation. But the more the app grows the more difference you will see and more important this separation will become.

巧合的是,你有非常相似的形状,很可能你有一个小的应用程序,在这个阶段,你很难看到这种分离的价值。但应用程序增长得越多,你就会看到越多不同之处,这种分离就会变得更加重要。


Imagine you have a form on the UI that creates something, let's say blog post. In this form you will have Author, Tags, Header image, and of course blog post content. All this can be in a single DTO object, you can validate this object as a whole (all required and valid things provided). But at the controllers level, you will construct multiple Tag entities, Author Entity, ImageHeaderFile entity, BlogPost entity and work with them. You don't want you lower level components to depend on higher level components.

假设您在UI上有一个表单,它创建了一些东西,比如博客文章。在这个表单中,您将拥有作者、标签、标题图像,当然还有博客文章内容。所有这些都可以放在单个DTO对象中,您可以将该对象作为一个整体进行验证(提供了所有必需和有效的内容)。但在控制器级别,您将构造多个标记实体、作者实体、ImageHeaderFile实体、博客发布实体并使用它们。您不希望较低级别的组件依赖于较高级别的组件。


Here are a couple links to start with:

以下是开始时的几个链接:




BTW, even despite on DTO is a Java convention it can't solve the problem of Generic fields, e.g.:

顺便说一句,尽管On DTO是一种Java约定,但它不能解决通用字段的问题,例如:


@Get(url/${variable})
@Reponse({
[$variable: string]: $value
})

TS Interfaces can solve this issue only, but you cant describe it in DTO
And to show it you will pass some hardcoded example

TS接口只能解决这个问题,但你不能用DTO来描述它,为了展示它,你需要传递一些硬编码的例子


class ResponseDto {
@ApiProperty({
...
example: [...]
})
[$variable]: SomeTSInterface[]
}

@Reponse({ status: 200, type: ResponseDto })


DTOs represent the structure of data transferred over the network it is meant to be for a specific use case whereas interfaces are more generalized specificity helps with better readability and optimizations. Interfaces don't exist after transpiling but nest accommodates dtos to be useful even after the transpilation face.

DTO表示通过网络传输的数据的结构,它是针对特定用例的,而接口更通用,专用性有助于更好的可读性和优化。接口在转换后不存在,但Nest适应DTO即使在转换之后也是有用的。



In my opinion,
DTO = Data Transfer Object. Dtos are like interfaces but their whole goal is to transfer data and validate it. They are mostly used in routers/controllers.

在我看来,DTO=数据传输对象。DTO类似于接口,但它们的全部目标是传输数据并对其进行验证。它们主要用于路由器/控制器。


You can simplify your API body and query validation logic by using them. For instance, you have a AuthDto which automatically maps the user email and password to an object dto to enforce validations.

您可以通过使用它们来简化您的API主体和查询验证逻辑。例如,您有一个AuthDto,它自动将用户电子邮件和密码映射到一个对象dto,以强制执行验证。


Where as the interface is just to declare how your response or a particular data model will be.

其中,AS接口只是声明您的响应或特定数据模型将如何。



One of the reasons for using dto is that it checks the input so that valid data is entered
One of the reasons for using the interface is that it helps us to comply with the Liskov substitution principle, which is related to solid.

使用dto的原因之一是它检查输入,以便输入有效数据。使用接口的原因之一是它帮助我们遵守与Solid相关的利斯科夫替代原理。



DTOs are used in the edge of APIs, so they can have some information about the validation (message, ...) and are used in documentation (OpenAPI, ...) but interfaces' main usage is Typing and they are used for communication between domains in for example backend APP

DTO在API的边缘使用,因此它们可以获得一些关于验证的信息(消息,...)并在文档中使用(OpenAPI,...)但接口的主要用途是打字,例如在后端应用程序中,它们用于域之间的通信



I'm not an expert but I do not understand why we use Dto
When we can use the schema model - what is the need for Dto and additional objects

我不是专家,但我不明白,既然我们可以使用模式模型,为什么还要使用DTO--DTO和其他对象有什么必要


更多回答

This was copied and pasted from the docs. Please use the quotation markdown feature or say that you copied and pasted it.

这是从文档中复制和粘贴的。请使用报价降价功能或说您复制并粘贴了它。

Also please provide link to original.

另外,请提供原始链接。

Why the downvotes here? This post adds some balance to the discussion by pointing out that the additional complexity might not make sense for smaller projects.

为什么这里会出现反对票?这篇文章为讨论增加了一些平衡,指出额外的复杂性对于较小的项目可能没有意义。

I realise this is > 2 years old, but a DTO can be useful to avoid exposing fields for an entity/model that clients shouldn't receive, but are still useful/needed at the service level. I could definitely envision situations where this isn't needed though, but for writing an enterprise API, it can be a useful pattern.

我意识到这已经有2年以上的历史了,但是DTO对于避免公开客户端不应该接收但在服务级别上仍然有用/需要的实体/模型的字段可能很有用。我当然可以设想不需要这样做的情况,但对于编写企业API来说,这可能是一个有用的模式。

Apology if I've misunderstood. But consider an enterprise API that serves up raw data and doesn't need its own front end. We don't want to expose certain values to consumers, and the only visibility the client would have is the API contract of inputs and outputs. That is the use case I'm talking about. In that instance the entity can contain our private fields, and the dto omits them, so clients will never see these fields.

如果我误解了请道歉。但是考虑一个提供原始数据的企业API,它不需要自己的前端。我们不想向消费者公开某些值,客户端唯一的可见性就是输入和输出的API契约。这就是我所说的用例。在这种情况下,实体可以包含我们的私有字段,而dto忽略它们,因此客户端永远不会看到这些字段。

Thanks. I haven't thought of that and it probably will never be relevant to my work. Good to make that point in this post!

谢谢。我还没有想过这一点,而且可能永远不会与我的工作相关。很高兴在这篇文章中表达了这一点!

Use DTO's so that it is very easy to change method parameters later on.

使用DTO,这样以后更改方法参数就很容易了。

OP also said "codegen can generate a set of typescript interfaces to be consumed by NestJS itself and which can be shared with another set of consumer applications like Angular too." so not just for type safety, OP intends to share the shape of the data with consumer applications too, so i would think having both DTO and interfaces would be necessary, i wouldn't mess with openApi code generation

OP还说:“codegen可以生成一组打印脚本接口,供NestJS自己使用,并可以与另外一组消费者应用程序共享,比如ANGLE。”因此,不仅为了类型安全,OP还打算与消费者应用程序共享数据的形状,所以我认为同时拥有DTO和接口是必要的,我不会扰乱OpenApi代码生成

So what stops use using the DTO instead of creating a new Interface? I can imagine that perhaps its for the sake of flexibility.

那么,什么停止使用DTO而不是创建新的接口呢?我可以想象,这可能是为了灵活性。

because you will use DTO in validation, which will be in runtime, and in runtime the interfaces don't exist, so you can't validate the user inputs.

因为您将在验证中使用DTO,而验证将在运行时进行,而在运行时接口不存在,因此您不能验证用户输入。

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

正如它目前所写的,你的答案并不清楚。请编辑以添加更多详细信息,以帮助其他人了解这是如何解决提出的问题的。你可以在帮助中心找到更多关于如何写出好答案的信息。

This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From Review

这并不能提供问题的答案。一旦你有了足够的声誉,你就可以对任何帖子发表评论;相反,提供不需要发问者澄清的答案。-摘自评论

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com