Brendonovich / prisma-client-rust

Type-safe database access for Rust
https://prisma.brendonovich.dev
Apache License 2.0
1.8k stars 104 forks source link

Usage as library generator #223

Closed kaylendog closed 1 year ago

kaylendog commented 1 year ago

There are a few cases in which prisma-client-rust having the ability to be used to generate a library is useful. In my particular case, I have a number of microservices, each of which deals with a separate part of the database, but all use the same schema. Having to include the prisma schema in every microservice repository is grounds for serious schema version mismatch problems.

Using prisma-client-rust to generate a library crate for a given schema is currently pretty difficult. I have been messing about with build scripts to generate the client at compile time, but rust-analyser is very unhappy about this.

It would be helpful to introduce a macro, akin to tonic's include_proto that expands to the prisma client generated by prisma generate, along with a similar prisma_build::generate() etc. that can be placed in build.rs.

oscartbeaumont commented 1 year ago

Can't you just compile the Prisma Client into a dedicated crate which can be reused within your Cargo workspace for other crates? A build script shouldn't be required for that. If you were to use an include_proto in each crate it would probs slow down your builds which wouldn't be optimal and using the macro in a single crate would be similar to the current setup with codegening the client into a single crate.

kaylendog commented 1 year ago

The microservices aren't contained within a workspace, they're all in separate repos. I can't commit the output of the generator since it is relative to the current workspace, and since it's generated I feel I shouldn't really be committing it anyway.

The first line of the generated script is always

pub static DATAMODEL_STR: &'static str =
    include_str!("<absolute path to schema here>");

Which will fail if I try and bundle this up as a crate to another machine. I could edit this to be relative, but

  1. I'd need to edit this every time I regenerate the client
  2. The script itself advises against editing
kaylendog commented 1 year ago

If you were to use an include_proto in each crate it would probs slow down your builds which wouldn't be optimal and using the macro in a single crate would be similar to the current setup with codegening the client into a single crate.

It's more likely I'd stick the output of the macro in its own crate and specify that as a dependency elsewhere. Making another gross comparison to what tonic does, here's an example where I do this with tonic.

Brendonovich commented 1 year ago

was typing out a response and then you said you're not using a workspace... that complicates things.

I don't think generating the client at build time or via a macro will ever be a good idea.

As for solutions to your problem - from what I understand of your situation I'm willing to meet you half way. DATAMODEL_STR and MIGRATIONS_DIR are resolved via absolute paths because MIGRATIONS_DIR cannot be reliably resolved via relative paths (thanks include_dir! and rustc), but I could add a config option to the generator part of the Prisma schema to specify a stable, relative path to the project that DATAMODEL_STR and MIGRATIONS_DIR can be resolved from.

This would allow the client to be put in a library and made portable, but it wouldn't fix your version mismatch problems - that's on your end, since each microservice would need to be updated when a new client is generated. I can't fathom not using a workspace for something like this, but if you really aren't able to then I think this part is out of my hands.

EDIT: I assume your project is Furink - While it's none of my business to dictate how you build your project, use a monorepo. It'll end up being less work for both of us haha.

kaylendog commented 1 year ago

That sounds helpful. Certainly seems better than my current build.rs which replaces the absolute path with a relative one.

The version mismatch issues are much more manageable when it's just a dependency in Cargo.toml I'm upgrading. I'm not using a workspace since the microservices are (slash will be, there's not a lot of them yet) written in various different languages - I could dump ones in the same languages into respective workspaces, but given each microservice might need different workflow pipelines in the future it made more sense to me to keep them separate.

I must ask about the conditional builds you mentionned - in reference to generating the client at build time, could you not just emit cargo:rerun-if-changed=prisma/schema.prisma? This is what my current build script does and it works a charm.

kaylendog commented 1 year ago

EDIT: I assume your project is Furink - While it's none of my business to dictate how you build your project, use a monorepo. It'll end up being less work for both of us haha.

Heh, I don't deny it's a sensible idea. I just have some maintainability concerns (which I do admit may turn out to be of little concern anyway - still v early in development so I can't say for sure).

Brendonovich commented 1 year ago

I could dump ones in the same languages into respective workspaces, but given each microservice might need different workflow pipelines in the future it made more sense to me to keep them separate.

As a maintainer of a large Rust + TS project, I don't think this should be a problem. Maybe Furink is different but how crazy could it be.

in reference to generating the client at build time, could you not just emit cargo:rerun-if-changed=prisma/schema.prisma?

yeah, you could. but i don't like it

Heh, I don't deny it's a sensible idea. I just have some maintainability concerns

IMO I think you should have more maintainability concerns around using microservices and separate repos. A monolith API powered by Prisma and rspc and a React frontend, all in a monorepo, will be way more maintainable. No gRPC, no microservices, just JSON kept in check by Rust and TypeScript.

kaylendog commented 1 year ago

Oh rspc looks quite nice - thank you for the recommendation. I'll have a play and see if my brain accepts the monolith idea!

Brendonovich commented 1 year ago

Going to close this for now since I really don't want to allow and encourage the patterns that I've expressed disdain for in this thread, but if it's really necessary in future then feel free to create another issue outlining why using a workspace doesn't work.