I recently started using TypeORM in a project with MongoDB and thought I’d share my thoughts on it.

TypeORM is typically used with SQL databases, but also has basic MongoDB support. Before I deciding to use TypeORM I tried doing some research online, but not too much came up. Others mentioned they were using it and it worked well enough for them, but didn’t go into too much detail. Hopefully this post covers those missing details for you and all the pain points you’ll face.

A quick summary of TypeORM from the homepage for those that haven’t used it before:

TypeORM is an ORM that can run in NodeJS, Browser, Cordova, PhoneGap, Ionic, React Native, NativeScript, Expo, and Electron platforms and can be used with TypeScript and JavaScript (ES5, ES6, ES7, ES8). Its goal is to always support the latest JavaScript features and provide additional features that help you to develop any kind of application that uses databases — from small applications with a few tables to large scale enterprise applications with multiple databases. TypeORM supports both Active Record and Data Mapper patterns, unlike all other JavaScript ORMs currently in existence, which means you can write high quality, loosely coupled, scalable, maintainable applications the most productive way.

TypeORM is really popular for SQL, but the go to solution for Mongo is Mongoose (621,194 weekly npm downloads).

Mongoose does its job and is battle tested, but I have felt it to be a little cumbersome to use at times. For the project I’m working on it would have been more than fine. I wanted to see what else was out there.

One area where things could be nicer with Mongoose is when using it with TypeScript. If you want TS type support for your models, you need to define both the Mongo schema and the TypeScript types. I use GraphQL on almost all my projects too, which means having to define 3 different types for each collection.

One way to solve this problem with Mongoose is using a library called Typegoose which automatically generates TS types for you from your Mongoose models. I haven’t used this library so can’t comment on it, but it looks promising.

Another solution is to use TypeORM which will also auto generate the TS types for you.

There are unfortunately many basic features missing from TypeORM for MongoDB and this is the list of items I wish I had read before getting started with it:

Projection Support

MongoDB TypeORM does not support projections. There is an open pull request for it however:

Default Values

Default values don’t work. You’ll have to handle all values yourself when creating each object. Automatically adding createdAt and updatedAt fields is possible however with:

@CreateDateColumn({ type: 'timestamp' }) createdAt: Date @UpdateDateColumn({ type: 'timestamp', nullable: true }) updatedAt?: Date

Lack of Documentation

This page will help you out a bit:

But beyond that there’s not much out there. You can Google around for issues or read the source code to figure stuff out.

TypeORM was not built for MongoDB

This may be pointing out the obvious, but TypeORM’s focus is heavily on SQL. The first response in the PR I linked to above for example:

We can’t go with such changes. If we keep going with mongodb-specific changes entity-manager/repository is going to be a mess for rdbms and we’ll always be blocked on some absolute incompatible features. I suggest to remove dependency of MongoEntityManager from EntityManager (e.g. remove extends) and completely re-implement MongoEntityManager with mongodb-specific features.

Nothing against the response and this approach may well make the most sense, but the simple point that the current state of TypeORM is not set up for MongoDB should be clear. It may be more MongoDB friendly in a year from now, but isn’t today.

Validation is broken

TS will give you an error if you try to save an object of the wrong shape to the database, but if you decide to add an object of type any , the TS errors go away and you can add an item of any shape. So one piece of advice is to avoid any here, but with a library like Mongoose, the data will be checked to be valid at runtime. This is also a problem if your project is in plain JavaScript.

You may need to convert to ObjectID format

Some methods support both String and ObjectID out the box (and will convert to the correct format if not already in it. For example, findOne works fine whether you pass it a string id or as an ObjectID object. findOneAndUpdate will not do this for you automatically however. A quick tip for getting around this problem is to use this helper function:

import { ObjectID } from 'mongodb' export const toObjectId = (value: string | ObjectID): ObjectID => { return typeof value === 'string' ? new ObjectID(value) : value }

It would be nice if this was supported out the box though.

Other Random Bugs