Create validated Meteor publications. Lightweight. Simple.
With this package you can define factory functions to create a variety of Meteor publications. Decouples definition from instantiation (also for the schema) and allows different configurations for different types of publications.
Minified size < 2KB!
- Decouple definition from instantiation
- Validate publication arguments as with
mdg:validated-method
- Just pass in the schema as plain object, instead of manually instantiating
SimpleSchema
- Create mixins (similar to
mdg:validated-method
) on the abstract factory level, on the factory level, or both (see mixins section) - Fail silently in case of errors (uses the publication's
error
andready
), undefined cursors or unexpected returntypes
Simply add this package to your meteor packages
$ meteor add leaonline:publication-factory
Import the createPublicationFactory
publication and create the factory function from it:
import { createPublicationFactory } from 'meteor/leaonline:publication-factory'
import { MyCollection } from '/path/to/MyCollection'
const createPublication = createPublicationFactory() // no params = use defaults
const fancyPublication = createPublication({ name: 'fancy', validate: () => {}, run: () => MyCollection.find() }) // minimal required
Under the hood it all runs like a
We support various ways to validate an input schema. To decouple schema definition from instantiation, we introduced a shemaFactory
, which
is basically a function that creates your schema for this collection. This also ensures, that
publications don't share the same schema instances.
import { createPublicationFactory } from 'meteor/leaonline:publication-factory'
import { MyCollection } from '/path/to/MyCollection'
import SimpleSchema from 'simpl-schema'
const schemaFactory = definitions => new SimpleSchema(definitions)
const createPublication = createPublicationFactory({ schemaFactory })
createPublication({
name: 'fancy',
schema: { author: String },
run: function({ author }) {
return MyCollection.find({ author })
}
})
and subcribe via
Meteor.subscribe('fancy', { author: 'Mr.x' }) // leaving author will raise an error
As you can see, there is no need to pass a validate
function as it is internally built using the schemaFactory
and the given schema
.
You can also override the internal validate
when using schema
by passing a validate
function.
This, however, disables the schema validation and is then your responsibility:
import { createPublicationFactory } from 'meteor/leaonline:publication-factory'
import { MyCollection } from '/path/to/MyCollection'
import SimpleSchema from 'simpl-schema'
const schemaFactory = definitions => new SimpleSchema(definitions)
const createPublication = createPublicationFactory({ schemaFactory })
createPublication({
name: 'fancy',
schema: { author: String },
validate: () => {},
run: function({ author }) {
return MyCollection.find({ author })
}
})
and subcribe via
Meteor.subscribe('fancy', {}) // leaving author will NOT raise an error
If none of these cover your use case, you can still use mixins.
You can also use Meteor's builtin check
and Match
for schema validation:
import { check } from 'meteor/check'
import { MyCollection } from '/path/to/MyCollection'
import { createPublicationFactory } from 'meteor/leaonline:publication-factory'
const schemaFactory = schema => ({
validate (args) {
check(args, schema)
}
})
const createPublication = createPublicationFactory({ schemaFactory })
createPublication({
name: 'fancy',
schema: { author: String },
run: function({ author }) {
return MyCollection.find({ author })
}
})
Note, that some definitions for SimpleSchema
and check
/Match
may differ.
There are three ways to define mixins:
- on the abstract factory function level, all publications created by the factory will contain these mixins
- on the factory level, you basically pass mixins the a single publication
- on both levels, where mixins from the abstract factory function are executed first; no overrides
If you want a certain mixin to be included for all publications created by the factory just pass them to the
createPublicationFactory
function:
import { createPublicationFactory } from 'meteor/leaonline:publication-factory'
import { ValidatedPublication } from 'meteor/mdg:validated-publication'
import { myDefaultMixin } from '/path/to/myDefaultMixin'
import { MyCollection } from '/path/to/MyCollection'
const createPublication = createPublicationFactory({ mixins: [myDefaultMixin] })
createPublication({
name: 'publicationWithMixin',
validate: () => {},
run: () => MyCollection.find(),
foo: 'bar' // assuming your mixin requires foo
})
You can also define mixins for each publication. This is the same as passing mixins to the ValidatedPublication
:
import { createPublicationFactory } from 'meteor/leaonline:publication-factory'
import { ValidatedPublication } from 'meteor/mdg:validated-publication'
import { myDefaultMixin } from '/path/to/myDefaultMixin'
import { MyCollection } from '/path/to/MyCollection'
const createPublication = createPublicationFactory() // use defaults
createPublication({
name: 'publicationWithMixin',
mixins: [myDefaultMixin],
validate: () => {},
run: () => MyCollection.find(),
foo: 'bar' // assuming your mixin requires foo
})
const publicationWithoutMixin = createPublication({
name: 'publicationWithoutMixin',
validate: () => {},
run: () => MyCollection.find(),
})
Of course you can define mixins on both levels, so that you have a certain set of default mixins and publication-specific mixins:
import { createPublicationFactory } from 'meteor/leaonline:publication-factory'
import { ValidatedPublication } from 'meteor/mdg:validated-publication'
import { myDefaultMixin } from '/path/to/myDefaultMixin'
import { someOtherMixin } from '/path/to/someOtherMixin'
import { MyCollection } from '/path/to/MyCollection'
const createPublication = createPublicationFactory({ mixins: [myDefaultMixin] })
const publicationWithMixin = createPublication({
name: 'publicationWithMixin',
validate: () => {},
run: () => MyCollection.find(),
foo: 'bar' // assuming your mixin requires foo
})
const publicationWithMixins = createPublication({
name: 'publicationWithMixin',
mixins: [someOtherMixin],
validate: () => {},
run: () => MyCollection.find(),
foo: 'bar', // assuming your mixin requires foo
bar: 'baz', // assuming the other mixin requires bar
})
With version 1.1.0 we introduced an onError
handler, that allows to log and transform errors.
Sometimes errors are not thrown as Meteor.Errors
and may be sent as ambiguous or confusing 500 messages
to the client.
Therefore it is possible to transform errors on both, abstract factory and publication, levels.
Add an onError
handler to your createPublicationFactory
invocation in order to handle any occurring error, including
validation errors and runtime errors:
const createPublication = createPublicationFactory({
onError: function(e) {
// if you want to log the error just pass it to your logger
myLogger.log(e)
// if you want to pass this error to the client
// and it's a Meteor.Error, just return it
if (e.errorType === 'Meteor.Error') {
return e
}
// if it is not a Meteor.Error but you still want to pass it
// (in this case we have a validation-error from SimpleSchema)
// just add the isClientSafe attribute to it
if (e.errorType === 'ClientError' && e.error === 'validation-error') {
e.isClientSafe = true
}
// if you like to transform if, feel free to do so
e.message = 'There was an error but we won\'t tell you about it'
// return the error on order to tell the publication to set it
// as error to be returned to client, however, you can also omit
// this and return nothing in order to let Meteor decide
return e
}
})
You can also handle errors differently for each publication you create. Also note, that like with mixins, the local definitions override the abstract factory level definitions.
const createPublication = createPublicationFactory({
onError: function(e) {
myLogger.logError(e)
}
})
const publicationWithErrorHandler = createPublication({
name: 'publicationWithMixin',
validate: () => {},
run: () => MyCollection.find(),
onError: function(e) {
// for this pub we log this error with a different priority
myLogger.logFatalError(e)
}
})
We use standard
as code style and for linting.
$ npm install --global standard snazzy
$ standard | snazzy
$ meteor npm install --global standard snazzy
$ standard | snazzy
We use meteortesting:mocha
to run our tests on the package.
$ TEST_WATCH=1 TEST_CLIENT=0 meteor test-packages ./ --driver-package meteortesting:mocha
MIT, see LICENSE