Noodles Practicing Medicine
    Share your code. npm Orgs help your team discover, share, and reuse code. Create a free org »


    NPM Module Build Status

    Tiller - MongoDB ODM for TypeScript

    Tiller is an ODM for MongoDB, written in TypeScript with an ActiveRecord-inspired API.

    Capabilities of Tiller include:

    • Modeling entire typed hierarchies through sub-documents or references
    • Ability to include serializers/deserializers to modify the structure of a JSON document in the database
    • Modern async/await API to evade callback hell
    • Add indexes right in the class definition
    • Extensible validation layer


    $ npm install tiller --save
    $ tsd install mongodb --save

    Make sure that you compile to ES6 JavaScript.

    Getting Started

        import 'source-map-support/register'
        import {DB, Document, collection, Collection, document, embed} from 'tiller';
        export class Cannon {
            shoot() {
        export class SpaceShip extends Collection {
            constructor(name?:string) {
       = name;
            fly() {
        (async () => {
            await DB.connect('testdb')
            let ship = new SpaceShip('SpaceShip1')
            ship.cannons = [new Cannon()];
            // Save to the database 
            // Load from the database 
            ship = await SpaceShip.findOne<SpaceShip>({name: 'SpaceShip1'})
  ;                 // flyiiiinng 
            ship.cannons[0].shoot();    // wumm 
            await DB.disconnect(true)
        })().catch((e) => {


    Creating Collections

    Making a JS class being stored as MongoDB collection is as easy as adding the @collection() decorator and inheriting from Collection:

    export class MyModel extends Collection {

    Optionall you can pass a parameter to the decorator that names the collection:


    The API to interact with the database is heavily inspired by ActiveRecord:

    let objs:Promise<Array<MyModel>> = MyModel.find<MyModel>({key: ...})
    // or using await:
    let objs:Array<MyModel> = await MyModel.find<MyModel>({key: ...})
    let objs:MyModel = await MyModel.findOne<MyModel>({key: ...})
    let objs:MyModel = await MyModel.get<MyModel>(idValue)

    Adding indexes

    Indexes can easily be added using the @index decorator. They are also supported on subdocuments:

    class MyModel extends Collection {
        @index({unique: true})

    Indexes spanning multiple fields are currently not supported.

    Collection instance states & lifecycle hooks

    Using the functions isNew() and isSaved() you can find out whether an instance of Collection has been saved in the database already:

    let obj = new MyModel()
    assert(!obj.isSaved() && obj.isNew())
    assert(obj.isSaved() && !obj.isNew())

    To perform pre- or post-save actions you can override the Collection methods beforeSave() and afterSave().

    Using non-ObjectId _ids

    You can use non-ObjectId types for _id by redefining _id:

    export class MyModel extends Collection {

    Saving Objects


    Regardless of whether an instance is a new completely new instance, was loaded from a database with (find() or similar) or an upsert should be performed - the #save() method will do:

    // Saves a new instance
    var obj1 = new MyModel()
    // Updates an instance
    var obj1 = MyModel.get<MyModel>(...)
    // Upsert an instance
    var obj1 = new MyModel()
    obj1._id =, true)

    Saving Referenced Objects


    Finding Objects


    Using Collection#get() you can fetch a single object using its _id value. You have to use ObjectID if you use standard _id values:

    obj = await MyModel.get<MyModel>(new ObjectID("56f90cf44c57a9c97f1ac295"))
    // or using numeric _id's
    obj = await ModelWithStandardId.get<ModelWithStandardId>(123)


    The static method #find(selector:any, limit?:number, sort?:any) will return an array of objects that match selector. Optionally you can specify a limit (or set this parameter to null) and a sort order.

    // Finding all objects with key == 'value'
    objs = await MyModel.find<MyModel>({key: 'value'})
    // Limiting the returned objects to 3
    objs = await MyModel.find<MyModel>({key: 'value'}, 3)
    // Sorting the returned objects to id, not limiting the returned objects
    objs = await MyModel.find<MyModel>({key: 'value'}, null, {id: 1})

    Of course #find() doesn't just return Objects. It rebuilds entire object hierarchies, and sets the correct object prototypes.


    Collection#findOne(selector) is similar to Collection#find(), but it returns only the first found document.

    Deleting Objects


    An object can be destroyed using the #destroy() method:

    await obj.destroy();

    Validating Objects

    Tiller also contains a basic, but extensible, validation layer. Use the @validate decorator to add a schema to your model:

    export class House extends Collection {
       @validate({required: true})
       @validate({type: ['red', 'white']})
    house = new House();
    house.isValid() // false = 'My House';
    house.isValid() // true
    house.color = 'brown';
    house.isValid() // false
    house.validate() // {color: ...}
    house.color = 'red';
    house.isValid() // true
    house.validate() // {}

    Refer to js-schema for details about supported types.

    Working with Plain old JavaScript Objects

    To recreate a typed object hierarchy from JSON/JavaScript Objects Collection.create() can be used. It will also recreate embedded and referenced documents.

    let myModel = MyCollection.create<MyCollection>({foo: 'bar', child: {a: 'b'}})


    • DIRTY Tracking to improve save speed
    • Remove problems with two documents/collections named equally
    • Implement batch operations
    • Add Continous Integration build
    • Complete Readme: References, Embedded Documents
    • Keep upward references in @document
    • Implement $lookup aggregation for fast loading of references
    • Implement support for aggregation queries
    • Support Model-level hooks, to support external service-based model validation, i.e. MyModel.addHook('afterSave', ...)
    • Add chai plugins: expect(myModel)
    • Use mongodb connection string to connect
    • Subclassing
    • Support aggregation
    • Audits (async, bg)
    • Inverse Reference


    • Arrays of Referenced Objects are supported, but not arrays of arrays of referenced objects


    • "Required" validation of references, when we don't deep-save


    npm i tiller

    Downloadslast 7 days







    last publish


    • avatar
    • avatar