Help with types in Repository Pattern
Hello,
I'm trying to set up a crud repository where it can be extended but I'm not sure how to get the typings correct.
Here is what I have and I'm stuck.
The issue that I have is I'm not sure how to actually get the primaryKey in a type safe way.
34 Replies
Right now your Generic S and your Generic ID parameters don't have any relation
The first thing you should do is in the first line:
Do your tables have a common name of the primary key, like
id
?
That would make it a lot easier, with less generic parametersUsually yes
You can check in the file how I’m implementing it
I was thinking of passing the primary key in the generic
If you do:
If your id is a string
I might be wrong, but that would make not necessary to have a generic for the id
Because you know in all your schemas have an
id: string
BTW, What errors are you seeing that made ask for help?I ended up going this route
it is working but I'm on finding a way to add select criteria
or I might just keep it as is and just make per repository queries
@angelelz what do you think? 😄
Interesting, I didn't have time this weekend to test it.
this is how you extend it
not the best looking
but works 😄
Looks great, mr. typescript wizard!
Did you manage to do this in a way that would allow to pass columns as a parameter for select() and the types for the returned rows for find/findAll would be correct?
I got it to a point where all the real db columns were right but it was missing the "virtual" ones.
So
was missing the madeUpFoo from the return type.
Just my humble opinion, my 2 cents so to speak. I don't really like this pattern, I prefer to have my data model behind an interface. The types are really easy to implement, as the interface wouldn't care about implementation details, just return types. Then create classes that implement that interface, and instantiate it with the db object.
Abstract classes are good for library code, where you need several implementation classes to have an underlying shared API and methods.
In application code, when you create a record, you might need to also create related records. When you need to get records, you'll also likely need to join them with related records, etc.
That's where this type of Crud abstract classes fall apart, imo.
Interfaces also make your code easy to test and replacing implementation classes becomes a breeze
Yeah, I do see what you mean. Personally I wasn't even planning to use the repositories for the more difficult queries/stuff for the reasons you just wrote, which in turn would make them kinda unnecessary I guess
It's an interesting exercise, not gonna lie! And I wouldn't like my opinion to discourage anyone from attempting to implement it and probably discover some good benefits!
Well exercise it was indeed. And I lost 😄
But indeed you kinda have to learn quite a lot of a library when you're trying to do generic stuff...
@Angelelz @Ayaz this was my attempt at implementing this pattern... any thoughts/suggestions? @Raphaël M (@rphlmr) ⚡ i also saw you commented on this somewhere else.
edit: my transaction was setup wrong but this seems to work
@Andrew Sherman i saw you also recommended this service-repo pattern somewhere else. any suggestions? I was trying to add joins but was getting errors i think on the return type... not sure how to handle joins. Maybe it's too complicated and i should just handle this in other repos that implement BaseRepo?
Can you share a real-world use case where you need to add joins to a query based on specific external values passed from the client?
Trying to design my app with Airtable like query/filter capabilities across other relational data... so user could filter on a "Contacts" object by applying filters on related data like a "Tags" object, filtering for "Tags that have conditions A, D, F, and E". I could totally be approaching this completely wrong tho
good, this scenario makes sense for me
@alexblokh @bloberenober I guess this is a good example for conditional joins
Yeah, this is the kind of query capabilities I'd love to build on the front end. Love the power& use cases it enables on the user side
Was something changed in the meantime how
sql
behaves since this time? I tried implementing this pattern, but I'm facing a strange problem:
- when I write sql`id`
a result is found without a problem,
- when I try interpolating id string in this manner: sql`${this.primaryKey}`
I get 0 results from the query. (The id string is passed to the class as an argument and is saved to a field.)
I ensured that the primaryKey
has "id"
string and I cannot understand why it I get 0 results when interpolating it
@Andrii Sherman
So in the nutshell, this works:
but this doesn't:
Inspecting this further with a debugger results that these are indeed different. The difference is in the queryChunks
attribute:
This is correct behavior. The SQL operator will take any dynamic parameter you pass, and put it in a parameters array to avoid SQL injection
Exactly. I've read up on this on docs, as I was just curious why it isn't working (I assumed it worked in the code sent by @Ayaz ).
As
sql
operator produces parametrized queries, now I understand that this evaluates to WHERE ('id' = 'some-id')
, so this is always false and returns no results. sql.raw()
is what I needed, as it doesn't escape the string pased to it.
https://orm.drizzle.team/docs/sql#sqlrawDrizzle ORM - next gen TypeScript ORM
Drizzle ORM is a lightweight and performant TypeScript ORM with developer experience in mind.
@Ayaz , that's an awesome example. And really helped me. Do you have github address which I can browse? I really like how you code and wanna inspect your proejcts and learn from them.
Hi @minester16 , sadly, I don't really have open projects to highlight these stuff, most of them are on the job.
If anyone would need to add custom ordering to this pattern, take a look at https://discord.com/channels/1043890932593987624/1217899465508257893
If anyone needs to extend the above with upserts, there you go:
GitHub
drizzle-orm/drizzle-orm/src/pg-core/query-builders/insert.ts at 470...
Headless TypeScript ORM with a head. Runs on Node, Bun and Deno. Lives on the Edge and yes, it's a JavaScript ORM too 😅 - drizzle-team/drizzle-orm