feathers-versions v0.2.1
Feathers Versions
ALPHA VERSION DOCUMENTATION
If you're reading this, feathers-versions is in alpha, and not all of the functionality is fully described or finalized.
Why?
- You're using feathers.js serverside, and you'd like to save versions of service documents.
- You love your family.
QuickStart
The following assumes you're familiar with feathers.js workflow. If you've never heard of feathers.js before, it's great. Learn it: feathers.js
Install
npm install feathers-versions
Set Up Versions Service
import feathers from 'feathers'
import hooks from 'feathers-hooks'
import versions from 'feathers-versions'
//set up a quick app that will allow you to create documents server side
const app = feathers()
.configure(hooks())
//calling versions without any arguments gives the default configuration
.configure(versions())Set up a Service that will use Versioning
Once the version service has been set up, you need to add a couple
of hooks to any service that you want to use versions.
The addVersion hook can only be placed as after patch, create and update hooks, and typically you'd want them on all three.
The clearVersions can only be placed as a after remove hook.
import { addVersion, clearVersions } from 'feathers-hooks'
import memory from 'feathers-memory'
const articles = app
//for the sake of example, we'll set up an in memory service
.use('articles', memory())
.service('articles')
//configure your add and clear hooks
const add = addVersion({
excludeMask: ['id'] //we don't need to save the id for every version.
})
const clear = clearVersions()
//apply the hooks to the articles service.
articles.hooks({
after: {
//we'll want to add a version whenever a document is patched, created or updated.
patch: add,
create: add,
update: add,
//and we'll want to remove versions when a document is deleted.
remove: clear
}
})Create a Document
That's it! Now, when you create or edit article documents, they'll have versions saved on the version service.
articles.create({ body: 'I hate sandwhiches!', author: 'James McSoup' })
.then( article => {
const query = { document: article.id, service: 'articles' }
//get the version for it
return app.service('versions').find({ query })
})
.then( versions => {
const version = versions[0]
console.log(version)
//A version document in this case would have the following structure:
{
id: 0, // id of version document
document: 0, // id of article document
list: //each of the saved versions, from earliest to latest
[{
saved: Date, //when the version was saved
user: null, //if there was a user entity associated with the call, this would be that users id
data: { body: 'I love sandwhiches!', author: 'James McSoup'} // the actual data of the saved version
}]
}
})Service Configuration
The service configuration comes with a couple of options, most importantly the adapter field:
idType and adapter
If you don't provide a a database adapter, feathers-versions will use feathers-memory by default, which probably won't be very useful.
idType is the constructor for the dataType that your id will be in. Number by default. This is important to set if you are using service adapters that don't user strings or numbers as ids, like mongodb.
To set the versions service to use mongodb:
import feathers from 'feathers'
import MongoService from 'feathers-mongodb'
import { ObjectId, MongoClient } from 'mongodb'
import hooks from 'feathers-hooks'
import versions from 'feathers-versions'
import MONGO_DB_URL from './mongo-db-url'
//once again, create an app that will allow you to create documents server-side
const app = feathers()
.configure(hooks())
MongoClient.connect(MONGO_DB_URL)
.then(db => {
//create your mongo adapter
const adapter = new MongoService({
Model: db.collection('versions')
})
//configure your versions service. It's important to set idType to ObjectId, here
app.configure(versions({
idType: ObjectId,
adapter
}))
})serviceName
By default, the service name for the versions service will simply be versions. If you'd like it to be something else, set this option:
import feathers from 'feathers'
import hooks from 'feathers-hooks'
import versions from 'feathers-versions'
const app = feathers()
.configure(hooks())
.configure(versions({
serviceName: 'history'
}))
//ta daa
const historyService = app.service('history')userEntityField and userIdField
These fields are used for getting a authenticated user object. If a user patches
an document that uses versioning, feathers-versions will save that users id with
the version data.
By default userEntityField is user and userIdField is _id
addVersion Hook Configuration
There are a couple of options when adding version hooks to documents:
limit
A limit to how many versions can be stored. If the limit is reached, the oldest versions will be deleted to make room for new ones. Default is 1000.
saveInterval
If set, saveInteval should be a number in milliseconds. New versions added in
less than the set number of milliseconds will be collapsed together. This is so that
if multiple changes are made frequently, they'll be considered one version. By default,
a version will be created with every patch or update.
excludeMask and includeMask
You should set EITHER excludeMask or includeMask, not both, and they
should be an array of strings, representing field names.
These fields will mask the data that gets saved to a version, so that redundant fields are ignored.
clearVersions Hook Configuration
The clearVersions hook currently receives no configuration.
getVersion helper method.
feathers-versions also exports a helper method called getVersion
It simplifies getting the version data for a specific document.
import { getVersion } from 'feathers-versions'
import app from './app' // assume we have a properly set up app here
void async function test () {
const articles = app.service('articles')
const doc = await articles.create({ body: 'Informed opinion.', author: 'Some Guy' })
let docVersions
docVersions = await getVersion(app, 'articles', doc.id)
// OR, getVersion can also be bound to app for readability
docVersions = await app::getVersion('articles', doc.id)
console.log(docVersions) /*
{
id: 0,
document: 0,
service: 'articles',
list: [{
user: null,
updated: [Date],
data: { body: 'Informed opinion.', author: 'Some Guy' }
}]
}
*/
}Further Considerations
- New versions will not be created if none of the masked data has been changed.