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 🙏

© 2024 – Pkg Stats / Ryan Hefner

ts-mongo-orm

v0.1.0

Published

A MongoDB ORM inspired by ActiveRecord.

Downloads

29

Readme

ts-mongo-orm

ts-mongo-orm is a MongoDB ORM inspired by ActiveRecord.

It's still under active development as I use it to build some apps, so until you see the 1.0.0 release, expect things to change or be missing.

In its current state, it has a fairly complete type-safe wrapper around MongoDB, and I'm already using it productively in deployed apps. See the "Future Work" section below for what's on the roadmap.

Installation

npm install --save ts-mongo-orm

Note that the following peer dependencies are required:

  • @hapi/joi and @types/hapi__joi for validation types.
  • mongodb and @types/mongodb for connecting to and using MongoDB.

Example Usage

See the ts-mongo-orm-example repo for an example program you can clone and run.

Below are some selected examples.

Connect to the database:

import {DatabaseConnectDefault} from "ts-mongo-orm";

(async () => {
    await DatabaseConnectDefault("mongodb://localhost:27017/test");
})();

Create define a User ActiveRecord:

import {ActiveRecord, Document, Field, ObjectIdField} from "ts-mongo-orm";
import {ObjectId} from "mongodb";
import * as Joi from "@hapi/joi";

@Document
class User {
  static databaseName = "test";
  static collectionName = "users";

  @ObjectIdField()
  _id: ObjectId;

  @Field(Joi.string())
  email: string;

  @Field(Joi.string())
  name: string;

  @Field(Joi.string())
  passwordHash: string;

  @Field(Joi.string())
  passwordSalt: string;

  @Field(Joi.date())
  createdAt: Date;

  constructor() {
    this._id = new ObjectId("000000000000000000000000");
    this.email = "";
    this.name = "";
    this.passwordHash = "";
    this.passwordSalt = "";
    this.createdAt = new Date();
  }
}

export const UserActiveRecord = ActiveRecord.for(User, User);

Note that the ActiveRecord class has a separation between the document class (which describes purely the data that will be stored in the database the and validations for that data), and the model class (which could define additional logic, helpers, computed fields, etc.)

In the above example, there is no additional model logic, so the User class is passed for both arguments to ActiveRecord.for. Defining additional model logic would might look like this:

class UserModel extends User {
  // This will appear on the active record object but will not get saved to the database:
  get nameAndEmail() {
    return this.name + " " + this.email;
  }
}

export const UserActiveRecord = ActiveRecord.for(User, UserModel);

Using the UserActiveRecord might look like this:

import {UserActiveRecord} from "./user";

export async function userFindAndLog(email: string) {
    // This query is fully type-checked:
    let user = await UserActiveRecord.findOne({email: email});
    
    if (user) {
        console.log(user.nameAndEmail);
    }
}

export async function userDeleteIfFound(email: string) {
    // This query is fully type-checked:
    let user = await UserActiveRecord.findOne({email: email});

    if (user) {
        console.log("Deleting " + user._id + "...");
        // Since user is an ActiveRecord instance, we can simply call delete()
        // on it to remove it from the DB:
        await user.delete();
    }
}

Goals

  • Be able to use the ActiveRecord pattern to find and update documents, similar to Ruby on Rails.
  • Make it easy to validate objects going into and coming out of the database.
  • Make it easy to define those validations by using decorators to annotate the necessary validations alongside the type definitions.
  • Provide a typed version of most (or all if possible) MongoDB APIs, specific to each type of document. For example, collection.update({}, {$set: {a: 1}}) should fail to typecheck if a is not a number or does not exist on the document type.
  • Minimal overhead. Getting the type-safety guarantees and validation should not come at a huge cost of speed, but a small cost is expected.
  • Optional:
    • If you need to bail out of the typings for whatever reason, that should be easy.
    • If you need to not use the ActiveRecord pattern, but instead update or query the database directly, that should be easy.

Non-Goals

This package will not do these things:

  • Have zero-overhead abstractions. Validation and the ActiveRecord pattern incur some cost by their very nature.
  • Provide a type-safe way of dealing with aggregations. Too much pain for too little gain.
  • Have exhaustive support for various versions of the mongodb library. Pull requests that add support for older versions are welcome, but I will focus only on the 4.x branch of the mongodb library.
  • Support for validation libraries other than joi. Pull requests making this optional are welcome.
  • Create typings for an existing DB automatically.

It's not that any of these things are bad, just that they are either outside the scope of this package, or require more time than I have.

Issues and Pull Requests

If you see an issue or a way to improve ts-mongo-orm feel free to open an issue or a pull request!

Please include as much as possible, and keep in mind that issues with associated pull requests are much more likely to get merged, as my time to manage issues on this project is limited.

Future Work

  • [ ] Updating to the v4.x driver broke some things. Might be worth rethinking how the superstructure of this module works so it's not as fragile to the whims of MongoDB. I'd rather expose the MongoDB API directly somehow, and just transparently provide my niceties on top.
  • [ ] A mechanism for doing type-safe updates and queries on nested fields (e.g. a type safe {$set: {"a.b.c": 123}})
  • [ ] Lifecycle hooks (e.g. OnLoad, AfterInit, BeforeUpdate, BeforeInsert, etc.)
  • [ ] Easy optional mechanism for running validations when reading from the database.

License

The license is MIT, see the LICENSE file for more details.