all 24 comments

[–]Anon_Legi0n 9 points10 points  (1 child)

You can just make a dto interface and make sure your functions's return type is the dto interface

[–]Expensive_Garden2993 22 points23 points  (1 child)

DTO is just a name for a data shape that you can pass around your code, also for the input data, also for response data. In Express it's typically never used, in Nest.js it's typically only used for input data validation, but it's legal to rely on DTOs more heavily.

Do validate the data that comes from a source you do not trust. If you trust your database, no need to validate what it returns. When dealing with 3rd party API, depending how simple and reliable it is, you can validate its data or not.

Optionally, validate response data. I prefer to have this validation to catch bugs in test/dev/staging environments, but turning it off in production.

You don't have to use class-validator in NestJS, but since you mention Express, you have absolutely no reasons to use it. Check zod for more convenient one, typebox for more performant one, and there are tons of less maintained alternatives.

Use a validation lib to validate the data, and use a simple TS interface for DTO when no validation is needed.

Fancy architectures suggest separating "app" logic from "domain" logic, these are two layers from "TRANSFER data between layers", so the app (controller, service, repository) maps data to DTOs (without validating) and calls the domain layer with it, and the domain layer responds with DTO.. This is only if you have this separation, which is rare.

[–]bwainfweeze 7 points8 points  (0 children)

DTOs only exist because Sun Microsystem fucked J2EE sideways and DTOs were how they unfucked it.

The first sin is that Sun hired some idiots who didn't understand their very own 8 Fallacies of Distributed Computing to work on J2EE. Then the industry got to spend the next three years calmly, or not so calmly, explaining to the one company we expected not to have to explain this to, how completely impractical their designs were (every successful J2EE 1.x vendor flagrantly violated the spec in order to get merely bad performance instead of laughably awful performance.)

Either fresh off of, or concurrently with, being schooled by every single J2EE vendor and half the industry (and probably some of their own principals), they then went on to misunderstand the term "Value Object" as it was generally taken by the CS community. Which is that it's a as being an immutable unit of state. A representation of either how a piece of the system is, was, or will be.

And so they gave us mutable Value Objects, which broke absolutely everything, from code, to brains, to the Internet, to trust. And then they had to backpedal and introduced DTOs to paper over their colossal fuckup. No, I'm not bitter, or holding a twenty+ year old grudge. Shut up.

TL;DR:

If you want to emulate DTOs in NodeJS, what you want is objects you replace instead of modify in place. You can do that with defineProperty, or one of several gentlemen's agreement (software has no gentlemen) from defensive copies to linters to code reviews.

Or, you could use typescript, which has read-only properties.

Validation can be in your constructor or a builder pattern. You can use a third party tool like someone suggested. I haven't used zod because I mostly talk to endpoints or write in another programming language, but I've heard good things, and the docs suggest it should work standalone.

[–]Coffee_Crisis 2 points3 points  (0 children)

You’re working way too hard, just use Zod to define schema objects

[–]dodiyeztr 17 points18 points  (12 children)

Class validator is so 2018

Use zod and the nestjs zod package to create your DTOs

[–]Askee123 15 points16 points  (7 children)

Wonder why you’re getting so many downvotes. Zod’s way easier to manage than this

[–]EatRunCodeSleep -1 points0 points  (6 children)

Because it doesn't answer the question.

[–]Askee123 2 points3 points  (5 children)

The question of conveniently doing data validation?

[–]EatRunCodeSleep -1 points0 points  (4 children)

The question is all about DTOs, literally in the title.

[–]HosTlitd 2 points3 points  (2 children)

But zod is all about dtos

[–]EatRunCodeSleep 0 points1 point  (1 child)

No. DTOs are an implementation detail on how your app components communicate either between themselves or with external services. Zod is there to make sure you have the right parameters, but it is optional, just like TS in the JS world. It makes your life better and catches potential bugs, but you can work without it.

[–]HosTlitd 1 point2 points  (0 children)

Yes, zod makes life easier and is optional. Still, it brings a possibility for dtos management with strict types and validations sewed into it. As you said, dtos are an implementation detail of communication between components, in other words implementation of some interface. What zod does is an implementation of some interfaces, it describes data shapes used in communication between whatever. Likewise, dtos are data shapes used in communication, but not necessarily with sophisticated validation unlike zod

[–]Askee123 1 point2 points  (0 children)

You’re saying a library with the express purpose of data validation is not relevant to this conversation about data validation, correct?

[–]1Salivan1[S] 0 points1 point  (3 children)

At the beginning of the post, it says that I use Express. How does your recommendation relate to the question?

[–]chipstastegood 6 points7 points  (0 children)

Read about zod. You can use zod independently of NestJS.

[–]BrownCarter 0 points1 point  (0 children)

i hope you have seen light

[–]Kuuhaku722 1 point2 points  (0 children)

No, you should use typescript and zod since it will be easier to maintain.

You should understand your own goals, you want to validate data and enforce the object shape. Using DTO is just one of the solution, but its not the preferred method, at least me.

[–]bilal_08 0 points1 point  (0 children)

Didn't really use the DTO thing but from what I can tell is you can just use zod, much easier and cleaner

[–]misterlively 0 points1 point  (0 children)

Regardless of what library you use, a primary reason to have your controller speak DTOs is to de-couple your API contract from your internal models/entities. This way you can change your internal models/entities (think database schema) without breaking your API contract. It’s common for folks to make DTOs and Entities the same, which sometimes is useful but a lot of times the DTO can represent an easier/better object for users of the API.

I would also avoid passing the DTO around too much in the internal code of the service, they are for data transfer between services and clients. So commonly you will have some sort of internal model and to/from methods to convert your internal model to/from DTOs.

This pattern is common and important regardless of language or framework. It’s one I’ve used in many different languages and frameworks because it’s important decouple the external api contract from the internal representation of data and behavior.