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

mers

v0.14.1

Published

*_Mongoose *_Express *_Rest *_Service

Downloads

79

Readme

#Mers *_Mongoose *_Express *_Rest *_Service

Mers is a plugin for express to expose mongoose finders as simple crud/rest operations.  The
basic idea being you should just define your model/finders and the rest should be be magic.

build status

Usage

Install mers, mongoose, express and body-parser

  $ npm install express --save
  $ npm install mongoose --save
  $ npm install body-parser --save
  $ npm install mers --save
    //You really need body parser for things to work correctly
     var express = require('express'),
        mongoose = require('mongoose'),
        Schema = mongoose.Schema,
        bodyParser = require('body-parser')

    app.use(bodyParser.json())
    app.use(bodyParser.urlencoded({ extended: true }));
    var SampleSchema = new Schema({
        name:String,
        age:Number
    });

    mongoose.model('sample', SampleSchema);
    var mers = require('mers');
    app.use('/rest', mers({uri:'mongodb://localhost/your_db'}).rest());

Configuration options include:

  • uri:uri://mongoose (as shown above)
  • mongoose:{mongoose} (your mongoose instance)
  • error:{function} (your custom Error Handler)
  • responseStream:{function} (your custom respost stream. See: lib/streams.js)
  • transformer:{function} (your custom transformer factory)

inject:{Nojector} (custom nojector add resovlers, or whatever)

###If you had a schema such as

var mongoose = require('mongoose'), Schema = mongoose.Schema,
 ObjectId = mongoose.Schema.ObjectId;

var CommentSchema = new Schema({
 title:String, body:String, date:Date
});


var BlogPostSchema = new Schema({
 author:ObjectId,
 title:String,
 body:String,
 buf:Buffer,
 date:Date,
 comments:[CommentSchema],
 meta:{
     votes:Number, favs:Number
 }
});
/**
* Note this must return a query object.   If it doesn't well, I dunno what it'll do.
* @param q
* @param term
*/
BlogPostSchema.statics.findTitleLike = function findTitleLike(q, term) {
 return this.find({'title':new RegExp(q.title || term.shift() || '', 'i')});
}
var Comment = module.exports.Comment = mongoose.model('Comment', CommentSchema);
var BlogPost = module.exports.BlogPost = mongoose.model('BlogPost', BlogPostSchema);

you could then access it at listing.

http://localhost:3000/rest/blogpost/
http://localhost:3000/rest/blogpost/$id
http://localhost:3000/rest/blogpost/$id/comments
http://localhost:3000/rest/blogpost/$id/comments/$id
http://localhost:3000/rest/blogpost/$id/comments/0
http://localhost:3000/rest/blogpost/finder/findTitleLike/term

###Pagination Pagination is also supported via skip= and limit= query params.

http://localhost:3000/rest/blogpost/$id?skip=10&limit=10

###Population Mongoose populate is supported, but this will be changing shortly to allow for more fine grained controll over population. Currently you can do

http://localhost:3000/rest/blogpost?populate=comments

or to specify particular fields.

http://localhost:3000/rest/blogpost?skip=10&populate[comments]=title,date

###Filter Filtering is available for strings. To find all the blog posts with C in the title.

http://localhost:3000/rest/blogpost?filter[title]=C

Also you can and or nor the filters by using + (and) - (nor) or nothing or http://localhost:3000/rest/blogpost?filter[-title]=C http://localhost:3000/rest/blogpost?filter[+title]=C&filter[-body]=A

To filter all String fields that have a C in them

http://localhost:3000/rest/blogpost?filter=C

###Sorting Sorting is supported 1 ascending -1 ascending.

http://localhost:3000/rest/blogpost?sort=title:1,date:-1

###Transformer Transformers can be registered on startup. A simple TransformerFactory is included. If the function returns a promise, it will resolve the transformer asynchronously. The transformers follow the same injection rules.

To transform asynchronously just return a promise from your function. You can chain transformers. Transformers can also inject, but the first argument should be the object you want to transform.


app.use('/rest', require('mers').rest({
    mongoose:mongoose,
    transformers:{
           renameid: function(obj){
                obj.id = obj._id;
                delete obj._id;
                //don't forget to return the object.  Null will filter it from the results.
                return obj;
           },
           /**
            Injects the user into the function, and checks if the
            owner is the same as the current user.  Works with passport.
           */
           checkUser:function(obj, session$user){
              if (obj.owner_id !== session$user._id){
                //returning null, short circuits the other transformers. And will
                //not be included in the response.
                return null;
              }else{
               return obj;
              }

           },
           /**
             Uses injection and async resolution.
           */
           async:function(obj, query$doIt){
             if (query$doIt){
                var p = promise();
                setTimeout(function(){
                    obj.doneIt =true;
                    //Mpromise resolve.  Should work with other promises, or any object with a then function.
                    p.resolve(null, obj);
                },50);
                return p;
             }else{
             return obj;
             }

           }
      }
    }));
}

to get results transformered just add

 http://localhost:3000/rest/blogpost?transform=renameid

It handles get/put/post/delete I'll add some docs on that some day, but pretty much as you expect, or I expect anyways. see tests/routes-mocha.js for examples.

###Static Finders It should also be able to be used with Class finders. Now handles class finders. Note: They must return a query object. They are passed the query object and the rest of the url. All of the populate's, filters, transforms should work.


/**
 * Note this must return a query object.
 * @param q
 * @param term
 */
BlogPostSchema.statics.findTitleLike = function findTitleLike(q, term) {
    return this.find({'title':new RegExp(q.title || term.shift(), 'i')});
}

So you can get the url

http://localhost:3000/rest/blogpost/finder/findTitleLike?title=term

or

http://localhost:3000/rest/blogpost/finder/findTitleLike/term

Promises with finders

Occassionally you may want to do something like a double query within a finder. Mers has got your back.

      BlogPostSchema.statics.findByCallback = function onFindByCallback(query$id) {
          return this.find({_id: query$id}).exec();
      }

Error Handling

To create a custom error handler


   app.use('/rest', rest({
         error : function(err, req, res, next){
               res.send({
                   status:1,
                   error:err && err.message
               });
           }).rest());

Custom ResultStream

You can create your own result stream. It needs to subclass Stream and be writable. This can allow for other formats, and preventing the wrapping of data in the payload.

##Method You can invoke a method on a model. This useful to expose more complicated things that can't just be filtered. Of course you can return nested nestings too...

###Returning an Object This one just returns an object, from /department/$id/hello/name

DepartmentSchema.methods.hello = function DepartmentSchema$hello(){
    return {name:'hello '+this.name};
}

###Returning a Promise. This is returns a promise from /department/$id/promises. Really you just need to return an object with an then function. So any promise library should work.

DepartmentSchema.methods.promises = function (data){
    var p = promise();
    setTimeout(p.resolve.bind(p, null, {name:'hello '+this.name}), 100);
    return p;
}

Returning a Query object.

This is returns a query from /department/$id/superDo

DepartmentSchema.methods.superDo = function DepartmentSchema$hello(data){
   return Department.find({
       _id:this._id
   });
}

##Examples. An example of a customized rest service can be found at

https://github.com/jspears/backbone-directory

##Parameter injection When invoking a method you often need data from the request to process. To do this we have an injection system. You can inject a method on a model, or a transformer.

It resolves the prefix of the parameter name deliminated by $ to the scope. See nojector for more information there. The built in resolvers are session, param, query, body, args, require

url: http://localhost/rest/department/finders/byName?name=Stuff
DepartmentSchema.static.byName = function DepartmentSchema$hello(query$name){
   return Department.find({
        name:query$name
       });
}

works on instances to...

url: http://localhost/rest/department/$id/hello/?name=STuff
DepartmentSchema.method.hello = function DepartmentSchema$hello(query$name, session$user){
    //session.user === session$user
   return Department.find({
        name:query$name
       });
       
}

Delete

Deleting is follows the rules of as a put, however, it has an option, of deleteRef, when you are deleteing a nested ref'd object and want to delete it from the refer'd collection. see routes-user-mocha.js