Which JavaScript ORM should you be using in 2018? An In-Depth Look

Object-Relational Mapping (ORM) libraries have become an essential part of the modern JavaScript ecosystem. By abstracting away the low-level details of interacting with databases, ORMs boost developer productivity and make applications much easier to maintain.

As a full-stack JavaScript developer, you have no shortage of ORM options to choose from these days. According to the State of JavaScript 2017 survey, nearly 60% of Node.js developers use an ORM. And that number keeps growing every year.

In this in-depth guide, we‘ll compare the most popular JavaScript ORMs and help you decide which one is the best fit for your project in 2018.

Why Have ORMs Become So Essential?

Before we jump into comparing libraries, let‘s zoom out and examine why ORMs have become such an important tool for JavaScript developers.

Imagine having to write manual SQL queries to perform every database operation in your code. Worse still, imagine having to write slightly different queries for each type of database. All of that low-level database interaction code would quickly clutter up your business logic, making your application harder to read and maintain.

An ORM solves these problems by abstracting away the database and providing a convenient, standardized interface for performing common operations like creating, querying, updating and deleting records. You write your code using JavaScript objects and the ORM library translates those into database commands under the hood.

This has a few key benefits:

  1. Productivity – ORMs dramatically reduce the amount of boilerplate code you need to write. Common tasks like defining schemas, setting up associations, and serializing/deserializing data are handled automatically.

  2. Maintainability – By centralizing all of your data access logic in one place, ORMs make your codebase much easier to reason about. You‘re not littering your routes and controllers with raw database operations.

  3. Reusability – Most ORMs provide a way to define your schemas and models separately from your application code. This makes it easy to share and reuse models across projects.

  4. Flexibility – Switching databases becomes much less painful when you‘re using an ORM. As long as your new database is supported, it‘s often just a simple config change.

In short, ORMs eliminate a lot of the tedious grunt work involved in working with databases. For large-scale JavaScript projects, they‘re becoming an increasingly essential tool.

The State of JavaScript ORMs in 2018

The JavaScript ecosystem never stands still for long. Even since last year, we‘ve seen a number of new ORM entrants as well as significant updates to existing libraries.

Here‘s a high-level overview of the most popular JavaScript ORMs as of 2018:

Library Database Support GitHub Stars Weekly Downloads Typescript Support
Sequelize PostgreSQL, MySQL, SQLite, MSSQL 17,600+ 373,983 Yes
TypeORM MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, WebSQL 10,200+ 189,052 Yes
Bookshelf PostgreSQL, MySQL, SQLite3 5,800+ 64,312 No
Objection.js PostgreSQL, MySQL, SQLite3, MSSQL 3,000+ 53,510 Yes
Mongoose MongoDB 18,100+ 570,421 Yes
Waterline MySQL, PostgreSQL, MongoDB, neDB, Redis 5,000+ 21,196 No
Loopback MySQL, PostgreSQL, Oracle, MongoDB, Cloudant 12,600+ 59,546 Yes

Sources: npmjs.com, GitHub, State of Javascript

Let‘s take a closer look at each of the top contenders.

Sequelize

Sequelize is a promise-based ORM for Node.js. It supports the dialects PostgreSQL, MySQL, SQLite and MSSQL and features solid transaction support, relations, read replication and more.

Here‘s a quick example of defining a Sequelize model:

const User = sequelize.define(‘user‘, {
  username: Sequelize.STRING,
  birthday: Sequelize.DATE
});

And here‘s how you‘d query that model:

User.findAll({
  where: {
    username: ‘john-doe‘
  }
}).then(users => {
  console.log(users)
})

Sequelize is one of the most widely used ORMs in the Node.js ecosystem. It has a large and active community with vibrant Slack and Gitter channels.

In addition to the core library, there‘s a healthy ecosystem of well-maintained add-ons and plugins for Sequelize that extend its functionality.

TypeORM

TypeORM is a more recent entrant that‘s rapidly gained mindshare. As the name suggests, it‘s built with TypeScript in mind. But you can just as easily use it with JavaScript.

One of TypeORM‘s key differentiators is that it supports both Active Record and Data Mapper patterns, whereas most other ORMs only support one or the other. It also has a powerful query builder and supports both NoSQL and relational databases.

Here‘s an example of defining an entity in TypeORM:

@Entity()
export class User {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    firstName: string;

    @Column()
    lastName: string;

    @Column()
    age: number;

}

To query the entity, you‘d write:

const repository = getRepository(User);
const users = await repository.find({ firstName: "John" });

For developers accustomed to object-oriented programming, the declarative syntax of TypeORM feels very natural. The documentation is also top-notch.

Bookshelf

Bookshelf is a JavaScript ORM built on top of the powerful Knex query builder. It‘s designed to work well with PostgreSQL, MySQL, and SQLite3.

Compared to something like Sequelize, Bookshelf is a more lightweight option. It provides a simple library for common tasks when querying databases, but leaves a lot of flexibility to the developer.

Here‘s how you‘d define a model with Bookshelf:

const User = bookshelf.model(‘User‘, {
  tableName: ‘users‘,
  posts() {
    return this.hasMany(Posts)
  }
})

And here‘s how you‘d query that model:

User.where(‘id‘, 1).fetch({withRelated: [‘posts‘]})
  .then(user => {
    console.log(user)
  })

If you‘re looking for a simple, no-frills ORM, Bookshelf is a solid option. It‘s not as widely used as something like Sequelize, but it has a loyal following.

Objection.js

Objection.js is another ORM built on top of Knex. It aims to provide a more full-featured ORM experience while still leveraging Knex‘s powerful query builder under the hood.

Here‘s an example of defining a model with Objection:

class Person extends Model {
  static get tableName() {
    return ‘persons‘;
  }

  static get relationMappings() {
    return {
      children: {
        relation: Model.HasManyRelation,
        modelClass: Person,
        join: {
          from: ‘persons.id‘,
          to: ‘persons.parentId‘
        }
      }
    };
  }
}

And here‘s how you‘d perform a query:

const adults = await Person.query()
  .where(‘age‘, ‘>‘, 18)
  .andWhere(‘age‘, ‘<‘, 65);

One of the standout features of Objection is its support for graph inserts. This allows you to perform complex, nested inserts that create related models all in a single query.

Mongoose

When it comes to MongoDB and Node.js, Mongoose is the go-to ORM. It provides a straightforward, schema-based solution to model your application data.

Here‘s an example Mongoose schema:

const userSchema = new Schema({
  name:  String,
  email: String,
  posts: [{ type: Schema.Types.ObjectId, ref: ‘Post‘ }]
});

And here‘s how you‘d query the model:

User.
  find({ name: /john/i }).
  where(‘email‘).equals(‘[email protected]‘).
  where(‘age‘).gt(17).lt(50).
  limit(5).
  sort(‘-name‘).
  select(‘name email‘).
  exec(callback);

One of the nice things about Mongoose is that it provides built-in type casting, validation, query building, and business logic hooks out of the box. The documentation is also excellent.

Waterline

Waterline is the default ORM used in the Sails.js framework, but it can also be used as a standalone module. It provides a uniform API for accessing a variety of different databases.

Here‘s an example Waterline model:

const User = Waterline.Collection.extend({
  identity: ‘user‘,
  datastore: ‘default‘,
  primaryKey: ‘id‘,

  attributes: {
    id: {
      type: ‘number‘,
      autoMigrations: {autoIncrement: true}
    },  

    firstName: {type:‘string‘},
    lastName: {type:‘string‘},

    // Add a reference to Pets
    pets: {
      collection: ‘pet‘,
      via: ‘owner‘
    }
  }
});

And here‘s how you‘d query the model:

User.find()
  .where({
    firstName: ‘john‘,
    age: {‘>‘: 18}
  })
  .populate(‘pets‘)
  .sort(‘lastName‘)
  .exec((err, users) => {
    // ...
  })

Loopback

Loopback is a highly-extensible, open-source Node.js framework based on Express. It allows you to quickly create dynamic end-to-end REST APIs and comes with a built-in ORM.

Here‘s an example Loopback model definition:

{
  "name": "user",
  "base": "User",  
  "idInjection": true,
  "properties": {
    "email": {
      "type": "string",
      "required": true
    },
    "password": {
      "type": "string",
      "required": true
    }
  },
  "validations": [],
  "relations": {
    "posts": {
      "type": "hasMany",
      "model": "Post",
      "foreignKey": ""
    }
  },
  "acls": [],
  "methods": {}
}

Performing a query is done through the find() method:

User.find(
  {where: {email: ‘[email protected]‘}}, 
  (err, results) => {
    // ...
  }
);

One of Loopback‘s standout features is its built-in API explorer. Once you‘ve defined your models and relationships, Loopback dynamically generates a Swagger UI that lets you inspect and test your API routes.

How to Choose the Right ORM For Your Project

As you can see, each of these ORMs has its own strengths and use cases. So how do you go about picking the right one for your project?

Here are some factors to keep in mind:

  1. Supported Databases – Perhaps the most important consideration is which databases the ORM supports. If you‘re locked into a specific database, then you‘ll need to choose an ORM that supports it.

  2. Learning Curve – Consider how much time you‘re willing to invest in learning a new library. Some ORMs are more complex and come with a steeper learning curve.

  3. Community Support – The size and activity level of the community around an ORM is a good indicator of whether you‘ll be able to get help when you need it. Look at factors like GitHub stars, Stack Overflow questions, and Gitter/Slack activity.

  4. Documentation – Good documentation is essential for getting up to speed quickly. Look for ORMs that have comprehensive, well-maintained docs.

  5. Performance – If you‘re building a high-traffic application, performance is going to be a key consideration. Some ORMs are optimized for read-heavy workloads while others are better for write-heavy ones.

  6. Project Size – Be realistic about the scope and complexity of your project. A lightweight ORM might be perfectly adequate for a small side project, but a more full-featured one will be necessary for an enterprise-scale application.

Ultimately, the best way to choose an ORM is to try a few different ones and see which one feels the most natural for your project. Don‘t be afraid to experiment and change course if you find that your initial choice isn‘t working out.

Final Thoughts

ORMs are a powerful tool that can save you a lot of time and headache when working with databases in your Node.js projects. Whether you opt for a full-featured library like Sequelize or a more lightweight one like Bookshelf, the important thing is that you take the time to learn your ORM of choice inside and out.

While powerful, ORMs are not a silver bullet. There will always be edge cases and performance-critical situations where it makes sense to bypass your ORM and write custom SQL queries.

As you evaluate the different options, remember that there‘s no one-size-fits-all solution. The right ORM for your project will depend on your specific requirements and constraints.

Whichever ORM you choose, make sure to take the time to read the documentation thoroughly, understand the key concepts, and look at sample projects before diving in. And most importantly, have fun and happy coding!

Similar Posts