Prisma – ORM for Node.js and TypeScript
prisma.io
prisma.io
Prisma is different. It’s absolutely got rough edges, but the extremely strong type safety makes Sequelize look like a joke. The query engine itself, written in rust, combines and optimizes queries inside every tick of the event loop so GraphQL N+1 issues are a thing of the past.
Also, the team and community behind it are amazing! I never thought having an active dev community behind an ORM would be important, but as the author of Sequelize-Typescript was forced to abandon it late last year and the author of TypeORM was also pretty much absent, Prisma was a breath of fresh air. I REALLY hope they can find a way to build a sustainable business out of it. Support packages, feature development contracts, something to keep them financially incentivized to keep making it better.
Happy to answer any questions about my experience using it if anyone has any.
But I used it for years and it was always buggy. Sometimes options wouldn't work correctly because the authors liked to do that clever JavaScript thing where they write `foo = optional_thing || default_value` even for BOOLEAN options. I always had issues with it not being able to truncate/drop tables correctly that had foreign keys (it didn't attempt to order them intelligently), some of the options weren't compatible with each other even though they were orthogonal (I'm thinking about the snake_casing options and the created_at/updated_at column features), etc, etc.
It just... didn't actually work. Over many versions. I think I used it from late 3.x somewhere through 5.x.
Maybe it's awesome now, but I doubt it.
I don't think I've ever called out on a library, or not felt truly thankful that it was there to help me. But Sequelize... man...
When I first got into Node, and got Sequelize, after working for years with .NET, Entity Framework, NHibernate and the likes, it just felt like horrible-everything. I've forced myself to use it in some projects, because node is cheap, and I kept thinking that I'm missing something. Some brilliance behind the questionable... everything. No. I can't even bring myself to think about that design mess. Sorry for the rant, Sequelize makes me feel insecure, and little, in the chaos of the Universe.
But I was like you- I picked up Node because we had an existing project in it. It only used libraries that had the most "stars" or whatever on NPM and they were all pretty deficient (and some were shockingly slow). But none were so frustrating as Sequelize.
The NPM ecosystem can be frustrating and annoying sometimes but today nodejs is a solid server runtime.
But the API, the client language, the ecosystem, and even the idea are bad, IMO.
Bad API example: Tell me how to deal with time zones in Node without pulling in a big third party library. Answer: You can't. So, a "scalable" backend platform can't handle datetimes from different time zones? Neat!
"Well, that's JavaScript's fault" you say. Node certainly has (a few) APIs that don't apply to the frontend, so why not add more of the important ones for backend work?
Or, maybe JavaScript really just doesn't belong on the backend at all...
Not to mention that it's single-threaded, so the answer to scaling up better is to just run more instances. -_-
See? I should've stayed quiet. ;)
Yeah, this sucks. It's impossible to do any actual processing in javascript since functions that don't return immediately will clog the event loop. Why can't we have javascript code that runs asynchronously and reports completion and results as events? Even browsers seem to have this now. I tracked issue #2133 on GitHub for a long time and nothing materialized.
Not sure if Node.js still lacks this feature. If so then it means Node.js is nothing but an I/O scripting platform. You get events and you make the system copy data from some source to some destination in response. Any sort of actual processing means latency becomes unacceptably high.
[1] https://nodejs.org/api/worker_threads.html#worker_threads_po...
Huh, care to explain this one? I’m using Prisma with Apollo Server without doing anything fancy in my resolvers. I just assumed I’m getting N+1 issues but didn’t bother to optimize yet.
There's an issue open for findMany as well, but it hasn't moved in over a year: https://github.com/prisma/prisma/issues/1477
Turns out Prisma wasn’t optimizing my queries, I think it has to do with me not using the sub query API and instead querying tables seperately (e.g posts.findMany({ where: { owner: user } }))
Then Nikolas Burk reached out to me and did his absolute best to convert me, but I was stuck in my ways, it HAD to behave like Eloquent, or it was not good enough and that DSL layer? No thanks, I don't like it.
Started writing code for my own ORM when I was like "What the heck am I doing? this gets me nowhere" And messaged Nikolas back saying I was dropping everything and planned to give Prisma a real solid try.
I'm so glad I did. I LOVE it now. I consider it a part of my GOAT stack.
This one I will believe when I see it. It would be a general solution to cache invalidation.
They have a whole blog article about why monitoring is important but don't seem come with it: https://www.prisma.io/blog/monitoring-best-practices-monitor...
This is a common misconception that stems from our history as a company and being early contributors to the GraphQL ecosystem. With the move to Prisma 2 however, there is no native GraphQL layer in Prisma any more. I've talked about this extensively in a recent livestream on Youtube [1] if you want to learn more :) There's also this article "How Prisma and GraphQL fit together" [2] that explains the historic dimensions of this if you're interested.
> It's unclear what's the pros are of Prisma compared to TypeORM.
I guess you could argue that one benefit is the superior type-safety Prisma provides [3]. Other folks have also called out that they prefer way how data is modeled with Prisma (via the Prisma schema), the migration system as well as the active maintenance, regular releases, the active community, the support and thorough documentation of Prisma. Ultimately it'll come down to your personal preference though which one is the more appropriate for your project :)
[1] https://www.youtube.com/watch?v=hMWMPpy4ta4&list=PLz8Iz-Fnk_...
[2] https://www.prisma.io/blog/prisma-and-graphql-mfl5y2r7t49c
[3] https://www.prisma.io/docs/concepts/more/comparisons/prisma-...
To use an ORM effectively you must learn both the API of the ORM and SQL. It does not absolve you from learning SQL, which I think was one of the unstated attractions to junior developers. Then you have to map between the API of the ORM and how it translates that to SQL under the hood. Then you have to understand how it handles caching and sessions and how that all works under the hood. This has been the source of so much complexity and so many bugs over the years that I no longer think the benefits are worth the cost. As always, there are exceptions and caveats, but I now believe it's better to use SQL directly than to use an ORM. In general there is too much complexity in all the layers of abstraction in software development these days, and I think the industry is strangely blind to the consequences of this. Complexity is death to software, and it should not be taken on lightly. The entire craft of a software engineer boils down to eliminating complexity and simplifying problems, the better you can do that, the more productive an engineer you will be.[1]
[1] https://github.com/sqljoy/sqljoy/blob/master/docs/pages/orm....
Well, I think if you're not blind to those things to some degree, you're stuck forever in a best-practices search, which I feel like more often than not ends up implying you have to rewrite your stack.
For example, I do most of my development thru Hasura now. It writes insanely performant SQL queries translated directly from GraphQL. As a data consumer, you only have to worry about the shape of the data you want, and none of the implementation details. There is one endpoint, and you pass it queries against the schema it exposes. Implementation details are abstracted heavily here, but in a "good" separation of concerns way.
I will never hand-write SQL again. Why would I? My front-ends can load everything in my data model expressively and with type safety thanks to graphql-codegen.
Now imagine you read this and realize you work in a completely different method and this answers some of your problems. You may start parallel development, but you certainly can't stop forward progress just to evaluate your stack. So I think the blinders are key to progressing in the short term despite the productivity losses you're taking in the long term, and then when you phrase it like this it is surely no surprise companies behave like that.
Can you expand on that? The chain of reasoning is probably clear to you, but I can't follow it.
> For example, I do most of my development thru Hasura now. It writes insanely performant SQL queries translated directly from GraphQL. As a data consumer, you only have to worry about the shape of the data you want, and none of the implementation details. There is one endpoint, and you pass it queries against the schema it exposes. Implementation details are abstracted heavily here, but in a "good" separation of concerns way.
I like Hasura a lot, I've advocated it at a previous company I worked for, and after many months of meetings I got it approved to add to our stack - which was then never prioritized, but that's another story.
> I will never hand-write SQL again. Why would I? My front-ends can load everything in my data model expressively and with type safety thanks to graphql-codegen.
I don't think writing GraphQL is superior to writing SQL, and again you have to think not just in terms of GraphQL but also in terms of how Hasura translates that to SQL under the hood. I am slightly biased though, because I'm creating on a framework to query the db using SQL from the frontend, I'm curious how you think that would compare with Hasura: https://github.com/sqljoy/sqljoy
> Now imagine you read this and realize you work in a completely different method and this answers some of your problems. You may start parallel development, but you certainly can't stop forward progress just to evaluate your stack.
I'm not really sure how this connects to my argument about complexity, although I think the link is clear to you, can you expand on that?
I reached the same conclusion. Everything is so much simpler that way. I ask the database for the data, the database gives it to me. That's the end of it.
I used to like highly abstract libraries but the truth is they provide the lowest common denominator in features at a huge cost in complexity. Now I'd rather work as closely to the implementation as possible. I feel like I actually understand how stuff works now.
It would contrast against traditional ("stateful") ORMs which use query builders (rather than raw SQL) to return database-aware (rather than pure) objects.
The name pureORM reflects both that it is pure "ORM" (there is no query builder dimension) as well as the purity of the mapped Objects.
I find ORMs very convenient and useful for simple queries, and especially for inserts/updates with many-to-many relations. And that is a very large part of a typical application. But you can very quickly meet the limits of an ORM with more complex queries, and I find it easier to just step down to SQL in those cases before learning the more complex parts of the ORM.
That said, for microservice extraction specifically, a bit of creative metaprogramming/monkey patching of AR to identify & categorize your callers is probably easier.
the problem with activerecord is most people usually don't put this in place to begin with because the active record pattern makes it easy not to, which creates a massive pile of spaghetti code long term.
- The generated client is top-tier. Fully specified in TypeScript with intelligent types that can be extended by the user.
- The schema language is great. It provides a cohesive experience that can fully express your database structure, and it also provides a migration framework to manage that structure's evolution.
- There's no hook for implementing access control at the model level, so you end up needing to create a higher-level API around the base queries to implement these yourself, which is a fairly large investment. I'd love to see a pair of functions that can modify the query before it gets sent to the Prisma engine to add extra query values, and a second function that can filter models before they are returned to the caller.
Where Prisma falls short is in testing. The testing story in Prisma is that you can either use a live database, or you can completely stub out all calls to Prisma in your application. The latter means you end up writing a bunch of tests for implementation rather than for behavior, or you end up manually writing an in-memory database that fits the Prisma API. The former means that you need to completely recreate your database after every test case.
The discussions on the Github seem to indicate that the old "wrap the test in a transaction" trick is forbidden, and Prisma seems architecturally set up to ensure that you can't even hack this behavior in.
All in all, we probably won't switch away from it, but I will continue to look for a better way to test my endpoints.
On testing, we're working on some documentation right now on how to mock the Prisma Client. I could see us generating a mockable client to help with this.
Bit of setup effort but well worth it IME even in situations where your tooling isn't strongly pushing you towards the approach.
2) It is at least an order of magnitude slower than not using a database at all.
Combined this makes for very slow tests. Certainly for larger applications with thousands of tests.
Having said that, I don't think there is a single right answer to the problem of testing applications that use a database and your suggestion still can be a valid solution.
- parallel sessions on one database just like multiple clients accessing your web app at the same time
- create multiple copies of a schema and run different independent testusite legs on each (you just have to pass X-Test-Schema in the HTTP header to select on which schema should the web app operate)
- I have an even better trick up my sleeve for testing an app with a real DB backend, and that's passing X-Test-Force-Time header that allows me to override time under which app and DB operates per-request, so I can test even time dependent behavior, like timeouts, timed queues and events, time dependent DB queries, etc. And in general just have a stable predicatble timestamps in the database for each test run and be able to compare database states at the end of the test run and check the diff for irregularities against the previous test runs, or known good state.
I have to patch postgresql to allow for output of now() current_timestamp, etc. to be overridable per session, but it's absolutely worth it to be able to simulate/control the flow of time across the whole stack.
You can actually do a lot of useful stuff with schemas and search_paths, without disrupting connected clients, and without having to re-create databases, all in a transactional/atomic manner, that is very useful for testing.
Parallelizes quite well when we test the engines at Prisma...
What do you think of using something like msw.io for mocking your endpoints as needed?
The website is https://mswjs.io/
Anyone who has been bitten by an ORM generating inefficient SQL (and subsequent having to learn and care about SQL) knows the above not to be true.
Which, yes, is a common state for many applications to be in. Wresting with SQL while using it very poorly in an effort to act like they’re not.
[edit] disagree with the quotes statement, that is, agree with the post. I worded that poorly.
[edit edit] I've been thinking about what you get from using an RDBMS while avoiding writing or knowing much about SQL or using any of the probably-extremely-nice features of your particular SQL DB (why is everyone always so eager to be DB-agnostic? If you do it right your DB will survive, and greatly ease, several rewrites of your application!) and I'm coming up with:
1) basic locking (I assume you don't want to know how to actually use transactions, beyond what your ORM does automatically, so you're just getting the basics), and
2) some probably-badly-insufficient indices, and
3) an ORM should at least get you a little normalization if you just follow patterns from its docs & examples, I suppose, though you're gonna need to understand some SQL to get much benefit out of it in your DB design and in your use of the DB, so...
An entire RDBMS seems like serious overkill if that's all you're really using.
SQL is an impressive technology and has stood the test of time! Yet, we claim that it's not the best tool for application developers who are paid to implement value-adding features for their organizations.
SQL is complex, it's easy to shoot yourself in the foot with and its data model (relational data / tables) is far away from the one application developers have (nested data / objects) when working in JS/TS. Mapping relational data to objects incurs a mental as well as a practical cost!
This is why we believe that in the majority of cases (which for most apps are fairly straightforward CRUD operations) developers shouldn't pay that cost. They should have an API that feels natural and makes them productive. That being said, for the 5% of queries that need certain optimizations, Prisma allows you to drop down to raw SQL and make sure your desired SQL statements are sent to the DB.
I see Prisma somewhat analogous to GraphQL on the frontend, where a similar claim could be: "Frontend developers should care about data, not REST endpoints". GraphQL liberates frontend developers from thinking about where to get their data from and how to assemble it into the structures they need. Prisma does the same by giving application developers a familiar and intuitive API.
I'm not sure about that. We've recently switched from JavaScript based querying code to mostly raw SQL, and we've reduced our code to about 25% of what it was, and it's much simpler to understand than it was before.
> I see Prisma somewhat analogous to GraphQL on the frontend, where a similar claim could be: "Frontend developers should care about data, not REST endpoints".
I'm not sure about Prisma, but IMO that GraphQL model isn't great. Realistically (for performance, etc) it will make a difference where that data came from. Not for super simple queries, but super-simple queries are super simple to do with REST/SQL anyway. I also feel like this distinction between front-end and back-end developers isn't great.
The GraphQL approach also leads to deployment issue with non-web clients. e.g. it can take a day to get an iOS build released, and there is no way to force users of older clients to update, so this can take months. So fixing a bad query is difficult. With a proper backend you can just swap out the query.
Things like performance, ease of use, reusability, schema generation etc are minor points and not the big issue you need to think about.
Personally I feel that an ORM will lead your project in the wrong direction and your database schema will suffer.
ORMs will usually mix both reading and writing within the same class, but those are not the same thing.
Readers will have different views of your model depending on who they are or what time it is. Writing on the other hand must fulfill your constraints.
Other common mistakes of ORMs is mapping one class with one table, but your data consist of series of relationships, that can’t be explained with a class.
An ORM creates an illusion that your data is actually the class entities in your repository. This can have the effect of creating constraints in your own mental model of your data, thus making it harder to evolve your schema because you are to fixed on how your classes are designed.
TypeScript is like a relational database (and other structural statically typed languages) of your application behavior, allowing you to query the current possible states of your data at any given point in your application code.
I’ve been thinking about this more since TypeScript got template-string generics, and folks have experimented with eg type-checking SQL strings back into application code.
This is the piece that draws me to an ORM: that it brings the “relational model” into application code, NOT the “object” part of an ORM.
Does anyone have any opinions about which language ecosystems create the most effective type-mappings between a RDBMS and application language?
Part of me also wonders if there’s a ton of time lost on mapping between languages like this when the tooling that’s really missing (afaik) is better tooling for producing, consuming, and generally interacting with SQL and db schemas as the data runs through your application pipeline.
Curious what your use case is, because in every codebase I've worked on, people pretty quickly get tired of writing out SELECT * FROM [table] WHERE id=? and UPDATE [table] SET field=? and looping over database cursors all day, and you end up with a half-done, buggy non-ORM.
But for anything more complex than that (e.g. SELECT queries joining several tables) we're using knex.raw so the query itself is raw SQL where we're very much thinking in the relational model rather than in terms of objects.
You have two options when marrying RDBMS SQL and OO: either mismanage the relational data so that developers can use a class hierarchy, or stop using a class hierarchy to represent data.
IME, applications come and go. They get rewritten, thrown away, obsoleted. The database, however, is there to stay. Mismanaging the data purely so that the application developers don't have to touch all that icky relational stuff almost always results in more work for less returns.
Now let me count the ways in which SQL is bad:
- it composes poorly due to its unwieldy cobolesque syntax
- it is a leaky abstraction revealing a lot of underlying implementation tradeoffs
- it doesn’t properly implement Cobb’s relational model
- it is poorly standardized with a ton of proprietary extensions and alterations present in virtually every implementation
- it is still poorly supported by tools because the model metadata lacks any standard interface to make universal tooling possible
A lot of the praise for SQL is just bandwagon hopping and cargo cult behaviour or a lack of vision by most people of how things could be much better
I do wish someone would create a compile-to-sql language that adds variables and basic conditional support. I think T-SQL (SQL Server) has a lot of this, but it's missing in the Open Source world.
Every database with a procedural language based on SQL (Oracle PL/SQL, Postgres PL/pgSQL, etc.) has it, T-SQL just doesn’t segregate procedural and declarative code the way most engines do. Mostly, I think this is a negative for T-SQL, but sometimes segregation means there’s no way to do one-off procedural scripts without making and then calling a procedure; but Postgres, for example, since 9.0 has the DO statement for this use case (which supports any procedural language installed in the DB, but defaults to PL/pgSQL.)
I can write a query that queries the result of another query, but I can't store a query and then pass that somewhere to query from.
Check the example at the beginning of https://www.postgresql.org/docs/13/queries-with.html
Postgres before it was called 'PostgreSQL', used something called QUEL, which is quite a bit cleaner. There is also Microsoft's LINQ.
A typical programming language can express all that SQL can. In a way, an ORM is a cross-compiler from your programming language to SQL.
SQL has always been a sort a magical, black-box in that you have very limited control over the query planner, and have to hint at it to do the right thing. (I guess `EXPLAIN` allows you to peak in the box a little)
I found [Apache Calcite](https://calcite.apache.org/docs/algebra.html) rather interesting. It provides the primitives that a DBMS is built from.
I would be interested to compare how PSQL is built in comparison. I would love a more layered/pluggable database that everyone can build off of. I think FoundationDB had this approach.
SQL could benefit from being language-integrated, yes. Very few languages can model relational algebra types though, if thats a thing you care about (it certainly is for me).
What I object to is the blanket “shouldn’t care about sql” statement because that’s what empowers people to use the ORM indiscriminately without understanding what’s under it (an understanding for which SQL is relevant) and then it’s the non-value-adding developers’ job to come in and untangle the mess, usually could have been avoided by dropping down one level, looking at the SQL that was generated (or maybe analyzing the query plan - again kind of hard if you don’t understand SQL) and realizing the ORM is doing something crazy.
Often times it's much better to be able to lookup some object by ID of the entity from some Map, than consuming endpoints returning some crazy nested partial data for the current view.
Depends on how much your app relies on client side caching and incremental sync of data.
This is more-true the less efficient the language is that you're using. I see people screw this up when writing e.g. dashboards that source their data from SQL, while leaning on an ORM, all the time, and it always kills performance (talking tens of seconds to minutes for things that should take a single-digit count of seconds, at worst).
What's good at turning complex relational data into a list, and fast? Yep, SQL.
Which is easier if you keep data normalized on the client side too, and look up related objects in a Map when needed, instead of keeping multiple copies of objects representing the same entity everywhere in your client code in some/vasrious denormalized forms.
It would actually be very nice for some use cases if I had a SQL interface to local data on the client side too, so that I can query/join them up arbitrarily as needed from what's loaded up to client storage. That's unpleasant to do in the browser with current platform APIs.
Basically I want WebSQL back in some form, instead of this IndexedDB thing. :)
I think we are all abusing GraphQL in this sense. Caching GraphQL data is a world of hurt.
The easiest way to sync is if your data is in the same model as the way it is stored, which is normalized if using SQL. Even when we do normalize most people will represent a 1-M as an array of foreign keys: `posts.comments = [1, 2, 3]` when in the db we use a foreign key on the `comment` entity: `comment.post_id = 1`. This causes such headaches for optimistic UI updates. It's a mess.
We should be using GraphQL to retrieve data and then render it directly.
Ultimately, I think we should be implementing our GraphQL API client-side against a local SQL DB that acts as a pass-through cache. Then your entire app runs offline and instantly respond to queries eliminating the need for a separate client-side cache, and you don't have to worry about about data model mismatch anymore.
> Depends on how much
I think all apps want this - unless they are SSRing with <100ms on every action.
I'd love to get rid of all these client-side state management libraries and just drop an SQL DB in there and allow components to subscribe to changes to queries on record updates, and then sync it to a cloud DB.
One interesting problem I came across is how to know when your local DB has enough records to be able to run a given query. E.g. If your team has 100K tasks in their cloud DB, and you run `SELECT * FROM tasks WHERE tasks.assignee = user.id`, and then another query of `SELECT * FROM tasks WHERE tasks.assignee = user.id AND tasks.completed = true`, the second task is a subset of the first, so it doesn't need to hit the server except to check if any updates were made to the tasks table. Then apply this to all queries including ones with joins. Actually some fun relational algebra can be used figure it out - one of the perks of working with SQL. The problem is actually similar to how to [incrementally maintain a materialized view](https://wiki.postgresql.org/wiki/Incremental_View_Maintenanc...) which Postgres actually does not support (Oracle does).
That statement is so wrong. All ORMs suffer from leaky abstraction. And good ORMs would never hide that fact, nor would they try to deny native access to the database and force a all-or-nothing principle onto the developer.
If "application developers" care about data that is stored in a relational database, they have to care about the database itself and the access patterns (SQL).
ORMs _only_ provide convenience! They cannot substitute knowledge about the underlying technologies. And they cannot hide complexity, but shift it to the ORM.
Sure, SQL is not perfect, and has it’s flaws. But instead of learning an established, declarative language designed for data access, there are developers that would much rather learn new tools and libraries to avoid it. If you need to use another programming language for data access, you’re off looking for another ORM. Or the next, better ORM of the year comes out.
There are stored procedures at my work that are more than 20 years old. They don’t need to change when the application code calling it is uplifted.
It’s not that much different than a modern React dev not wanting to understand how a plain HTML form works.
Very rarely does an organization completely rewrite their products in completely different languages. And if they do, I'd hope they take the due diligence to investigate the tools they plan to switch to and make sure they are a good fit.
> You have to learn an entirely new ORM each time.
You have to learn an entirely new everything each time. ORM's aren't anything different. New language = new paradigm, new routing, new templating, new everything.
> What about all of the people who have to read your code?
What about them?
> Which ORM's do they know?
I'd assume that the team would have conversations to gather information on what ORM's everyone knows and decide which is best for their team. I don't see how this is an issue. Do you just randomly pull people into your company and just like, let them wander aimlessly?
> Everyone knows SQL.
No they don't. And if they did, a lot of stuff isn't huge massive complicated apps, most are basic CRUD apps where ORM's do all the lifting for 98% of the stuff needed done. A lot of us haven't touched SQL in so long that we couldn't construct a simple SELECT statement if we tried. I know because I wrote SQL from 2000-2014, but then tried to write a simple SELECT statement in a `pg` CLI to fetch some stuff and completely forgot how to do it.
We should only have to care about data and not SQL. But the reality is that we very much do have to care about SQL.
I tend to prefer avoiding ORMs as a preference because of this but unfortunately, development expectations in the current culture don't pad time enough to do this, so you're often reliant on an ORM and wait until things break before someone paying the bills will be swayed to allow developers to go in and clean things up. The "wait until it breaks to go fix and redesign everything" mentality isn't unique to ORMs though, it plagues any abstraction, framework, library, whatever that gets enough done to supply something mostly functional in a shorter time scale.
If you're going to use a SQL database, you need to understand what it is and what rules to obey to get its benefits. Hiding that complexity away makes people think they're writing a fast, concurrent high-availability (TM) service while it's actually a ball of mud that sometimes gets into the right shape.
I think the answer of whether to use one depends on the type and load/volume of app you're working with combined with the dynamics, size, and skill level of your team(s). I'm extremely comfortable writing, profiling, query planning, and debugging SQL queries. Others aren't, and therefore having an ORM to query data in the DB with the syntax of the language you're using in your projects makes way more sense, if nothing other in order to speed your team up.
I mention this because to my mind it reinforces my belief that learning an ORM (which I've done in the past, and then never used again) isn't worth the effort; learning SQL is, if for no other reason than it's portable (and you'll have to learn it anyway when the ORM's abstraction leaks).
A quick glance at the @prisma/client code makes it seem like this pattern is still the case, since they have an "engine-core" package that downloads a binary server behind the scenes and runs it on a free port in the background.
O_____O
- No supported way to do a case-insensitive sorting. https://github.com/prisma/prisma/issues/5068
- Can’t sort by an aggregate value like user’s post count. https://github.com/prisma/prisma/issues/3821
- Can’t control between inner/left join.
- Can’t do subqueries.
- Migration rollbacks are experimental maybe unsupported now? At least I only see mention in Github issues and not in the docs.
- Transactions appear to expect a series of queries? It doesn’t look like you can execute any app code during a transaction?
- No support for pessimistic row locking e.g. SELECT… FOR UPDATE ?
- No way to mixin raw query partials like `where('name ILIKE ?')`. You either need to write the whole query raw or not.
- Validations are done at the database level.
- Complex validations seem tricky to write in this format
- No built-in way to make clean user-facing validation messages.
- You can’t check that a model instance is valid without just trying to insert it into the database
- The official documented validation example has you connecting via psql and adding a constraint?
- So following the offical example my validations aren’t documented in the codebase via a model or a migration?
- Also they don’t have a validation example documented if you’re using MySQL instead of Postgres?
- Cascading deletes are handled the same way as validations. As in Prisma basically does nothing other than document how to implement it yourself outside of the library.
- No model methods. I guess that's not a surprised because it's "not an ORM". A model really is just a data mapping? Anyways it seems like you would end up rolling your own wrapper around this and there's no recommendations on standardized architecture.
- No callbacks. These have been controversial at times so are teams using Prisma writing something akin to "services" instead?
- Syntax nitpick but one of these is vulnerable to a SQL injection and it seems really easy for a new developer to get mixed up?
- prisma.$queryRaw(`SELECT \* FROM User WHERE email = ${email}`);
- prisma.$queryRaw`SELECT \* FROM User WHERE email = ${email}`;
- No way of batch loading like Active Records’s find_in_batches / find_each. All objects are just loaded into memory?
- No way of hooking into queries for instrumentation. e.g. ActiveSupport::Notifications.subscribe
FYI - This is after fairly brief research. Not guaranteed 100% accurate.The biggest issue was the transactions which was a non starter for us. It wasn't very helpful when after explaining our use case and being told "we are doing it wrong" in the GH discussions, and instead were told to write rollback code manually instead of using transactions was a very poor answer.
- In development, we think we already have a better solution. Migrate will tell you when there is a discrepancy between your migrations and the actual schema of your dev database, and offer to resolve it for you.
- In production, currently, we will diagnose the problem for you. But indeed, rollbacks are manual: you use `migrate resolve` to mark the migration as rolled back or forward, but the action of rolling back is manual. So I would say it _is_ supported, not just as convenient and automated as the rest of the workflows. Down migrations are somewhat rare in real production scenarios, and we are looking into better ways to help users recover from failed migrations.
- The migration is reversible in the first place. It did not drop or irreversibly alter anything (table/column) the previous version of the application code was using.
- The up migration ran to the end, it did not fail at some step in the middle.
- The down migration actually works. Are your down migrations tested?
- You have a small enough data set that the rollback will not take hours/bring down your application by locking tables.
There are two major avenues for migration tools to be more helpful when a deployment fails:
- Give a short path to recovery that you can take without messing things up even more in a panic scenario
- Guide you towards patterns that can make deploying and recovering from bad deployment painless, i.e. forward-only thinking, expand-and-contract pattern, etc.
We're looking into how we can best help in these areas. It could very well mean we'll have down migrations (we're hearing users who want them, and we definitely want these concerns addressed).
You can also test migrations against a restored prod database snapshot but again there's no guarantee some incompatible data hasn't been inserted in the meantime.
I wonder if you have looked at how it works. Because they have put in something like a decade to make it work and it's very powerful and a joy to use.
Down migrations are indeed very useful and important once you get used to it. First and foremost they give you a very strong confidence in changing your schema. The last time I told someone who I helped with django to "always write the reverse migration" was yesterday.
No way you can automatically resolve the discrepancies you can get with branched development. Partially because you can use migrations to migrate the data not just to update the schema. It's pretty simple as long as we're just thinking about adding a few tables or renaming columns. You just hammer the schema into whatever the expected format is according to the migrations on that branch. But even that can go wrong: what if I introduced a NOT NULL constraint on a column in one of the branches and I want to switch over? Say my migration did set a default value to deal with it. Hammering won't help here.
The thing is that doing the way Django does it is not that hard (assuming you want to write a migration engine anyway). Maybe you've already looked at it, but just for the record:
- they don't use SQL for the migration files, but python (would be Typescript in your case). This is what they generate. - the python files contain the schema change operations encoded as python objects (e.g. `RenameField` when a field gets renamed and thus the column has to be renamed too, etc.). - they generate the SQL to apply from these objects
Now since the migration files themselves are built of python objects representing the needed changes, it's easy for them to have both a forward and the backward migration for each operation. Now you could say that it doesn't allow for customization, but they have two special operations. One is for running arbitrary SQL (called RunSQL (takes two params: one string for the forward and one for the backward migration) and one is for arbitrary python code (called RunPython, takes two functions as arguments: one for the forward and one for the backward migration).
One would usually use RunSQL to do the tricky things that the migration tool can't (e.g. add db constraints not supported by the ORM) and RunPython to do data migrations (when you actually need to move data around due to a schema change). And thanks to the above architecture you can actually use the ORM in the migration files to do these data migrations. Of course, you can't just import your models from your code because they will have already evolved if you replay older migrations (e.g. to set up a new db or to run tests). But because the changes are encoded as python objects, they can be replayed in the memory and the actual state valid at the time of writing the migration can be reconstructed.
And when you are creating a new migration after changing your model you are actually comparing your model to the result of this in-memory replay and not the db. Which is great for a number of reasons.
- The replaying of the migrations history is exactly what we do, but not in-memory, rather directly on a temporary (shadow) database. It's a tradeoff, but it lets us be a lot more accurate and be more accurate, since we know exactly what migrations will do, rather than guessing from an abstract representation outside of the db.
- I wrote a message on the why of no down migrations above. It's temporary — we want something at least as good, which may be just (optional) down migrations.
- The discrepancy resolving in our case is mainly about detecting that schemas don't match, and how, rather than actually migrating them (we recommend hard resets + seeding in a lot of cases in development), so data is not as much of an issue.
Django doesn't try (too hard) to validate your model against the actual DB schema. Because why would it? You either ran all the migrations and then it matches or you didn't and then you have to. (Unless you write your own migrations and screw them up. But that's rare and you can catch it with testing.) While your focus then seems to be to check if the schema (whatever is there in the db) matches the model definition. Based on my experience (as a user) this latter is not really something that I need help with.
Data is actually an issue in development and hard resets + (re)seeding is pretty inconvenient compared to what django provides. E.g. in my current project we're using a db snapshot that we've pulled from production about two years ago (after thorough anonymization, of course). We initialize new dev environments and then it gets migrated. It probably takes about half a minute to run as opposed to about 2 seconds of back migrating 2-3 steps.
One assumption with Prisma Migrate is that since we are an abstraction of the database, and support many of them, we'll never cover 100% of the features (e.g. check constraints, triggers, stored procedures, etc.), so we have to take the database as the source of truth and let users define what we don't represent in the Prisma Schema. On SQL databases, we let you write raw SQL migrations for example, so you have full control if you need it.
Prisma certainly is not perfect and whether you should use it depends on your project and individual requirements. What I can tell you is that we are shipping releases [2] with new features and improvements every two weeks. We are also very eager to learn about more use cases that people want to accomplish with Prisma. The best way to bring these to our attention is by commenting on existing GitHub issues and creating new ones if the one for your use case doesn't exist yet. This helps us prioritze and implement these new features. We also have a roadmap [3] where you can see all the features that we are currently working on.
Hope that helps for now!
[1] https://www.prisma.io/docs/concepts/overview/should-you-use-...
Most of these are pretty important to us. I did try to put lower priority items near the bottom of the list. However, I did omit several nice-to-haves already. e.g. ActiveRecord's ability to diff changes after an update (ActiveModel::Dirty)
I wouldn't say any of them are complete full blockers by themselves but cumulatively yes they prevent us from considering Prisma for most projects.
A drop-in replacement for PrismaClient for tests would have been wonderful.
Typically you would have a set of unit tests which test your code only and use mocks for external dependencies, in this case you would mock Prisma.
Tests involving DB state are not unit tests anymore and require a bit more setup but most frameworks provide a test harness/runner to quickly spin up a part of your app with dependencies resolved (instead of mocked) so you can call controller/handler/service methods directly and verify their behavior.
The boilerplate for this should mostly be limited to switching out an environment variable for the database connection and maybe setup some fixtures. Is this not possible in Prisma?
It's a tricky problem because our use of the type-system is quite complex.
I quite like your idea of a test mode or maybe a mock client that we generate.
Or maybe just invest in this: https://github.com/oguimbal/pg-mem/issues/27 He has already put a lot of effort into it and I think with just a bit more support he can support Prisma
Yes please!! :)
On the one hand, you can wrap your DB operations in some kind of mockable interface(s), and then test your business logic.
On the other hand, you really need to test that the actual SQL ops work anyway- what if you screwed up a foreign key constraint?- that wouldn't show up in your business logic tests or your in-memory SQLite or whatever.
The big differences between v1 and v2 make me uneasy. I am reminded of the constant API churn of React-Router, or the complete change from AngularJS to Angular. This approach makes me very hesitant to learn the library, because I am afraid of having the choice of painful upgrade or a dead dependency down the road.
I am also wary of the library being VC-funded, because I am afraid of what kinds of features will be held at ransom down the line once they need to start making money.
For my latest large project, I just used SQL and I am happy. All I did was add some code to make sure the snake case was converted to camel case. Things like returning arrays of child relations can all be done in SQL these days with things like json_agg and json_build_object and so on.
Half of my SQL on the other hand doesn't even map to an ORM. Where is the insert ... select ... where not exists ... These things aren't even possible in the ORM concept model.
They have a vs code extension for editing the prisma schema.
It’s not hard to see that you make fields “String” @unique, etc.
Just make one model and that’s 80% of anything you ever need.
Want to make a relation? Make a new model
model Posts { users User[] }
Hit save, it adds the required code to relate it to the User model.
Run the generate command. Make a client instance.
Create something: db.NameOfModel.create()
And as you’re typing, typescript is auto completing it.
There’s barely anything else to learn. And it’s a quick doc search away.
Don’t hate on it because you love sql, it’s a great tool.
That is not how this works at all. You don't know what you don't know until you need "how do I do this query in prisma". You then spend an hour trying to figure it out, and maybe it isn't possible, then on to the next thing. It takes more than an hour to learn SQL, so if it takes you an hour to learn Prisma, you are missing something.
In fact. One that prisma didn’t support.
Ordering by the count of a relationship, and returning the few highest counts.
So I added a GitHub issue that is being tracked.
And what did I do for now?
I wrote the SQL using prisma’ raw query function.
You can learn SQL and use an ORM.
Things don’t always have to be one vs the other
I would rather forget everything about Prisma the second I wake up in the morning and have to read their docs on how to do everything all over again, every single day, than try to understand what the heck is going on in SQL's docs.
Most queries you can figure out just from typescript auto complete too...
Majority of queries in an app are basic relationships and crud actions... doesn’t take endless hours figuring this out with prisma.
If it did, we wouldn’t use it.
That's because the ORM cannot do a lot. Unless you're doing extremely simple SQL queries it will not handle what you want it to do. Now what? Drop into RAW sql and lose all type safety and you are on your own.
No thanks, I started using Prisma for https://listifi.app and switched it over to knex.js because the second I hit a query that Prisma couldn't handle I was on my own. Knex on the other hand could handle everything because it's a simple query builder.
I think the main difference of these libraries compared to Prisma is the level of abstraction. If you want to work with SQL and you appreciate type-safety, then all of these tools will be fabulous options for you! If you want to work on a somewhat higher level of abstraction but still have full type-safety, Prisma will be more appropriate.
hah. This looks cool, thanks for sharing!
I guess the post refers to our opinionated stance on "long-running transactions" which Prisma indeed does not at the moment. The best resources to learn about this are on GitHub [2] and our blog [3].
[1] https://www.prisma.io/docs/guides/performance-and-optimizati...
[2] https://github.com/prisma/prisma/issues/1844
[3] https://www.prisma.io/blog/how-prisma-supports-transactions-...
But my gosh "Application developers should care about data – not SQL"
That is fundamentally wrong - or at least - it is very limiting.
Think very hard about adopting a framework that tries to shield you from SQL. Because at the end of the day, you will be looking at SELECTS and INSERTS in your logs because you are wondering why things are so slow or why your memory usage skyrockets.
It looks like Prisma allows you to go to raw SQL if you must, yet you won't have a good time using some of PostgreSQL more advanced features.
If your domain model is simple and will remain simple in the future, then - by all means - use a builder that creates queries from your domain model. Use a single-source-of-truth approach that is usable from the back-end and front-end.
In other cases, where your domain model changes over time, be very careful when choosing an ORM but extra double careful in picking the right data store.
Prisma is the first time it all just makes sense.
The code generation, auto migrating without any code, automatic crud resolvers with nexus, the list goes on.
After using it, I don’t know how you can use any other language for backend. It would just take you much longer and require so much extra work. Nothing like prisma exists in other languages.
Also FoalTS is a nice small framework for building rest endpoints with TypeScript, and recently landed @prisma support/documentation besides typeorm.
Just random fyi, hope someone finds it useful if they wan't to try prisma in a more batteries included package.
For anyone who uses PostgreSQL and is interested in Prisma, check out the following message, which has a section that includes a list of features that were deemed out-of-scope (e.g., bulk upserts):
https://github.com/prisma/prisma/issues/4998#issuecomment-76...
Prisma Raises $12M Series A - https://news.ycombinator.com/item?id=23651605 - June 2020 (111 comments)
Prisma 2.0 – Type-safe and auto-generated database client - https://news.ycombinator.com/item?id=23466834 - June 2020 (107 comments)
Prisma 2.0 Beta: Type-safe Database Access - https://news.ycombinator.com/item?id=22739121 - March 2020 (122 comments)
Comparing Database Types - https://news.ycombinator.com/item?id=21060866 - Sept 2019 (170 comments)
Prisma – Database tools for modern application development - https://news.ycombinator.com/item?id=19598287 - April 2019 (83 comments)
Show HN: Prisma – Turn Any Database into a GraphQL API - https://news.ycombinator.com/item?id=17076202 - May 2018 (16 comments)
Prisma Cloud – A GraphQL Database Platform - https://news.ycombinator.com/item?id=16529450 - March 2018 (9 comments)
Prisma – An open-source GraphQL API layer for your database - https://news.ycombinator.com/item?id=16160803 - Jan 2018 (34 comments)
Prisma: Turn your database into a real time GraphQL API - https://news.ycombinator.com/item?id=16159874 - Jan 2018 (9 comments)
(sorry just a joke, ORMs do have their place and this looks useful. I'd think of Prisma as a lightweight ORM because it doesn't try to do "Business Rules" as they are known. But then, trying to do Business Rules will generally turn everything into a nightmare anyway so maybe lightweight ORM is the way to go)
ORMs are useful. ORMs aren't always the right answer. ORMs that don't let you mix and match so you can bypass any given layer of abstraction as required annoy the pants off me ... usually even more than anti-ORM zealouts annoy the pants off me.
So, yeah, joke appreciated :D
Edit: After quickly going through the intro, I prefer MikroORM, it's simpler.
It's simple and the use of identity map within a unit of work, tied to a request context (for example), is just brilliant.
The handling of relations (with configurable loading strategies) and other features such as Embeddables and Filters make it a real joy to use.
And if at any point I can't achieve something with MikroORM itself (which generally in my case is calling a PostgreSQL function), I can easily grab the underlying Query Builder from Knex.
It also has the tooling to generate a schema from your Entities, or generate Entities from an existing schema - so it's easy to get started on something new or existing.
Other upsides are a much better RBAC column/row permissions, migrations with up/down, a better admin UI.
I find that with new tech, I'm more interested in what it can't do rather than what it can.
One big one is at some point you need caching on a query level, and for that you have to purchase enterprise. I suppose they need to make money somehow, and it's a smart level to do it because it's almost impossible to not need caching at some fairly early stage. It would be nice if they let you test it out without "Contact Sales", and while there may be some way to wrap it from above, I don't think it's very easy.
A meta critique would be the project seems to have less momentum than Prisma. Releases aren't as frequent or large, and I'd have much preferred they double down on PG and keep adding query abilities, performance, etc, instead of expanding database engines.
That sounds like it has "vendor lock-in" written all over it.
Hasura is really awesome, so far as I have used it.
Now I gotta look into prisma
I almost wonder if they aren't mutually exclusive. With hasura, you need to run your own server anyways, for auth or special cases. Maybe that server could run prisma? Let hasura do the migrations, and prisma track it?
One of the things that is most perplexing to me is the inability to autogenerate a seed file from existing data, to reseed a new database. This would be incredibly helpful for testing and working with staging data. For example, we have a staging database and staging server, and want to be able to reseed the database with some data set, but don't want to write all the data inserts by hand. I'm sure there are many ways to avoid doing this (db-specific export/import, scripting the inserts in a loop over a collection of arrays of values which you've curated by hand, or in our case just copying the sqlite db file). Still, Prisma's story around seeding and data fixtures in general currently leaves a lot to be desired.
There have also been many rough edges I've run into around shaping results (getting category names which have been applied to all blog posts by a given user, for example), and applying schema changes, but I'm not willing to rule out user error / inexperience for many of these
There is no shortcut. And we don't need one.
For most non trivial applications - when I used an ORM or other abstractions over SQL - there came the point at which I needed to dive quite deep into the workings of the ORM. However, learning how your DB and SQL works is much better spend time than learning the complexities of an ORM.
Equally important: The time saved upfront diminishes over the lifecycle of the project. Often (not always) figuring stuff out about the abstraction costs much more time and energy than writing the boilerplate. Typing out the boilerplate is boring, that's why we hate it so much. Figuring something new out? Time flies ...
For my projects I've got a template for basic CRUD operations including a frontend store. Full control, easy to use and understand and only a couple of minutes to fill in the field names for a new database table or view.
Food for thought if prisma.io team is watching.
[0] https://www.paloaltonetworks.com/company/press/2019/palo-alt...
[1] https://blog.paloaltonetworks.com/2019/11/cloud-prisma-cloud...
I would love to try Prisma otherwise.
WHERE id IN (<subquery>)
if I built up <subquery> using the ORM.The other uncommon thing I look for is eliminating N+1 not just based on a given query and related entities, but given a starting collection and loading their related entities and further related entities.
Icing is if it can do all the above while making intermediate results asynchronously available as it works through it all.
Checked here for window function support. https://www.prisma.io/docs/concepts/components/prisma-client...
> Prisma currently supports PostgreSQL, MySQL, SQLite, SQL Server (Preview). A connector for MongoDB is in the works, sign up for the Early Access program here (https://prisma103696.typeform.com/to/FriDuIeM).
I think this quote summarizes our plans nicely: Prisma's vision is to democratize the custom data access layer used by companies like Facebook, Twitter and Airbnb and make it available to development teams and organizations of all sizes.
The open-source ORM we're launching today will of course remain open-source and we'll keep investing into it since it's be the foundation for the commercial tools that folks will be able to use on top.
We've explained that in the blog post here in the "Open-source, and beyond"-section [3].
Does that help? :)
[1] https://imgur.com/O1lwo0v.png
[2] https://imgur.com/Hb9VOWN.png
[3] https://www.prisma.io/blog/prisma-the-complete-orm-inw24qjea...
Can you elaborate what exactly you mean with "GraphQL integration"? You might be referring to Prisma 1 which was a "GraphQL layer for your database". Prisma 2 (which we are referring to with this article) doesn't have a native GraphQL integration any more :)
Prisma is very different from Sequelize in various ways (see our docs [1]):
- Your data model lives in the Prisma schema
- Database access doesn't happen via model instances but via the Prisma Client API [2] which always returns plain JS objects making your queries much easier to reason about - Prisma Migrate auto-generates migrations based on your data model (and lets you customize these migrations when needed)
- Prisma comes with Prisma Studio out of the box – a modern database GUI
- Prisma is not a community-project but built by a VC-funded company
– We care a lot about our community, documentation, and providing support (something we've seen lots of developers complain about with other ORMs)
[1] https://www.prisma.io/docs/concepts/more/comparisons/prisma-...
Thanks for clarifying this. I know there tend to be a kneejerk reaction towards VC-funded software projects. How do you guys plan to make money on this?
I think this quote summarizes our plans nicely: Prisma's vision is to democratize the custom data access layer used by companies like Facebook, Twitter and Airbnb and make it available to development teams and organizations of all sizes.
The open-source ORM we're launching today will of course remain open-source and we'll keep investing into it since it's be the foundation for the commercial tools that folks will be able to use on top.
- use DBs that directly provide GraphQL endpoints such as Fauna, Upstash, etc. - use an ORM with more traditional DBs.
What are the pros and cons of each?
Note: the performance impact does depend heavily on whether your database maps well on ORMs. Traditional databases have an impedance missmatch when it comes to translating tables to an objet format. Graph databases or the way Fauna works (documents with relations and map/reduce-like joins) map well on ORMs so the performance impact would be small.
TypeORM -> classic ORM, the "default" choice in node, but not very mature compared to what exists in Java or things like Django ORM. In my experience it's not an amazing lib, transaction management is a bit painful, I've seen a couple bugs happen.
Knex -> it works but not sure why you'd use that rather than TypeORM
MikroORM -> not a lot of experience, one of my colleagues really likes it, read the principles behind it and see if that talks to you as it's a bit opinionated
Slonik -> Never used it but probably what I'd set up on a new project. Like many people I think that query builders are BS and I'd rather use SQL: Slonik makes that very easy while giving you the usual getOne/getMany utilities
I'd like to try Prisma though, I've heard good things
It's been excellent for me -- I ignore the bad abstractions, have a good underlying database (postgres), and drop down to parametrized raw SQL whenever I need to, it's held up extraordinarily well I think.
Could you expand on some specific issues/frustrations? Just curious about the side I'm clearly not seeing.
The things that the DB can do (I'm thinking about postgres) are amazing, and you just aren't going to get a chance to use a lot of it really from a lowest common denominator ORM. Yeah, you probably don't need it -- but then again why not just use a tool like Postgrest from the start?
The downside for me about dropping into raw SQL is that it makes much of the usefulness of an ORM in being able to refactor your schema and stuff disappear, because the type system and instrumentation can’t auto-refactor a raw SQL string. But I agree that I’d take that over relying on TypeORMs bugginess.
> I’m just scratching the surface, it’s telling that there are 1500+ unresolved issues on the repo and 200+ unmerged PRs.
So just a side note on this, I judge repositories based on teh ratio of closed to unresolved (and whether they have a bot that auto-closes issues). 200+ unmerged PRs is bad -- that maintainer needs help. I've interacted with the main maintainer before a long time ago and I had a good impression, but they probably weren't ready for how big their project got (how can anyone be) and they clearly didn't charge enough to make it a joy to work on their project. Money isn't the main motivator for everyone, but it would enable hiring people, or taking vacations as appropriate, etc.
There are quite a few other ORMs for working with DBs in TypeScript (none of them providing the same type-safety guarantees that Prisma does though [1]).
In fact, we laid them out on a docs page [2] in case you want to learn which tool might be appropriate for your use case.
[1] https://www.prisma.io/docs/concepts/more/comparisons/prisma-...
[2] https://www.prisma.io/docs/concepts/overview/should-you-use-...
What I am looking for is ideally something that helps with type saftey and seeds/migrations/schema creation.
The thing is of course that with very complex queries you'll end up making quite intricate string building stuff which may not be very pretty. I myself am curious to find out if Prisma can make that part go away.
There is also a good discussion going on here: https://www.reddit.com/r/node/comments/l00fit/what_do_you_th...
I think we can achieve a huge simplification in the industry if we develop a universal state-management solution that runs in the frontend and backend.
Without a consistent model, we are forced to write so much additional code to manage caching, optimistic UI updates, and offline capabilities.
Current RDBMS' are not ideal (most data is better represented as a graph), but because the relational model is so widely used in the backend, they are a good candidate for a universal data store on frontend and backend. And it is possible to represent graph-like structures in RDBMS' too, but it makes SQL querying very unfriendly.
GraphQL is an ideal language for declaratively specifying what data you need for the UI layer. If you doubt this, play around with the [Github GraphQL API][1]. It's an incredible experience to be able to explore and pluck exactly what you need. SQL and REST don't come close to this experience.
But GraphQL is essentially providing us a nicer way to write SQL compared to using JOINs, and to output it in a nested format for convenient rendering with nice typing. But it lacks a huge amount of flexibility.
So we should keep GraphQL as our API, but we should implement it client-side against a data model similar to what we use in our cloud db: SQL, and then do real-time syncing or use it as the model for our server-cache.
An added benefit is we don't have to worry anymore about what is local-state and what is server-persisted, or worry about choosing a good state management solution client-side.
The necessary pieces to make this happen don't exist at the moment, but I would have liked to use Prisma as the DB API for in-browser SQLite in the beginning.
I think there is then the opportunity to go full-stack and offer developers a client-side GraphQL library that talks to an in-browser SQL DB and to end the constant turnover in state-management libraries. I think every frontend developer is overwhelmed at this point by the proliferation of these libraries, and the simplicity they go seeking in things like "just use React.Context api", don't offer them what they will eventually need which is optimistic UI updates, offline modes, real-time collaboration, local-first experiences, etc.
I think we are all still in search of the right state management solution and it would be cool if Prisma did it!