{"id":13652671,"url":"https://github.com/jspears/mers","last_synced_at":"2025-04-05T20:10:35.712Z","repository":{"id":2585354,"uuid":"3566618","full_name":"jspears/mers","owner":"jspears","description":"Mongoose Express Rest Service ","archived":false,"fork":false,"pushed_at":"2015-06-21T20:56:17.000Z","size":850,"stargazers_count":343,"open_issues_count":14,"forks_count":42,"subscribers_count":22,"default_branch":"master","last_synced_at":"2024-04-14T08:05:12.388Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jspears.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-02-28T00:43:56.000Z","updated_at":"2024-03-06T17:19:14.000Z","dependencies_parsed_at":"2022-08-06T12:30:29.623Z","dependency_job_id":null,"html_url":"https://github.com/jspears/mers","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jspears%2Fmers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jspears%2Fmers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jspears%2Fmers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jspears%2Fmers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jspears","download_url":"https://codeload.github.com/jspears/mers/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247393574,"owners_count":20931813,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T02:01:01.507Z","updated_at":"2025-04-05T20:10:35.676Z","avatar_url":"https://github.com/jspears.png","language":"JavaScript","readme":"#Mers\n *_Mongoose\n *_Express\n *_Rest\n *_Service\n \n    Mers is a plugin for express to expose mongoose finders as simple crud/rest operations.  The\n    basic idea being you should just define your model/finders and the rest should be be magic.\n\n![build status](https://travis-ci.org/jspears/mers.svg)\n\n## Usage\n\nInstall mers, mongoose, express and body-parser\n\n```sh\n  $ npm install express --save\n  $ npm install mongoose --save\n  $ npm install body-parser --save\n  $ npm install mers --save\n\n```\n\n```javascript\n    //You really need body parser for things to work correctly\n     var express = require('express'),\n        mongoose = require('mongoose'),\n        Schema = mongoose.Schema,\n        bodyParser = require('body-parser')\n\n    app.use(bodyParser.json())\n    app.use(bodyParser.urlencoded({ extended: true }));\n    var SampleSchema = new Schema({\n        name:String,\n        age:Number\n    });\n\n    mongoose.model('sample', SampleSchema);\n    var mers = require('mers');\n    app.use('/rest', mers({uri:'mongodb://localhost/your_db'}).rest());\n\n```\nConfiguration options include:\n* `uri:uri://mongoose`  (as shown above)\n* `mongoose:{mongoose}` (your mongoose instance)\n* `error:{function}` (your custom Error Handler)\n* `responseStream:{function}` (your custom respost stream. See: lib/streams.js)\n* `transformer:{function}` (your custom transformer factory)\n# `inject:{Nojector}` (custom nojector add resovlers, or whatever)\n\n###If you had a schema such as\n   ```javascript\nvar mongoose = require('mongoose'), Schema = mongoose.Schema,\n    ObjectId = mongoose.Schema.ObjectId;\n\nvar CommentSchema = new Schema({\n    title:String, body:String, date:Date\n});\n\n\nvar BlogPostSchema = new Schema({\n    author:ObjectId,\n    title:String,\n    body:String,\n    buf:Buffer,\n    date:Date,\n    comments:[CommentSchema],\n    meta:{\n        votes:Number, favs:Number\n    }\n});\n/**\n * Note this must return a query object.   If it doesn't well, I dunno what it'll do.\n * @param q\n * @param term\n */\nBlogPostSchema.statics.findTitleLike = function findTitleLike(q, term) {\n    return this.find({'title':new RegExp(q.title || term.shift() || '', 'i')});\n}\nvar Comment = module.exports.Comment = mongoose.model('Comment', CommentSchema);\nvar BlogPost = module.exports.BlogPost = mongoose.model('BlogPost', BlogPostSchema);\n```\n\nyou could then access it at\n    listing.\n    \n    http://localhost:3000/rest/blogpost/\n    http://localhost:3000/rest/blogpost/$id\n    http://localhost:3000/rest/blogpost/$id/comments\n    http://localhost:3000/rest/blogpost/$id/comments/$id\n    http://localhost:3000/rest/blogpost/$id/comments/0\n    http://localhost:3000/rest/blogpost/finder/findTitleLike/term\n    \n    \n###Pagination\nPagination is also supported via skip= and limit= query params.\n\n    http://localhost:3000/rest/blogpost/$id?skip=10\u0026limit=10\n\n###Population\nMongoose populate is supported, but this will be changing shortly to allow for more\nfine grained controll over population.  Currently you can do\n\n    http://localhost:3000/rest/blogpost?populate=comments\n\nor to specify particular fields.\n\n    http://localhost:3000/rest/blogpost?skip=10\u0026populate[comments]=title,date\n\n\n\n###Filter\nFiltering is available for strings. To find all the blog posts with C in the title.\n\n    http://localhost:3000/rest/blogpost?filter[title]=C\n\nAlso you can and or nor the filters by using + (and) - (nor)  or nothing or\n    http://localhost:3000/rest/blogpost?filter[-title]=C\n    http://localhost:3000/rest/blogpost?filter[+title]=C\u0026filter[-body]=A\n\n\n\nTo filter all String fields that have a C in them\n\n    http://localhost:3000/rest/blogpost?filter=C\n\n\n###Sorting\nSorting is supported 1 ascending -1 ascending.\n\n  http://localhost:3000/rest/blogpost?sort=title:1,date:-1\n\n###Transformer\nTransformers can be registered on startup.  A simple TransformerFactory is\nincluded.  If the function returns a promise, it will resolve the transformer\nasynchronously.   The transformers follow the same injection rules.\n\nTo transform asynchronously just return a promise from your function.  You can\nchain transformers.  Transformers can also inject, but the first argument should\nbe the object you want to transform.\n\n\n```javascript\n\napp.use('/rest', require('mers').rest({\n    mongoose:mongoose,\n    transformers:{\n           renameid: function(obj){\n                obj.id = obj._id;\n                delete obj._id;\n                //don't forget to return the object.  Null will filter it from the results.\n                return obj;\n           },\n           /**\n            Injects the user into the function, and checks if the\n            owner is the same as the current user.  Works with passport.\n           */\n           checkUser:function(obj, session$user){\n              if (obj.owner_id !== session$user._id){\n                //returning null, short circuits the other transformers. And will\n                //not be included in the response.\n                return null;\n              }else{\n               return obj;\n              }\n\n           },\n           /**\n             Uses injection and async resolution.\n           */\n           async:function(obj, query$doIt){\n             if (query$doIt){\n                var p = promise();\n                setTimeout(function(){\n                    obj.doneIt =true;\n                    //Mpromise resolve.  Should work with other promises, or any object with a then function.\n                    p.resolve(null, obj);\n                },50);\n                return p;\n             }else{\n             return obj;\n             }\n\n           }\n      }\n    }));\n}\n```\n\n\n\nto get results transformered just add\n\n     http://localhost:3000/rest/blogpost?transform=renameid\n\n\n\nIt handles  get/put/post/delete I'll add some docs on that some day, but pretty much as you expect, or I expect anyways.\nsee tests/routes-mocha.js for examples.\n\n###Static Finders\nIt should also be able to be used with Class finders. Now handles class finders. Note: They must return  a query object.\nThey are passed the query object and the rest of the url. All of the populate's, filters, transforms should work.\n\n```javascript\n\n/**\n * Note this must return a query object.\n * @param q\n * @param term\n */\nBlogPostSchema.statics.findTitleLike = function findTitleLike(q, term) {\n    return this.find({'title':new RegExp(q.title || term.shift(), 'i')});\n}\n```\n\nSo you can get the url\n\n\n```\nhttp://localhost:3000/rest/blogpost/finder/findTitleLike?title=term\n```\n\nor\n\n```\nhttp://localhost:3000/rest/blogpost/finder/findTitleLike/term\n```\n\n#### Promises with finders\nOccassionally you may want to do something like a double query within a finder.   Mers has got your back.\n\n```javascript\n      BlogPostSchema.statics.findByCallback = function onFindByCallback(query$id) {\n          return this.find({_id: query$id}).exec();\n      }\n\n\n```\n\n\n### Error Handling ###\nTo create a custom error handler\n\n```javascript\n\n   app.use('/rest', rest({\n         error : function(err, req, res, next){\n               res.send({\n                   status:1,\n                   error:err \u0026\u0026 err.message\n               });\n           }).rest());\n\n```\n\n\n### Custom ResultStream\nYou can create your own result stream. It needs to subclass Stream and be writable.  This can allow\nfor other formats, and preventing the wrapping of data in the payload.\n\n\n##Method\nYou can invoke a method on a model.  This useful to expose more complicated things\nthat can't just be filtered.   Of course you can return nested nestings too...\n\n\n###Returning an Object\nThis one just returns an object, from /department/$id/hello/name\n\n```javascript\nDepartmentSchema.methods.hello = function DepartmentSchema$hello(){\n    return {name:'hello '+this.name};\n}\n```\n\n###Returning a Promise.\nThis is returns a promise from /department/$id/promises.  Really you just\nneed to return an object with an then function.  So any promise library should work.\n\n```javascript\nDepartmentSchema.methods.promises = function (data){\n    var p = promise();\n    setTimeout(p.resolve.bind(p, null, {name:'hello '+this.name}), 100);\n    return p;\n}\n```\n\n### Returning a Query object.\nThis is returns a query from /department/$id/superDo\n\n```javascript\nDepartmentSchema.methods.superDo = function DepartmentSchema$hello(data){\n   return Department.find({\n       _id:this._id\n   });\n}\n```\n\n##Examples.\nAn example of a customized rest service can be found at\n\n    https://github.com/jspears/backbone-directory\n\n\n##Parameter injection\nWhen invoking a method you often need data from the request to process.  To do this\nwe have an injection system.   You can inject a method on a model, or a transformer.\n\nIt resolves the prefix of the parameter name deliminated by $ to the scope.  See\n nojector for more information there. The built in resolvers are\nsession,\nparam,\nquery,\nbody,\nargs,\nrequire\n\n```\nurl: http://localhost/rest/department/finders/byName?name=Stuff\n```\n\n\n```javascript\nDepartmentSchema.static.byName = function DepartmentSchema$hello(query$name){\n   return Department.find({\n        name:query$name\n       });\n}\n```\n\nworks on instances to...\n\n```\nurl: http://localhost/rest/department/$id/hello/?name=STuff\n```\n\n\n```javascript\nDepartmentSchema.method.hello = function DepartmentSchema$hello(query$name, session$user){\n    //session.user === session$user\n   return Department.find({\n        name:query$name\n       });\n       \n}\n```\n\n\n### Delete\nDeleting is follows the rules of as a put, however, it has an option, of deleteRef, when you\nare deleteing a nested ref'd object and want to delete it from the refer'd collection. see\nroutes-user-mocha.js\n\n\n\n","funding_links":[],"categories":["Servers"],"sub_categories":["Node.js"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjspears%2Fmers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjspears%2Fmers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjspears%2Fmers/lists"}