Skip to content
Stand With Ukraine
On this page

Benchmarks

In following benchmarks I'm comparing time elapsed on queries with different ORMs and query builders.

All queries are running sequentially.

Y-axis represents milliseconds - the lower is better.

All tests are done locally on a laptop with Intel Core i7 10 Gen of U-series, Manjaro Linux.

All the code with instruction is in the repo here.

WARNING

Treat these benchmarks as approximate and outdated (captured in Feb 2023), Orchid ORM and competitors changed since then.

Better benchmarks with a use of isolated Docker instances per ORM, with ops/s instead of ms results, are to be done yet.

Tested against Prisma and Sequelize, which are probably the most popular node.js ORMs.

kysely included out of curiosity, it was interesting how it handles more complex cases. Didn't figure out how to make nested select or insert with kysely though.

knex as well is included for simple cases, but doing nested selects and inserts is too complex with it.

Load all records from a single table

Measuring a simple query which loads all records from the table with 10 columns, 1000 times.

Load nested relation records

Measuring a query with nested select, query is performed 500 times.

Here is code for Orchid ORM:

ts
await db.post
  .select('id', 'title', 'description', {
    author: (q) => q.author.select('id', 'firstName', 'lastName'),
    tags: (q) => q.postTags.pluck('tagName'),
    lastComments: (q) =>
      q.comments
        .select('id', 'text', {
          author: (q) => q.author.select('id', 'firstName', 'lastName'),
        })
        .order({ createdAt: 'DESC' })
        .limit(commentsPerPost),
  })
  .order({ createdAt: 'DESC' });

This query is selecting all posts with specific columns, author of post with its columns, post tags, last 3 comments of the post, each comment includes its author's data.

The queries of Prisma and Sequelize are fetching the same data, except that they don't allow to pick resulting field names like lastComments, so the result must be mapped on a JS side.

Simple insert

Insert data to a table with 7 columns not counting autogenerated by the database. Run query 1000 times.

All ORMs give more or less consistent numbers, except Prisma: for the first attempt it showed 2.1s, second attempt 1.3s, third attempt 1.8s.

That's a mystery why Knex is slower here than the others.

Nested insert

Insert post with comments and with tags, 1000 times.

Sequelize is only inserting posts, because I didn't figure out how it is possible to insert related records all at once.

Why Orchid ORM performs faster

Orchid ORM is build with performance in mind, it aims to perform as few queries as possible to load as much as possible. It does no mapping unless necessary.

Prisma is based upon Rust server, communication between node.js and the Rust server is implemented inefficiently, so it is slower in some benchmarks than the others.

Sequelize is generating gigantic inefficient SQL queries when it tries to load relations, it is slower in tests with selecting relations.

Some ORMs like Sequelize, TypeORM, MikroORM are performing mapping to a class instance: first data is loaded from db, then they construct class instances with the data. And when you need to return JSON response to client, need to serialize these class instances to plain objects first.