Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/feathersjs-ecosystem/feathers-mongoose
Easily create a Mongoose Service for Feathersjs.
https://github.com/feathersjs-ecosystem/feathers-mongoose
Last synced: 3 months ago
JSON representation
Easily create a Mongoose Service for Feathersjs.
- Host: GitHub
- URL: https://github.com/feathersjs-ecosystem/feathers-mongoose
- Owner: feathersjs-ecosystem
- License: mit
- Created: 2014-01-18T23:48:08.000Z (almost 11 years ago)
- Default Branch: master
- Last Pushed: 2023-12-01T01:00:21.000Z (12 months ago)
- Last Synced: 2024-07-23T13:21:09.121Z (4 months ago)
- Language: JavaScript
- Homepage:
- Size: 2.14 MB
- Stars: 189
- Watchers: 13
- Forks: 96
- Open Issues: 32
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: .github/contributing.md
- License: LICENSE
Awesome Lists containing this project
- awesome-feathersjs - feathers-mongoose - feathers-logo.png) (Plugins / Database)
README
# feathers-mongoose
[![CI](https://github.com/feathersjs-ecosystem/feathers-mongoose/workflows/CI/badge.svg)](https://github.com/feathersjs-ecosystem/feathers-mongoose/actions?query=workflow%3ACI)
[![Dependency Status](https://img.shields.io/david/feathersjs-ecosystem/feathers-mongoose.svg?style=flat-square)](https://david-dm.org/feathersjs-ecosystem/feathers-mongoose)
[![Download Status](https://img.shields.io/npm/dm/feathers-mongoose.svg?style=flat-square)](https://www.npmjs.com/package/feathers-mongoose)A [Feathers](https://feathersjs.com) database adapter for [Mongoose](http://mongoosejs.com/), an object modeling tool for [MongoDB](https://www.mongodb.org/).
```bash
$ npm install --save mongoose feathers-mongoose
```> __Important:__ `feathers-mongoose` implements the [Feathers Common database adapter API](https://docs.feathersjs.com/api/databases/common.html) and [querying syntax](https://docs.feathersjs.com/api/databases/querying.html).
> This adapter also requires a [running MongoDB](https://docs.mongodb.com/getting-started/shell/#) 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](http://mongoosejs.com/docs/guide.html) for more information on defining your model.
```js
const mongoose = require('mongoose');
const service = require('feathers-mongoose');// A module that exports your Mongoose model
const Model = require('./models/message');// Make Mongoose use the ES6 promise
mongoose.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 definition
- `lean` (*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](https://docs.feathersjs.com/api/events.html#custom-events) sent by this service
- `paginate` (*optional*) - A [pagination object](https://docs.feathersjs.com/api/databases/common.html#pagination) containing a `default` and `max` page size
- `whitelist` (*optional*) - A list of additional query parameters to allow (e..g `[ '$regex', '$populate' ]`)
- `multi` (*optional*) - Allow `create` with arrays and `update` and `remove` with `id` `null` to change multiple items. Can be `true` for 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 from `Model`.
- `useEstimatedDocumentCount` (*optional*, default: `false`) - Use `estimatedDocumentCount` instead (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 `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](https://docs.feathersjs.com/api/hooks.html) and use it as usual. See the [Mongoose Guide](http://mongoosejs.com/docs/guide.html) for more information on defining your model.
### params.mongoose
When making a [service method](https://docs.feathersjs.com/api/services.html) 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](https://docs.feathersjs.com/api/hooks.html):
```js
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 here
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.
Using the `writeResult` mongoose option will return the write result of a `patch` operation, including the _ids of all upserted or modified documents. This can be helpful alongside the `upsert` flag, for detecting whether the outcome was a find or insert operation. More on write results is available in the [Mongo documentation](https://docs.mongodb.com/manual/reference/method/db.collection.update/#writeresult)
```js
const data = { address: '123', identifier: 'my-identifier' }
const params = {
query: { address: '123' },
mongoose: { upsert: true, writeResult: 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 @feathersjs/socketio mongoose feathers-mongoose
```In `message-model.js`:
```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`:
```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;
// Connect to your MongoDB instance(s)
mongoose.connect('mongodb://localhost:27017/feathers');// Create an Express compatible Feathers application instance.
const app = express(feathers());// Turn on JSON parser for REST services
app.use(express.json());
// Turn on URL-encoded parser for REST services
app.use(express.urlencoded({extended: true}));
// Enable REST services
app.configure(express.rest());
// Enable Socket.io services
app.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 returned
paginate: {
default: 2,
max: 4
}
}));
app.use(express.errorHandler());// Create a dummy Message
app.service('messages').create({
text: 'Message created on server'
}).then(function(message) {
console.log('Created message', message);
});// Start the server.
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](http://localhost:3030/messages).
## Querying, Validation
Mongoose by default gives you the ability to add [validations at the model level](http://mongoosejs.com/docs/validation.html). Using an error handler like the one that [comes with Feathers](https://github.com/feathersjs/feathers-errors/blob/master/src/error-handler.js) your validation errors will be formatted nicely right out of the box!
For more information on querying and validation refer to the [Mongoose documentation](http://mongoosejs.com/docs/guide.html).
## $populate
For Mongoose, the special `$populate` query parameter can be used to allow [Mongoose query population](http://mongoosejs.com/docs/populate.html).
> **Important:** `$populate` has to be whitelisted explicitly since it can expose protected fields in sub-documents (like the user password) which have to be removed manually.
```js
const mongoose = require('feathers-mongoose');app.use('/posts', mongoose({
Model,
whitelist: [ '$populate' ]
});app.service('posts').find({
query: { $populate: 'user' }
});
```## Error handling
As of v7.3.0, the original Mongoose error can be retrieved on the server via:
```js
const { ERROR } = require('feathers-mongoose');try {
await app.service('posts').create({ value: 'invalid' });
} catch(error) {
// error is a FeathersError
// Safely retrieve the original Mongoose error
const mongooseError = error[ERROR];
}
```## Discriminators (Inheritance)
Instead of strict inheritance, Mongoose uses [discriminators](http://mongoosejs.com/docs/discriminators.html) 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:
```js
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');// Discriminator key, we'll use this later to refer to all text posts
var options = {
discriminatorKey: '_type'
};var TextPostSchema = new Schema({
text: { type: String, default: null }
}, options);// Note the use of `Post.discriminator` rather than `mongoose.discriminator`.
var TextPost = Post.discriminator('text', TextPostSchema);// Using the discriminators option, let feathers know about any inherited models you may have
// for that service
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:```js
{
_type: 'text'
}
```and Feathers will automatically swap in the correct model and execute the query it instead of its parent model.
## Collation Support
This adapter includes support for [collation and case insensitive indexes available in MongoDB v3.4](https://docs.mongodb.com/manual/release-notes/3.4/#collation-and-case-insensitive-indexes). Collation parameters may be passed using the special `collation` parameter to the `find()`, `remove()` and `patch()` methods.
### Example: Patch records with case-insensitive alphabetical ordering
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'`.
```js
const patch = { shouldStudyMore: true };
const query = { grade: { $gte: 'c' } };
const collation = { locale: 'en', strength: 1 };
students.patch(null, patch, { query, collation }).then( ... );
```### Example: Find records with a case-insensitive search
Similar to the above example, this would find students with a grade of `'c'` or greater, in a case-insensitive manner.
```js
const query = { grade: { $gte: 'c' } };
const collation = { locale: 'en', strength: 1 };
students.find({ query, collation }).then( ... );
```For more information on MongoDB's collation feature, visit the [collation reference page](https://docs.mongodb.com/manual/reference/collation/).
## Mongo-DB Transaction
This adapter includes support to enable database transaction to rollback the persisted records for any error occured for a api call. This requires [Mongo-DB v4.x](https://docs.mongodb.com/manual/) installed and [replica-set](https://linode.com/docs/databases/mongodb/create-a-mongodb-replica-set/#start-replication-and-add-members) enabled.
Start working with transaction enabled by adding the following lines in `app.hooks.js` or `.hooks.js`.
```js
const TransactionManager = require('feathers-mongoose').TransactionManager;
const isTransactionEnable = process.env.TRANSACTION_ENABLE || false;
const skipPath = ['login'];let moduleExports = {
before: {
all: [],
find: [],
get: [],
create: [
when(isTransactionEnable, async hook =>
TransactionManager.beginTransaction(hook, skipPath)
)
],
update: [
when(isTransactionEnable, async hook =>
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;
```## Query Modifiers
Sometimes it's important to use an unusual Mongoose Query method, like [specifying whether to read from a primary or secondary node,](https://mongoosejs.com/docs/api.html#query_Query-read) 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.
```js
// Specify a global query modifier when creating the service
app.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 basis
app.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 basis
app.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.
## Contributing
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 mongodb
```The command needs to stay open while running the tests with
```
npm test
```## License
[MIT](LICENSE)
## Authors
- [Feathers contributors](https://github.com/feathersjs-ecosystem/feathers-mongoose/graphs/contributors)