Reputation: 27286
I am implementing a NestJS backend with a PostgreSQL DB. I am no fan of ORMs so I am using Kyseley. Moreover I write my PostgreSQL DDL scripts by hand and then use kysely-codegen to generate the DTO objects once the database schema has been created. So my single source of truth is plain old DDL scripts written for a PostgreSQL database. So far so good.
I am now stuck at the following problem: I have no easy way to use the DTO objects (actually interfaces) automatically generated by kysely-codegen
when doing pipe validation. Firstly, kyseley-codegen
generates interfaces, not classes I can annotate with the annotation supplied in class-validator. Secondly, even if classes were created I wouldn't be able to modify them as this is automatically generated code and my source of truth is the PostgreSQL DDL scripts. I suppose that if kyseley-codegen
generated classes I might have been able to use patch-package to add the class-validator
annotation in a DRYish way but the point is moot since kysely-codegen
generates interfaces.
It seems therefore that the only option available to me would be to bite the bullet and go fully WET and create DTO classes with class-validator
annotations by hand. I would then use the DTO interfaces created by kysely-codegen
in the actual DB queries and the hand crafted DTO classes in the NestJS validation pipelines. Of course being WET this entails maintaining the PostgreSQL DDL scripts and the DTO classes synchronized by hand.
Is there a practicable DRY approach that accomplishes the above or is it hopeless to try and have the PostgreSQL DDL scripts being the single source of truth?
Upvotes: 1
Views: 450
Reputation: 157
I am using the same tooling here, and this is how I'm implementing my DTOs. I implement the interface that kysely-codegen
provides, wrapped with kysely
's Insertable
interface to play nicely with the ColumnType
type that the codegen library applies to more complex columns. Extending this interface ensures that, if anything changes with the source of truth Database interface from kysely-codegen
, an error will occur downstream in my DTOs.
export class CreateExampleTableDto implements Partial<Insertable<ExampleTable>> { /***/ }
With regards to keeping everything DRY, I don't think that's an entirely feasible goal here. Per the Nest docs, DTOs are constructs that intentionally represent an object's structure at the point in time in which it comes over the wire. To me, it makes sense to me that kysely-codegen
library only generates Interfaces and not actual JS Classes, as Classes generally represent real objects with a uniform shape of properties that actually exist within that object, versus an Interface only describing the shape of an object. Classes with class-validator
properties on them that are also used for our Kysely Database
interface would create a lot of false positives, as all of the class-validator
metadata on certain rows would be 'wrong', per how Kysely represents it's own types for certain columns (e.g. ColumnType<T, U, V>
).
In my answer above, when accepting data that intends to create a record within the database, often there will only be partial data from that entire class. Take for example a 'created at' or 'updated at' column, or maybe an internal ID. These are often things a client won't know about, and definitely shouldn't have the responsibility of creating or updating - the source of truth there should be your server. Maintaining Partial<T>
DTOs, I believe, is an acceptable middle ground, and easier to customize or maintain if you have multiple incoming or outgoing parameters that rely on Nest Pipes.
Upvotes: 0