nedb-models

A simple and useful model wrapper for nedb (using nedb-promises).

Usage no npm install needed!

<script type="module">
  import nedbModels from 'https://cdn.skypack.dev/nedb-models';
</script>

README

nedb-models

nedb-models is a simple and extensible model wrapper for nedb using nedb-promises.

Check out the docs!

IMPORTANT

As of nedb-promises 5.0.0 nedb package has been replaced with a fork of the original package, @seald-io/nedb to solve some vulnerability issues originating from nedb!

Contents

Installation

npm install nedb-models

Usage

Basics

A model class has almost all methods nedb's Datastore does, like Model.find(), Model.findOne, etc.

Check out the docs to see all of them!

const { Model } = require('nedb-models')

class Book extends Model {
    static datastore() {
        return {
            filename: 'books.db'
        }
            
        // same as
        // return 'books.db'
    }
    
    async sell() {
        this.soldAt = Date.now()
        return await this.save()
    } 
}

Book.find()
// [ Book { ... }, Book { ... } ]

// find the first book
let book = Book.findOne()
// Book { ... }

// sell it
book.sell().then(book => {
    // Book { soldAt: XXXXXX, ... }
})

Extensions

nedb-models includes three extensions that you can use right out of the box:

To implement them in your model, all you have to do is:

const { Model, Timestamps, SoftDeletes, Encryption } = require('nedb-models')

class Book extends Model {
    ...
}

Book.use(SoftDeletes)
// or Book.use([Timestamps, SoftDeletes])
// or Book.use(Timestamps)
// or Book.use(Encryption)

// now that your model is using SoftDeletes, 
// you can do funky stuff like:
Book.findRemoved()
Book.forceRemove({ ... })
Book.restore({ ... })

Creating extensions

Let's take a look at how an extension is built:

class Timestamps extends Extension {
    apply() {
        let __class = this.__class

        /**
         * In this case we have to add the `timestampData: true`
         * property to the datastore options.
         *
         * Since the datastore options are returned by a static
         * method we need to overwrite this method while
         * accessing it.
         * This is where tha last parameter of (in this case) `setStatic`
         * comes in handy. If you set this to true, and the second parameter
         * is a function, the module will pass the original value
         * to your function and then override it with the returned value.
         */
        this.setStatic('datastore', datastore => {
            /**
             * Don't use arrow functions when overwriting methods (static or instance),
             * because arrow functions don't handle the context (`this`) well.
             */
            return function () {
                /**
                 * In the new function first we retrieve the original options.
                 * We do that inside the new function to keep the possibility
                 * of dynamic properties.
                 */
                let options = datastore.call(__class)

                /**
                 * We add the property while also checking
                 * the type of the original options.
                 */
                if ( ! options) {
                    options = { timestampData: true }
                } else if ('string' === typeof options) {
                    options = {
                        filename: options,
                        timestampData: true
                    }
                } else if ('object' === typeof options) {
                    options = augmenter(options)({
                        timestampData: true
                    })
                }

                return options
            }
        }, true)

        /**
         * Finally we return true to show that
         * applying the extension was successful.
         */
        return true
    }
}

Check out the docs!

What's new

2.2.0

What's coming

  • Tests (for SoftRemoves and Encryption)