Today, I was discussing with a friend which PostgreSQL client (ORM) he should use. Choosing the best option is a challenging task, but there's a spicy take I always bring up in this conversation: avoid using Prisma as your ORM.
Table of Contents
- Introduction
- Why Not to Use Prisma?
- Why Is This a Problem?
- Deep Diving into the Problem
- Consequences
- When Should I Stop Using ORMs?
- Conclusion
Why Not to Use Prisma?
If you're unfamiliar, Prisma is a well-known TypeScript ORM for PostgreSQL and MongoDB. It was the first ORM I learned to use, and this decision led to some difficulties later on.
Prisma's primary issue is that its entity creation and database modeling only work with their own language.
Why Is This a Problem?
First, let's compare a Prisma entity with a TypeORM entity to understand what makes Prisma different from other ORMs on the market.
The code above represents a TypeORM implementation of a user entity.
As you can see, we're using TypeScript to validate all the fields based on their types or relationship references. In the code above, I used the library class-validator to apply the validation decorators.
The code above represents the same entity, but rewritten entirely using Prisma's own language.
Deep Diving into the Problem
What's the problem here?
At first glance, it might seem trivial, but it actually makes a lot of sense: with Prisma, you're forced to create all your logic using their programming language (or markdown language, I'm not sure what it's called).
This specifically prohibits you from using external libraries or JavaScript code to build more reliable logic in your entities.
Consequences
Since you can't use JavaScript in your code, you should expect to waste a lot of time trying to figure out how Prisma's scaffolding language works and how to create schemas using it.
There's also one very important detail: you can't use any external library or helper.
Imagine a situation where you need to customize the way your primary keys are generated. With TypeORM, you can simply use this approach:
However, to achieve the same result using Prisma, you will need to do something like this:
When Should I Stop Using ORMs?
There is an interesting discussion where developers argue that using ORMs is a mistake and they need to stop it as soon as possible.
I really disagree, but with some conditions.
I agree with freeing yourself from ORMs when they function like Prisma: they expect you to learn a completely new language or methods just to make it easier to write code.
"Wait, are you saying that creating an easier ORM is bad?"
No. I am saying that removing the flexibility of writing more raw-like queries to make the ORM easier can cause issues for developers, such as forgetting how to use SQL.
Conclusion
This article is clearly a matter of opinion, but I am trying to illustrate a straightforward point: you should not use ORMs that force you to be distant from SQL queries. Your ORM should help you better organize and formulate your queries, not write entire queries for you.
What do you think?
Photo by Milad Fakurian on Unsplash
Top comments (18)
I’d argue that Prismas schema is a lot closer to sql than typeORM. You’re also learning something new, whichever one you choose.
As someone who writes a lot of sql at work, I love Prisma for my personal projects where I want to move faster. Prisma is incredibly simple to learn, and you can dip into raw sql very easily.
Be careful about using TLAs without explaining them first, you lose out on potential readers!
I agree!
thanks for the feedback. I'll be careful next time
Maybe read the documentation a little better, none of the downsides you mentioned are unique to prisma, you will have the same issues in all strong typed orms, regardless of what programming language you chose. Low barrier to entry of js/ts with high entropy makes it so that everyone with 3 braincells thinks they should succeed without even reading the docs, that is just insanity.
And about the conclusion "forgetting how to use SQL", where to even start... if someone actually KNEW SQL they would just use SQL for everything, which is generally the case (show me an SQL expert still using an ORM rofl), then the ORM is not there so you stop sucking at SQL, it technically serves different purposes from a mental model perspective; then ORMs don'
t make you suck at SQL, it's relying on them without even wanting to do
OrmQuery.toString()
or whatever the method is in your preferred programming language.Hot take: ORMs don't make you bad at SQL, it's your lack of commitment to understanding how something works.
Final hot take: When using an ORM, reflect on the compiled SQL; when using an SQL abstraction layer, it's smart to also learn SQL in the process/parallel/whatever. This is true for any abstraction layer.
You want to use Super-X that makes your X life easier but you don't know X or Super, maybe this post is just a journal, and maybe opinions are not knowledge. Guess it's time to unsubscribe from Dev, this being a weekly promoted post is just stupid for the 20th time in a row.
I couldn’t agree more
Hi,
The only issue I would consider is the loss of flexibility.
I was searching for an ORM like SQLAlchemy we have in python but didn't found anything.
I think no other language have something like that, the closer I found is drift, a dart ORM but it uses code generation too, anyway it is rater advansed.
Well, EFCore+LINQ is a very flexible tool, too and it's pattern can be implemented in any OO language.
Go and check SQLAlchemy to see what I'm talking about.
Said that, Prisma is a good tool to go fast in the JS/TS land even with it's flexibility loss, and the schema DSL is not that hard to learn.
SQLalchemy reminds me of Hibernate
ZenStack solve all this "problems"
I take your point on the limitations because there most definitely are. However, I'd argue that if you're doing something that contravenes convention then you should be questioning your design and not the ORMs! Anything that is a wrapper around another language is an abstraction, and all abstractions have limitations.
I'd also argue that learning this is a moot point given how easily it is to leverage LLMs to generate DSLs.
It feels a little unfair to write off an entire ORM because of a personal gripe/preference.
For your "deep dive" I think you're leading yourself to your own answer there.
The fact you are calling the Prisma schema "markdown" shows it's a definition language. Programming isn't meant to live here. This simply defines the database structure.
prisma.io/docs/orm/prisma-schema/o...
I think one of the strongest parts to Prisma is its migration engine, and database introspection; even though I still long for RoR Active Record's migrations in the JavaScript ecosystem.
Thoughts On Logic Placement
"Model Hooks"
RoR had an idea of "model hooks" but even this may be a weak form of what you're looking for in terms of primary key generation.
Let the DB Handle Key Generation
One could argue that the DB should handle the primary key generation. This may end up helping with relationships, and indexing down the road. It's possible you may have another ID for presentation's sake. Also it's nice to have the DB avoid uniqueness conflicts which auto increment, or uuid can avoid.
If I'm remembering correctly you can actually utilize functions for default values in Prisma (I had to do this for a 3rd party since standard UUIDs were too large for its idempotency key 😵💫🤯). This would require you to write your logic utilizing SQL and add the function to the DB likely through a migration so that all developers and environments can have parity. This also enters the realm of arguing where logic should exist. Within the DB is likely quicker, but is easier to lose the scrutiny of tests and coverage. It may also become harder to debug, and live outside of your logging suite. Then again most folks are leveraging an ORM for abstraction and simplicity, so this route is counter productive for some.
PS: this is hard to find in the documentation, hence the lack of a link. Or this may require stitching two ideas together possibly too.
Design Patterns
Another argument may be to utilize a pattern such as the decorator pattern or the repository/plugin pattern. This allows you to abstract your logic away from your ORM's logic and responsibility.
You might even go further to have a "service" that sits above your repository, if the sole responsibility of your repository is to handle CRUD between the business layer and the ORM, and potentially convert the ORM type to a business type. This is adds quite a few layers of abstraction but may be best for some. I would argue this is the level of abstraction most are trying to avoid by leveraging an ORM like Prisma.
Back to ORMs
Let us not forget the true meaning of ORMs. Yes we can write raw SQL that is performant, but originally ORMs - Object Relational Mapping allowed us to more easily fetch and save data to and from our databases while modeling it as an object that was easy to work with.
I think over time we've started to expect much more from ORMs such as validation, types, migrations, introspection, relationships, hooks, synchronization, etc. We start to stray away from the single responsibility principal that is part of the SOLID principle. All these things in one package make our lives easier most often, but also adds additional abstraction, and slowdown at the price of simplicity.
PS: it's been a while since I've touched Prisma. The last I remember is that it didn't utilize joins for relationships which was a huge slow down, although there is a whole rabbit hole to go down for whether or not JOINS are the fastest route to go 😆
PSS: remember to index your database where needed, and don't over index :P
If the problem is learning a new language, I don't see a problem at all.
skill issue. you can also auto generate schema it's not that hard man
sequelize is a very good orm, its similar to mongoose queries, easy learning curve, allows us to write manual queries where ever necessary.
Sequelize is not supported in typescript i guess
github.com/sequelize/sequelize-typ...
Some comments may only be visible to logged-in visitors. Sign in to view all comments.