npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mithrandirii/type-mongodb

v2.0.0-beta.9

Published

A simple decorator based MongoDB ODM.

Readme

type-mongodb makes it easy to map classes to MongoDB documents and back using @decorators.

Features

  • Extremely simply @Decorator() based document mapping
  • Very fast 🚀! (thanks to JIT compilation)
  • RAW. MongoDB is already extremely easy to use. It's best to use the driver as it's intended. No validation, no change-set tracking, no magic -- just class mapping
  • Custom Repositories
  • Event Subscribers
  • Transaction Support
  • Discriminator Mapping
  • & more!

How to use

type-orm allows you to create a base document class for common functionality. Notice that we don't enforce strict types. MongoDB is "schema-less", so we've decided to just support their main types and not do anything fancy. Again, we wanted to keep it as close to the core driver as possible.

import { Id, Field } from 'type-mongodb';
import { ObjectId } from 'mongodb';

abstract class BaseDocument {
  @Id()
  _id: ObjectId;

  get id(): string {
    return this._id.toHexString();
  }

  @Field()
  createdAt: Date = new Date();

  @Field()
  updatedAt: Date = new Date();
}

Now create our document class with some fields.

import { Document, Field } from 'type-mongodb';
import { BaseDocument, Address, Pet } from './models';

@Document()
class User extends BaseDocument {
  @Field()
  name: string;

  @Field(() => Address)
  address: Address; // single embedded document

  @Field(() => [Address])
  addresses: Address[] = []; // array of embedded documents

  @Field(() => [Pet])
  pets: Pet[] = []; // array of discriminator mapped documents

  @Field(() => [Pet])
  favoritePet: Pet = []; // single discriminator mapped document
}

And here's the embedded Address document.

import { Field } from 'type-mongodb';

class Address {
  @Field()
  city: string;

  @Field()
  state: string;
}

type-mongodb also has support for discriminator mapping (polymorphism). You do this by creating a base class mapped by @Discriminator({ property: '...' }) with a @Field() with the name of the "property". Then decorate discriminator types with @Discriminator({ value: '...' }) and type-mongodb takes care of the rest.

import { Discriminator, Field } from 'type-mongodb';

@Discriminator({ property: 'type' })
abstract class Pet {
  @Field()
  abstract type: string;

  @Field()
  abstract sound: string;

  speak(): string {
    return this.sound;
  }
}

@Discriminator({ value: 'dog' })
class Dog extends Pet {
  type: string = 'dog';
  sound: string = 'ruff';

  // dog specific fields & methods
}

@Discriminator({ value: 'cat' })
class Cat extends Pet {
  type: string = 'cat';
  sound: string = 'meow';

  // cat specific fields & methods
}

And now, lets see the magic!

import { DocumentManager } from 'type-mongodb';
import { User } from './models';

async () => {
  const dm = await DocumentManager.create({
    connection: {
      uri: process.env.MONGO_URI,
      database: process.env.MONGO_DB
    },
    documents: [User]
  });

  const repository = dm.getRepository(User);

  await repository.create({
    name: 'John Doe',
    address: {
      city: 'San Diego',
      state: 'CA'
    },
    addresses: [
      {
        city: 'San Diego',
        state: 'CA'
      }
    ],
    pets: [{ type: 'dog', sound: 'ruff' }],
    favoritePet: { type: 'dog', sound: 'ruff' }
  });

  const users = await repository.find().toArray();
};

What about custom repositories? Well, that's easy too:

import { Repository } from 'type-mongodb';
import { User } from './models';

export class UserRepository extends Repository<User> {
  async findJohnDoe(): Promise<User> {
    return this.findOneOrFail({ name: 'John Doe' });
  }
}

Then register this repository with the User class:

import { UserRepository } from './repositories';
// ...

@Document({ repository: () => UserRepository })
class User extends BaseDocument {
  // ...
}

... and finally, to use:

const repository = dm.getRepository<UserRepository>(User);

What about custom IDs? You can either create your own type that extends Type, or use our built-ins:

import { Id, Field, UUIDType } from 'type-mongodb';

@Document()
class User {
  @Id({ type: UUIDType })
  _id: string;

  // fields can also be a "UUID" type.
  @Field({
    type: UUIDType /* create: true (pass this to auto-generate the uuid, otherwise, omit) */
  })
  uuid: string;
}

What about events? We want the base class to have createdAt and updatedAt be mapped correctly.

import {
  EventSubscriber,
  DocumentManager,
  InsertEvent,
  UpdateEvent
} from 'type-mongodb';
import { BaseDocument } from './models';

export class TimestampableSubscriber implements EventSubscriber<BaseDocument> {
  // Find all documents that extend BaseDocument
  getSubscribedDocuments?(dm: DocumentManager): any[] {
    return dm
      .filterMetadata(
        (meta) => meta.DocumentClass.prototype instanceof BaseDocument
      )
      .map((meta) => meta.DocumentClass);
  }

  beforeInsert(e: InsertEvent<BaseDocument>) {
    if (!e.model.updatedAt) {
      e.model.updatedAt = new Date();
    }

    if (!e.model.createdAt) {
      e.model.createdAt = new Date();
    }
  }

  beforeUpdate(e: UpdateEvent<BaseDocument>) {
    this.prepareUpdate(e);
  }

  beforeUpdateMany(e: UpdateEvent<BaseDocument>) {
    this.prepareUpdate(e);
  }

  prepareUpdate(e: UpdateEvent<BaseDocument>) {
    e.update.$set = {
      updatedAt: new Date(),
      ...(e.update.$set || {})
    };

    e.update.$setOnInsert = {
      createdAt: new Date(),
      ...(e.update.$setOnInsert || {})
    };
  }
}

...then register TimestampableSubscriber:

const dm = await DocumentManager.create({
  /// ...,
  subscribers: [TimestampableSubscriber]
});

Other Common Features

// custom collection and database
@Document({ database: 'app', collection: 'users' })

// using internal hydration methods
dm.toDB(User, user);
dm.fromDB(User, { /* document class */ });
dm.init(User, { /* user props */ });
dm.merge(User, user, { /* user props */ });

For more advanced usage and examples, check out the tests.