- Notifications
You must be signed in to change notification settings - Fork96
Easily create a Mongoose Service for Feathersjs.
License
feathersjs-ecosystem/feathers-mongoose
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
AFeathers database adapter forMongoose, an object modeling tool forMongoDB.
$ npm install --save mongoose feathers-mongoose
Important:
feathers-mongooseimplements theFeathers Common database adapter API andquerying syntax.
This adapter also requires arunning MongoDB database server.
Returns a new service instance initialized with the given options.Model has to be a Mongoose model. See theMongoose Guide for more information on defining your model.
constmongoose=require('mongoose');constservice=require('feathers-mongoose');// A module that exports your Mongoose modelconstModel=require('./models/message');// Make Mongoose use the ES6 promisemongoose.Promise=global.Promise;// Connect to a local database called `feathers`mongoose.connect('mongodb://localhost:27017/feathers');app.use('/messages',service({ Model}));app.use('/messages',service({ Model, lean, id, events, paginate}));
Options:
Model(required) - The Mongoose model definitionlean(optional, default:true) - Runs queries faster by returning plain objects instead of Mongoose models.id(optional, default:'_id') - The name of the id field property.events(optional) - A list ofcustom service events sent by this servicepaginate(optional) - Apagination object containing adefaultandmaxpage sizewhitelist(optional) - A list of additional query parameters to allow (e..g[ '$regex', '$populate' ])multi(optional) - Allowcreatewith arrays andupdateandremovewithidnullto change multiple items. Can betruefor all methods or an array of allowed methods (e.g.[ 'remove', 'create' ])overwrite(optional, default:true) - Overwrite the document when update, making mongoose detect is new document and trigger default value for unspecified properties in mongoose schema.discriminators(optional) - A list of mongoose models that inherit fromModel.useEstimatedDocumentCount(optional, default:false) - UseestimatedDocumentCountinstead (usually not necessary)queryModifier(optional) - A function that takes in the raw mongoose Query object and params, which modifies all find and get requests unless overridden. (see Query Modifiers below)queryModifierKey(optional, default:'queryModifier') - The key to use to get the override query modifier function from the params. (see Query Modifiers below)
Important: To avoid odd error handling behaviour, always set
mongoose.Promise = global.Promise. If not available already, Feathers comes with a polyfill for native Promises.
Important: When setting
leantofalse, Mongoose models will be returned which can not be modified unless they are converted to a regular JavaScript object viatoObject.
Note: You can get access to the Mongoose model via
this.Modelinside ahook and use it as usual. See theMongoose Guide for more information on defining your model.
When making aservice method call,params can contain amongoose property which allows you to modify the options used to run the Mongoose query. Normally, this will be set in a beforehook:
app.service('messages').hooks({before:{patch(context){// Set some additional Mongoose options// The adapter tries to use these settings by defaults// but they can always be changed herecontext.params.mongoose={runValidators:true,setDefaultsOnInsert:true}}}});
Themongoose property is also useful for performing upserts on apatch request. "Upserts" do an update if a matching record is found, or insert a record if there's no existing match. The following example will create a document that matches thedata, or if there's already a record that matches theparams.query, that record will be updated.
Using thewriteResult mongoose option will return the write result of apatch operation, including the _ids of all upserted or modified documents. This can be helpful alongside theupsert flag, for detecting whether the outcome was a find or insert operation. More on write results is available in theMongo documentation
constdata={address:'123',identifier:'my-identifier'}constparams={query:{address:'123'},mongoose:{upsert:true,writeResult:true}}app.service('address-meta').patch(null,data,params)
Here's a complete example of a Feathers server with amessages Mongoose service.
$ npm install @feathersjs/feathers @feathersjs/errors @feathersjs/express @feathersjs/socketio mongoose feathers-mongooseInmessage-model.js:
constmongoose=require('mongoose');constSchema=mongoose.Schema;constMessageSchema=newSchema({text:{type:String,required:true}});constModel=mongoose.model('Message',MessageSchema);module.exports=Model;
Then inapp.js:
constfeathers=require('@feathersjs/feathers');constexpress=require('@feathersjs/express');constsocketio=require('@feathersjs/socketio');constmongoose=require('mongoose');constservice=require('feathers-mongoose');constModel=require('./message-model');mongoose.Promise=global.Promise;// Connect to your MongoDB instance(s)mongoose.connect('mongodb://localhost:27017/feathers');// Create an Express compatible Feathers application instance.constapp=express(feathers());// Turn on JSON parser for REST servicesapp.use(express.json());// Turn on URL-encoded parser for REST servicesapp.use(express.urlencoded({extended:true}));// Enable REST servicesapp.configure(express.rest());// Enable Socket.io servicesapp.configure(socketio());// Connect to the db, create and register a Feathers service.app.use('/messages',service({ Model,lean:true,// set to false if you want Mongoose documents returnedpaginate:{default:2,max:4}}));app.use(express.errorHandler());// Create a dummy Messageapp.service('messages').create({text:'Message created on server'}).then(function(message){console.log('Created message',message);});// Start the server.constport=3030;app.listen(port,()=>{console.log(`Feathers server listening on port${port}`);});
You can run this example by usingnode app and go tolocalhost:3030/messages.
Mongoose by default gives you the ability to addvalidations at the model level. Using an error handler like the one thatcomes with Feathers your validation errors will be formatted nicely right out of the box!
For more information on querying and validation refer to theMongoose documentation.
For Mongoose, the special$populate query parameter can be used to allowMongoose query population.
Important:
$populatehas to be whitelisted explicitly since it can expose protected fields in sub-documents (like the user password) which have to be removed manually.
constmongoose=require('feathers-mongoose');app.use('/posts',mongoose({ Model,whitelist:['$populate']});app.service('posts').find({query:{$populate:'user'}});
As of v7.3.0, the original Mongoose error can be retrieved on the server via:
const{ERROR}=require('feathers-mongoose');try{awaitapp.service('posts').create({value:'invalid'});}catch(error){// error is a FeathersError// Safely retrieve the original Mongoose errorconstmongooseError=error[ERROR];}
Instead of strict inheritance, Mongoose usesdiscriminators as their schema inheritance model.To use them, pass in adiscriminatorKey option to your schema object and useModel.discriminator('modelName', schema) instead ofmongoose.model()
Feathers comes with full support for mongoose discriminators, allowing for automatic fetching of inherited types. A typical use case might look like:
varmongoose=require('mongoose');varSchema=mongoose.Schema;varPost=require('./post');varfeathers=require('@feathersjs/feathers');varapp=feathers();varservice=require('feathers-mongoose');// Discriminator key, we'll use this later to refer to all text postsvaroptions={discriminatorKey:'_type'};varTextPostSchema=newSchema({text:{type:String,default:null}},options);// Note the use of `Post.discriminator` rather than `mongoose.discriminator`.varTextPost=Post.discriminator('text',TextPostSchema);// Using the discriminators option, let feathers know about any inherited models you may have// for that serviceapp.use('/posts',service({Model:Post,discriminators:[TextPost]}))
Without support for discriminators, when you perform a.get on the posts service, you'd only get backPost models, notTextPost models.Now in your query, you can specify a value for your discriminatorKey:
{_type:'text'}
and Feathers will automatically swap in the correct model and execute the query it instead of its parent model.
This adapter includes support forcollation and case insensitive indexes available in MongoDB v3.4. Collation parameters may be passed using the specialcollation parameter to thefind(),remove() andpatch() methods.
The example below would patch all student records with grades of'c' or'C' and above (a natural language ordering). Without collations this would not be as simple, since the comparison{ $gt: 'c' } would not include uppercase grades of'C' because the code point of'C' is less than that of'c'.
constpatch={shouldStudyMore:true};constquery={grade:{$gte:'c'}};constcollation={locale:'en',strength:1};students.patch(null,patch,{ query, collation}).then( ...);
Similar to the above example, this would find students with a grade of'c' or greater, in a case-insensitive manner.
constquery={grade:{$gte:'c'}};constcollation={locale:'en',strength:1};students.find({ query, collation}).then( ...);
For more information on MongoDB's collation feature, visit thecollation reference page.
This adapter includes support to enable database transaction to rollback the persisted records for any error occured for a api call. This requiresMongo-DB v4.x installed andreplica-set enabled.
Start working with transaction enabled by adding the following lines inapp.hooks.js or<any-service>.hooks.js.
constTransactionManager=require('feathers-mongoose').TransactionManager;constisTransactionEnable=process.env.TRANSACTION_ENABLE||false;constskipPath=['login'];letmoduleExports={before:{all:[],find:[],get:[],create:[when(isTransactionEnable,asynchook=>TransactionManager.beginTransaction(hook,skipPath))],update:[when(isTransactionEnable,asynchook=>TransactionManager.beginTransaction(hook,skipPath))],patch:[],remove:[]},after:{all:[],find:[],get:[],create:[when(isTransactionEnable,TransactionManager.commitTransaction)],update:[when(isTransactionEnable,TransactionManager.commitTransaction)],patch:[],remove:[]},error:{all:[],find:[],get:[],create:[when(isTransactionEnable,TransactionManager.rollbackTransaction)],update:[when(isTransactionEnable,TransactionManager.rollbackTransaction)],patch:[],remove:[]}};module.exports=moduleExports;
Sometimes it's important to use an unusual Mongoose Query method, likespecifying whether to read from a primary or secondary node, but maybe only for certain requests.
You can access the internal Mongoose Query object used for a find/get request by specifying the queryModifier function. It is also possible to override that global function by specifying the function in a requests params.
// Specify a global query modifier when creating the serviceapp.use('/messages',service({ Model,queryModifier:(query,params)=>{query.read('secondaryPreferred');}}));app.service('messages').find({query:{ ...},}).then((result)=>{console.log('Result from secondary:',result)});// Override the modifier on a per-request basisapp.service('messages').find({query:{ ...},queryModifier:(query,params)=>{query.read('primaryPreferred');}}).then((result)=>{console.log('Result from primary:',result)});// Disable the global modifier on a per-request basisapp.service('messages').find({query:{ ...},queryModifier:false}).then((result)=>{console.log('Result from default option:',result)});
Note: Due to replication lag, a secondary node can have "stale" data. You should ensure that this "staleness" will not be an issue for your application before reading from the secondary set.
This module is community maintained and open for pull requests. Features and bug fixes should contain
- The bug fix / feature code
- Tests to reproduce the bug or test the feature
- Documentation updates (if necessary)
To contribute, fork and clone the repository. To run the tests, a MongoDB v4.0.0 server is required. If you do not have a MongoDB server running you can start one with:
npm run mongodbThe command needs to stay open while running the tests with
npm testAbout
Easily create a Mongoose Service for Feathersjs.
Resources
License
Contributing
Uh oh!
There was an error while loading.Please reload this page.