This package helps you to validate how you mutate your Vuex store. You can say that it is a Validator for the mutations. You can easily validate the payload for each of your mutations, so that you can be sure of the integrity of your Store Data.
This package also has support for any custom schema validator you may choose. You can create your own implementation and extend this package to use that schema.
Though Vuex allows you to set the store state directly without calling any mutation. This package won't validate anything outside the mutations.
// NPM:
$ npm install --save vuex-store-validator
// Yarn:
$ yarn add vuex-store-validator
By default it comes with Joi
validator. It also has support for Superstruct
and Yup
validation engine. Read their respective docs to find out which one is best for you.
It did come with Ajv
validation engine on ^1.0
but for the sake of bundle size it is removed from the bundle, but no need to worry, you can still make use of it by providing custom validator to the package.
If you want to use joi then you'll have to install it as your project dependency.
// NPM:
$ npm install --save joi
// Yarn:
$ yarn add joi
If you want to use superstruct then you'll have to install it as your project dependency.
// NPM:
$ npm install --save superstruct
// Yarn:
$ yarn add superstruct
If you want to use yup then you'll have to install it as your project dependency.
// NPM:
$ npm install --save yup
// Yarn:
$ yarn add yup
If you want to use ajv then you'll have to install it as your project dependency.
// NPM:
$ npm install --save ajv
// Yarn:
$ yarn add ajv
After that, see how to setup AJV
with this package here: Custom Validator Usage
- Add the validator to the plugins section of your Root Store.
// store.js
import VuexStoreValidator from 'vuex-store-validator';
export default new Vuex.Store({
...
plugins: [new VuexStoreValidator()],
});
- Add Rules to your store and respective modules
// store.js
import VuexStoreValidator, { ENGINE } from 'vuex-store-validator';
export default new Vuex.Store({
rules: {
SET_USER: ...SCHEMA HERE...
},
state: {user: null},
mutations: {
SET_USER(state, user) {
state.user = user;
}
},
plugins: [new VuexStoreValidator()],
});
// Joi Schema...
import Joi from 'joi';
SET_USER: Joi.object({
name: Joi.string().required(),
age: Joi.number().required(),
}).required(),
// Superstruct Schema...
import {object, string, number} from 'superstruct';
SET_USER: object({
name: string(),
age: number(),
}),
// Yup Schema...
import {object, string, number} from 'yup';
SET_USER: object().shape({
name: string().required(),
age: number().required(),
}).required(),
NOTE: If you want to use anything other than Joi then, you will have to add the engine option in the plugin registration
// Superstruct...
plugins: [new VuexStoreValidator({engine: ENGINE.SUPERSTRUCT})],
// Yup...
plugins: [new VuexStoreValidator({engine: ENGINE.YUP})],
Now whenever you call the mutation from anywhere be it inside an action or from any component. The payload you pass will be validated against the schema you've defined.
For the above piece of code if you try and call the SET_USER
mutation without valid data:
// From a Component
this.$store.commit('SET_USER', {name: 'John'});
// Or from an action
login({commit}, user) {
commit('SET_USER', {name: 'John'});
}
// Result:
// ValidationError: "email" field is required for mutation: SET_USER
This will work for all the nested modules as well. Just remember to add a new rules
option to your module definition with state, action etc.
Let's face it, the real world projects are complex. It's not as straight forward as defining a schema. Sometimes you need to have conditional schema which depends on some other state property or the mutation payload itself.
But don't worry, you can even define the schema in a closure. You have 2 parameters available in that closure. The store and the mutation payload itself.
Example:
rules: {
SET_USER(store, data) {
// Store -> Global Store instance
// Data -> {name: 'John'}
return Joi.object({
...
});
}
}
That's not a problem at all. Your project will work as is. This package will only validate the ones for which you have specified a schema. Unless you are using this package in Strict Mode
.
// store.js
import VuexStoreValidator from 'vuex-store-validator';
export default new Vuex.Store({
...
plugins: [new VuexStoreValidator({strict: true})],
});
If you set strict mode to true
, then if you don't have a schema defined for any of your mutation, it will throw an exception.
import Ajv from 'ajv';
// Define an async validator...
/**
* It accepts the schema and the data payload
* It should throw an error if your validation fails OR
* The return value should be the error string or null
*/
const ajvEngine = async (schema, data) => {
const ajv = new Ajv({ jsonPointers: true, $data: true });
const validate = ajv.compile(schema || {});
await validate(data);
if (validate.errors) {
throw new Error(validate.errors.map((item) => item.message).join(', '));
}
};
// Now extend with the custom validator...
plugins: [
new VuexStoreValidator({
engine: 'ajv',
extend: {
ajv: ajvEngine,
},
})
]
// Make sure that the engine name and the extend key are the same
// Now your schema would look something like this:
rules: {
MUTATION_NAME: {
type: 'object',
properties: {
name: {
type: 'string',
},
age: {
type: 'number',
},
},
required: ['name', 'age'],
},
}
You can also leverage Vue PropType Validator and make your own implementation of that. Maybe even React PropType validator. Or anything you like.
This package won't prevent you from setting invalid data to your store. But it will throw appropriate exception every time you set invalid data, so that you can be aware of where bad data might be coming from.
You can see and debug your code based on the Production Logs related to any bad data. If you are using Ignition or Sentry.
After Cloning the repository, install all npm dependencies by running: npm install
.
Then Run Tests:
$ npm run test
Please see CHANGELOG for more information on what has changed recently.
Please feel free to contribute ideas and PRs are most welcome.
The MIT License (MIT). Please see License File for more information.