@teacode/feathers-mongoose-teacode
A Feathers database adapter for Mongoose, an object modeling tool for MongoDB.
Important: It's forked from [feather-mongoose] (https://github.com/feathersjs-ecosystem/feathers-mongoose) changing code to trigger pre save and pre remove hooks in mongoose.
$ npm install --save mongoose @teacode/feathers-mongoose-teacode
Important: feathers-mongoose
implements the Feathers Common database adapter API and querying syntax.
This adapter also requires a running MongoDB database server.
API
service(options)
Returns a new service instance initialized with the given options. Model
has to be a Mongoose model. See the Mongoose Guide for more information on defining your model.
const mongoose = require('mongoose');
const service = require('feathers-mongoose');
const Model = require('./models/message');
mongoose.Promise = global.Promise;
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 of custom service events sent by this servicepaginate
(optional) - A pagination object containing a default
and max
page sizediscriminators
(optional) - A list of mongoose models that inherit from Model
.
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 lean
to false
, Mongoose models will be returned which can not be modified unless they are converted to a regular JavaScript object via toObject
.
Note: You can get access to the Mongoose model via this.Model
inside a hook and use it as usual. See the Mongoose Guide for more information on defining your model.
params.mongoose
When making a service method call, params
can contain a mongoose
property which allows you to modify the options used to run the Mongoose query. Normally, this will be set in a before hook:
app.service('messages').hooks({
before: {
patch(context) {
context.params.mongoose = {
runValidators: true,
setDefaultsOnInsert: true
}
}
}
});
The mongoose
property is also useful for performing upserts on a patch
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 the data
, or if there's already a record that matches the params.query
, that record will be updated.
const data = { address: '123', identifier: 'my-identifier' }
const params = {
query: { address: '123' },
mongoose: { upsert: true }
}
app.service('address-meta').patch(null, data, params)
Example
Here's a complete example of a Feathers server with a messages
Mongoose service.
$ npm install @feathersjs/feathers @feathersjs/errors @feathersjs/express mongoose feathers-mongoose
In message-model.js
:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const MessageSchema = new Schema({
text: {
type: String,
required: true
}
});
const Model = mongoose.model('Message', MessageSchema);
module.exports = Model;
Then in app.js
:
const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
const socketio = require('@feathersjs/socketio');
const mongoose = require('mongoose');
const service = require('feathers-mongoose');
const Model = require('./message-model');
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost:27017/feathers');
const app = express(feathers());
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.configure(express.rest());
app.configure(socketio());
app.use('/messages', service({
Model,
lean: true,
paginate: {
default: 2,
max: 4
}
}));
app.use(express.errorHandler());
app.service('messages').create({
text: 'Message created on server'
}).then(function(message) {
console.log('Created message', message);
});
const port = 3030;
app.listen(port, () => {
console.log(`Feathers server listening on port ${port}`);
});
You can run this example by using node app
and go to localhost:3030/messages.
Querying, Validation
Mongoose by default gives you the ability to add validations at the model level. Using an error handler like the one that comes with Feathers your validation errors will be formatted nicely right out of the box!
For more information on querying and validation refer to the Mongoose documentation.
$populate
For Mongoose, the special $populate
query parameter can be used to allow Mongoose query population.
app.service('posts').find({
query: { $populate: 'user' }
});
Discriminators (Inheritance)
Instead of strict inheritance, Mongoose uses discriminators as their schema inheritance model.
To use them, pass in a discriminatorKey
option to your schema object and use Model.discriminator('modelName', schema)
instead of mongoose.model()
Feathers comes with full support for mongoose discriminators, allowing for automatic fetching of inherited types. A typical use case might look like:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Post = require('./post');
var feathers = require('@feathersjs/feathers');
var app = feathers();
var service = require('feathers-mongoose');
var options = {
discriminatorKey: '_type'
};
var TextPostSchema = new Schema({
text: { type: String, default: null }
}, options);
var TextPost = Post.discriminator('text', TextPostSchema);
app.use('/posts', service({
Model: Post,
discriminators: [TextPost]
}))
Without support for discriminators, when you perform a .get
on the posts service, you'd only get back Post
models, not TextPost
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.
License
MIT
Authors