ORM & Database
❓ How it works
Section titled “❓ How it works”The project uses MikroORM for ORM and database management. The implementation is based on the MikroORM NestJS module provided by the MikroORM team.
💡 Why we use MikroORM
Section titled “💡 Why we use MikroORM”MikroORM is a solid ORM, supported by a strong community (and an excellent developer), and provides really good default for the technology we use.
-
Very stable and proven
-
Very close to SQL, no fancy abstraction
-
Easy to extend as needed (e.g. custom columns)
-
Actually based on Knex, a rewrite is planned for v7 to use Kysely instead
-
Drizzle, très cool car 100% TS mais avec encore pas mal de lacunes quand on creuse un peu (pas de transactions par défaut, un typage bif bof trop basé sur l’inférence, etc.)
Why not TypeORM?
Section titled “Why not TypeORM?”TypeORM has been around for a long time (we used it for years before switching to MikroORM), and it shows. Its core architecture wasn’t great, and it has become a mess of spaghetti code.
The project is not longer maintained by a dedicated team, and its stability and performance are not great.
Why not Prisma?
Section titled “Why not Prisma?”We have tested Prisma extensively, and even use it in production for some projects. However we chose to stick to MikroORM because:
- Prisma adds a compilation step, which slows down the development process (often need to restart the ts-server, and this can get slow on large projects)
- Prisma does not provide a “default” transaction management for incoming HTTP requests, which is a must-have for NestJS projects
- The support for custom columns is really bad. A custom column will remove the main methods on the entity (
findMany,create, etc.), the column is completely ignored by the ORM. You need to then extend the whole Prisma client to re-create the methods. Comparatively, other ORM allow to create “hooks” or transformation methods (in input and output) on the column (Mikro or Drizzle). - To build these methods in the extended client, the only solution is to pass by SQL, which cannot be validated at build (IDE extensions allow to validate at least at dev time). To avoid losing too much reliability, we decided to double some queries (we let Prisma do a
findthen we do aSELECTmanually just for our missing custom column), it’s far from optimal but we prefer it to … rewrite everything in SQL! - The experience with Nest.JS is very bad if you need to extend the Prisma client. I lost hours to find a clean solution, and I’m not convinced yet. Issue Github
Why not Drizzle?
Section titled “Why not Drizzle?”A lot of great things with Drizzle, which we like quite a lot:
- It’s really easy to set up, even more than Prisma
- It’s nice to define your entities in Typescript, even if the relations are more verbose than Prisma
- The migrations and
drizzle kit pusbwork well - The generated types are useful (but with some limits see below)
- Support of PostGIS by default, and custom types seem to be better supported
- Easy to setup hooks on events
But some drawbacks:
- There are some quite basic Typing which are not exported by Drizzle. For example, if you want to type an object
filtersthat you would use inselect(…).where(filters), you need to use inference to get this type. - You lose a lot of “magic” proposed by default in Prisma or Mikro. For example, it’s not possible to add relations in an object you would want to persist, you will need to make several requests. This makes things more difficult for dynamic helpers, etc.
- The query builder is less practical than the one of Mikro
- When you are used to Mikro and its “default transactions” (via em.flush), it’s a pain to manage everything manually in Drizzle. It reminds me of the time we used decorators to pass the TypeORM context from one method to another in our services. Honestly for the moment I don’t know how to manage, this seems not at all usable in prod.
📝 How to use
Section titled “📝 How to use”We use Mikro in a very basic way, it’s up to you to read the official documentation for more details.
Some important concepts to know:
- MikroORM use the concept of “Unit of Work” and “Identity Map”. Those concepts are really key, so ensure you understand them well.
- Linked to those concepts, be warry of
em.fork()which will create a new Context - MikroORM NestJS module automatically creates a new Context for each request, you don’t need to do anything special to get it. But if you want to use some functions outside of a request — for example in a CRON job — you will need to create a new Context manually (or use the
EnsureRequestContextdecorator).
:::note [About migrations] Migrations are an important part of database management, you can read more about them in the Database Migrations page. :::