Objection.js: an ORM that admits it is a query builder and means it
An SQL-friendly ORM for Node.js
At a glance
- What is it?
- A knex-based layer for Node.js that gives you models, relations, eager loading and graph inserts while leaving SQL as the query language and migrations entirely to you. Version 3.1.5, MIT licensed, and one of the few ORMs whose README lists what it refuses to do.
- Who is it for?
- Objection.js is a good fit for a team that wants relation handling and eager loading without giving up control of the SQL, and a poor fit for one that wants entities, a persistence layer, or automatic schema management. The README is unusually honest about that, and the three things it declines to do are the three people migrate away over, so read the list before you adopt it rather than after.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 122 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Star history
- Vincit/objection.js7,337 ★ · -3
| Repository | 2026-10-08 | 2026-10-22 |
|---|---|---|
| Vincit/objection.js | 7.3k | 7.3k |
-3 Stars in the last 14 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The README argues with the acronym in its first paragraph
Objection.js is an ORM for Node.js that aims to stay out of your way and make it as easy as possible to use the full power of SQL and the underlying database engine while still making the common stuff easy and enjoyable. That is the marketing sentence. The next part is the actual position, and it is unusual to see a library argue with its own category this early:
Even though ORM is the best commonly known acronym to describe objection, a more accurate description is to call it a relational query builder. You get all the benefits of an SQL query builder but also a powerful set of tools for working with relations.
That sentence is the whole design. Everything else in the library is either knex passed through, or a relation system layered on top of it.
The dependency structure makes that concrete. Objection.js is built on an SQL query builder called knex, and all databases supported by knex are supported by objection.js. SQLite3, Postgres and MySQL are thoroughly tested, with the test suite linked from the README as evidence. So the database question is answered by knex's support matrix rather than by objection's, which is a real advantage when your requirements include a database neither project has ever heard of.
The scale is established: 7340 stars, 637 forks, MIT licensed, with the last push to `main` on 2026-06-23 and 135 open issues. That is a mature, actively used library rather than an experiment, and the contributor list in `package.json` names Sami Koskimäki as author alongside people from Vincit, Photostructure and elsewhere.
What you get: models, relations, graph operations, transactions
The feature list is short enough to read in full, which is itself a design signal.
An easy declarative way of defining models and relationships between them. A simple way to fetch, insert, update and delete objects using the full power of SQL. Powerful mechanisms for eager loading, inserting and upserting object graphs. Easy to use transactions. Official TypeScript support, with typings at `typings/objection/index.d.ts`. Optional JSON schema validation. And a way to store complex documents as single rows.
Two of those deserve unpacking because they are where a query builder stops being a query builder.
Eager loading and graph operations are the reason to use objection rather than raw knex. Loading a parent with its children in one round trip, and inserting or upserting an entire object graph with correct foreign key ordering, is the work that hand-written SQL pushes into application code and gets wrong. Doing it declaratively off the relation definitions is the value proposition.
Storing complex documents as single rows is the pragmatic escape hatch. Not everything wants to be normalised, and a document column that keeps a JSON blob intact is a legitimate modelling decision rather than a failure of the relational model.
JSON schema validation is optional and built on Ajv, which appears in the dependencies along with `ajv-formats`. Keeping it optional is the right call: validation belongs at the boundary, not between every internal function call, and making it a choice lets you apply it where your data actually enters the system.
TypeScript support is official rather than community-typed, and the `test:typings` script runs `tsc` as part of the test command, which means the typings are checked in CI rather than maintained on goodwill.
What you give up, stated plainly
Most libraries bury this section. Objection.js gives it equal billing, and the three refusals are the whole argument for the design.
No fully object-oriented view of your database. With objection you don't work with entities, you work with queries. It doesn't try to wrap every concept with an object oriented equivalent, and the reasoning given is that the best attempt at doing that is Hibernate, which is excellent but has 800k lines of code and a lot more concepts to learn than SQL itself. The conclusion drawn is that writing a good traditional ORM is borderline impossible, so objection attempts a completely different way of working with SQL.
No custom query DSL. SQL is the query language. The clarification is important though: you don't have to write SQL strings, because a knex-based query builder constructs the SQL, and if the query builder fails you the `raw` helper is there for raw SQL strings. So this is a claim about the escaping hatch always existing, not about string concatenation being required.
No automatic database schema creation and migration from model definitions. The reasoning is that automatic schema generation is convenient for simple things but usually gets in your way for anything non-trivial. Objection leaves schema work to you and recommends knex's migration tool for the job, pointing at the `examples/koa-ts` project as a reference implementation.
Those three map exactly onto the three common complaints about ORMs: entities are leaky, the DSL hides SQL, and generated schemas fight migrations. If none of those bother you, there are simpler options. If all three do, this is a coherent position rather than a missing feature list.
The test infrastructure tells you how seriously this is maintained
There is no code fence in this project's README, which is consistent with a library whose value is its API rather than a copy-paste snippet. The shipped examples are the documentation: `examples/minimal/`, `examples/koa/`, `examples/koa-ts/`, `examples/plugin/` and `examples/plugin-with-options/`.
The recommendation is to clone the example project and start playing with it, with the TypeScript version offered alongside. The plugin examples matter more than they look: a plugin system with options is what lets a team extend query building without forking, and it is the mechanism that keeps objection.js from having to grow features for every edge case.
What runs in CI is visible in `package.json`. The `test` script runs eslint, then mocha with `--slow 10`, a 15000ms timeout, the spec reporter, recursive discovery over `tests/`, excluding `tests/unit/relations/files/**`, and finally `npm run test:typings`. There is a `test:fast` variant that adds `--bail` for a quicker inner loop. Prettier and eslint cover `examples`, `lib`, `tests`, `testUtils`, `typings` and `doc`. Docs are VuePress, built from the `doc/` directory with `docs:dev` and `docs:build`.
The databases that suite runs against come from `docker-compose.yml` at the root:
version: '3'
services:
postgres:
image: 'postgres'
container_name: 'objection_postgres'
command: postgres -c fsync=off -c synchronous_commit=off -c full_page_writes=off -c random_page_cost=1.0
ports:
- '5432:5432'
environment:
- POSTGRES_HOST_AUTH_METHOD=trust
mysql:
image: 'mysql:5'
container_name: 'objection_mysql'
ports:
- '3306:3306'
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD=yesRead that as a test fixture rather than a deployment template. `fsync=off`, `synchronous_commit=off` and `full_page_writes=off` are there to make tests fast, `random_page_cost=1.0` tells the planner to stop preferring sequential scans, and `POSTGRES_HOST_AUTH_METHOD=trust` removes password handling entirely. The `mysql:5` tag is also a snapshot of a decision made years ago; a library advertising MySQL support in 2026 while its test compose file pins version 5 is a small inconsistency worth noting when you judge how current the testing is.
Versioning, releases, and where the changelog actually is
The package version in `package.json` is 3.1.5, with `main` at `lib/objection.js`, `types` at `./typings/objection/index.d.ts`, and engines requiring Node `>=14.0.0`. The published `files` list is deliberately narrow: `README.md`, `LICENSE`, `lib/*` and `typings/*`. No test utilities, no examples, no docs, so the installed package stays small.
The version scheme deserves attention because objection has been through breaking changes. The README links a v1 to v2 to v3 migration guide, which is the sort of thing a project only needs when it has actually broken its users twice. If you are evaluating this fresh, read that migration guide before reading anything else, because it tells you what the project considers unstable.
The odd part is that there are no GitHub releases at all for this repository. The releases list is empty. The changelog is instead published as a website page under the release notes URL, alongside the migration guide, and the API reference and guides sit under the objection.js documentation site. So the answer to "what changed in 3.1.5" is on a documentation site, not in the repository.
That arrangement is defensible: VuePress documentation is versioned and rendered better than release notes on a repository page. It is also easy to trip over, because the muscle memory from every other library sends you to the releases tab and finds nothing. Other useful entry points listed in the README are the who-uses-it discussion, the API reference, the example projects, the contribution guide and the plugins guide.
The repository itself is compact: `lib/`, `typings/`, `doc/`, `tests/`, `testUtils/`, `examples/`, a `reproduction-template.js` for bug reports, a `setup-test-db.js`, and `publish-docs.sh`. That last detail, a reproduction template in the root, tells you what kind of issues the maintainers find easiest to act on.
Choosing between this and knex directly
Since objection.js is a layer over knex, the honest question is whether you need the layer at all.
Choose plain knex when your data model is genuinely flat, when you write SQL by hand anyway, or when you want zero opinions about model definitions. knex gives you a query builder and migrations, which is most of what a small application needs, and it means one fewer abstraction between your code and the database.
Choose objection.js when relations are load-bearing. Eager loading that avoids N+1 queries, graph inserts that get foreign key ordering right, and upserts across related tables are the cases where the layer pays for itself. The declarative model and relation definitions also mean the shape of your schema is expressed once and reused, which matters in a codebase with more than a handful of entities.
The cost of the layer is that queries become method chains rather than SQL strings, and a bug in a relation definition produces a subtly wrong query rather than an exception. That is a real trade, and it is why objection's `raw` escape hatch exists and why it is documented as a first-class API rather than a fallback.
Two other signals from the repository are worth weighing. The project is hosted under the Vincit GitHub organisation but the author, documentation domain and repository URL all live under `koskimas`, a long-standing pattern from the maintainer's own hosting. And the pinned `mysql:5` test image suggests the compatibility testing is thorough for the databases it covers and less current than the headline. Both are fine; neither is a reason to avoid the library.
Editorial conclusion
Objection.js is a good fit for a team that wants relation handling and eager loading without giving up control of the SQL, and a poor fit for one that wants entities, a persistence layer, or automatic schema management. The README is unusually honest about that, and the three things it declines to do are the three people migrate away over, so read the list before you adopt it rather than after. Two operational notes. Version 3.1.5 requires Node 14 or later, and there are no GitHub releases to consult, so the changelog lives on the website at the release notes URL rather than on the releases page, which is easy to miss when you go looking for what changed. And the `docker-compose.yml` at the repository root, which spins up Postgres and MySQL for the test suite, pins `mysql:5` and unsets `fsync`, `synchronous_commit` and `full_page_writes` on Postgres, because it exists to run tests fast and should never be the basis for anything you care about. If you want the shortest path to a working setup, clone the `examples/koa` project as the README suggests and edit from there.
Frequently asked questions
What is Objection.js?
Objection.js is an ORM for Node.js, though the README argues that a more accurate description is a relational query builder. It is built on knex, so every database knex supports is supported here, and SQLite3, Postgres and MySQL are thoroughly tested. You get declarative models and relations, eager loading, graph inserts and upserts, transactions, official TypeScript support and optional JSON schema validation.
Is knex an ORM?
Not by its own account. Objection.js describes knex as an SQL query builder, which is the accurate framing: knex constructs SQL through a chainable API and provides a migration tool, but it has no notion of models or relations. Objection.js is the layer that adds those, sitting on top of knex rather than replacing it.
Does Objection.js create or migrate my database schema?
No, and that is deliberate. The README says automatic schema generation from model definitions is convenient for simple cases but usually gets in the way for anything non-trivial, so schema work is left to you and knex's migration tool is recommended. The `examples/koa-ts` project is the reference implementation to copy from.
Which version of Objection.js should I use, and how do I find the changelog?
The current package version is 3.1.5 and it requires Node 14 or later. There are no GitHub releases for this repository, so the changelog is published as a website page under the release notes URL rather than on the releases tab. Because the project has had breaking v1 to v2 and v2 to v3 transitions, read the migration guide before adopting it.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/vincit-objection-js)Star history chart for your README
[](https://hysenlabs.com/projects/vincit-objection-js)