Straints
The JSON Object Validator
What is this?
Straints is JSON object validator that can be used both in node and in the browser. Validation constraints are declaratively defined in a single JSON or YAML file.
For example:
create_user: constrain: name: - { method: "isNull", flip: true } email: - { method: "isNull", flip: true } - { method: "isEmail" }
With the above (YAML) configuration, executing a Straints validation with a 'create_user' context will ensure that a JSON object has non-null name
and email
properties and that email
is in a valid format.
In essence, the goal of this project is to provide an easy way to maintain validations used across a web application in a single place.
Features
- Manage site-wide validations in a single place.
- Use the same validation configuration in node and in the browser
- Reusable constraint configurations
- Reusable validation contexts/schemas
- Validate nested objects or nested arrays of objects
NOTE
Validation tests are not in the scope of this project, and Straints uses validator.js as its validator by default. You can also configure an alternative validator (See Validators section).
Awesome. How do I get it?
npm
npm install straints
or, as a dependency in your project
npm install straints --save
bower
bower install straints
or, as a dependency in your project
bower install straints --save
What's the Setup?
For Node/Express
var straints = require('straints');
straints.configure({ ... });
app.use(straints.usage);
With no configuration options specified Straints will look for a validation.json file at the root of your project.
The app.use()
call simply adds the Straints instance to the request object. So, in your controllers you can do
reqstraints;
To get at the 'global' instance provided through straints.usage
, use
straints
You can also create a new Straints instance if you need to (and pass config options as necessary).
straints
For the Browser
You will find the js for the browser in the /dist folder.
Or, if you wish to use the default validator:
Then you can do
With no configuration options specified Straints will simply try to request /validation.json.
Note that The methods described in For Node/Express are available on StraintsFactory
.
How does all this work?
Configuration Options
Here are the config options listed with their defaults.
-
load
('[project-root|http-root]/validation.json')
Specify the validation configuration data here. Here are the options:- (string)
In a browser this is assumed to be a URL and an XHR GET is attempted. Otherwise, it will assume this is a filename. - (function)
Must be of the form
{ ; }You could also load a file like this
load: straintsor a URL like this
load: straints- (object)
The JSON VC data itself.
- (string)
-
datatype
('json')
The type of data being loaded, whenload
is a string or function ('yaml' or 'json') -
validator
(validator)
Specify the test method implementation to use here (See Validators section). -
useStrings
(true)
Set totrue
to have values converted to strings prior to validation (necessary for validator.js). -
log
(null)
There is some logging available. Specify the log function here.
Validation Configuration (VC)
Let's start with an example:
person: constrain: name: [ is.notNull ] email: [ is.notNull, isEmail ]basketball: player: include: person constrain: position: [ is.basketballPosition ] team: nested: coach: include: person players: nested: ____: include: basketball.player constrain: name: [ is.notNull ] coach: [ is.notNull ] players: [ is.notNull ]is: - { name: notNull, method: isNull, flip: true } - { name: basketballPosition, method: isIn, params: [ [ point, guard, forward, water ] ] }
Note that there are some 'reserved' words in the VC. These are constrain
, include
, nested
, and ____
(four underscores). More on these later. The sections below will refer to the example above.
Contexts
A 'context' generally defines how a JSON object will be validated. If a path level in the VC has a constrain
, include
, or nested
child, then it can be used as a context.
In the above our contexts are:
- person
- basketball.player
- basketball.team
- basketball.team.nested.coach
- basketball.team.nested.players
- basketball.team.nested.players.____
Constrain
The context child constrain
defines constraints by property name. These constraints will be required if the context is included in the validation session.
For instance, the 'person' context requires that:
- 'name' is not null
- 'email' is not null
- 'email' is a valid email address
Include
A context can include
other contexts by listing their names in an array or comma-delimited string.
In the above example, the 'basketball.player' context includes the 'person' context. This means that all of the configuration for the 'person' context now also applies to 'basketball.player' as well.
Nested
If you need to validate an object that includes another object, you can use the nested
context area. This works similarly to constrain
except that you are specifying new contexts for each property rather than constraints.
In the above example, a 'basketball.team' object must have a 'coach', as specified by the constrain
section of that context. But, we also have
nested: coach: include: person
This means that we want to validate the 'coach' property as an object, and so we specify a context for that property.
The Quadruple Underscore
While nested
alone works well for single objects, what if you want to validate a list of objects?
Here's the nested 'players' part of the 'basketball.team' context.
nested:
players:
nested:
____:
include: basketball.player
Nest again inside of 'players' to get at the objects in the list. Then you can use the ____
value to apply the context to every element of the array.
Why the double nesting? Remember that a JS Array is similar to an Object, except that it uses numbers as keys. Thus an array is effectively an object of objects.
In case you were just wondering, yes, this trick will work on objects of objects as well. It is worth remembering, however, that ____
will apply its contextual validation rules to EVERY object found as a property value of the parent object.
Constraints
Constraints can be defined anywhere in the VC, but wherever they appear they must be in an array. There are 3 ways to specify a constraint in the VC.
1. Constraint Methods
The simplest way, is to specify the name of a test method in the validator implementation.
- isEmail
Note that if the test method requires parameters you will need to build a constraint object instead.
2. Constraint Object
This is an actual definition of a constraint. Its unique identifier is the path to it in the VC.
- { name: notNull, method: isNull, flip: true }
Define constraint objects using these attributes:
-
method
(string)
The method on the validator object that will be called. -
name
(string)
Identifies the constraint within the list. If not specified,method
will be used. If more than one constraint have the same identifier within the same list only the first one loaded will be used. -
params
(array)
An array of additional parameters to be passed tomethod
on validation (for a single parameter brackets are not needed). -
flip
(boolean)
Set totrue
to reverse the boolean value returned bymethod
.
3. Constraint References
Lastly, you can include an individual constraint by specifying the path to it in the VC.
shoes: constrain: size: - is.notNullis: - { name: notNull, method: isNull, flip: true }
You can also include an entire array of constraints.
shoes: constrain: size: [ sizes ]sizes: - { name: isNotNull, method: isNull, flip: true } - { method: isIn, params: [ [ small, medium, large ] ] }
NOTE
Be aware of potential conflicts here. If a constraint array and a validator method have the same name, the latter will take precedence.
The Quadruple Underscore (again)
The ____
can also be used in the realm of constraints.
constrain:
____:
- is.notNumeric
email:
- isEmail
address:
- isAddress
Here, the ____
applies its constraints to all properties of the object. This means that, 'email' and 'address' (and EVERY other field) on the object must not be numeric.
Running a Validation
straints
-
target
(object)
The object to be validated -
contexts
(string or array)
One or more contexts to be validated against -
complete
(function(results))
Called when validation has completedresults
(object)
See Validation Results section below.
-
validate
(boolean <= function(property, target, constraint, result))
Called for every constraint test during the validation sessionproperty
(string)
name of the property that was evaludatedtarget
(object)
the object under validationconstraint
(object)
validation test that was runresult
(boolean)
validation result- returns (boolean) the final validation result
Validation Results
Here's the rundown on the information available in the validation results object.
-
target
The JS or JSON object that validation was run against. -
contexts
The contexts specified for the validation session. -
tried
For each property, the identifiers of the executed constraint tests on the property. -
failed
For each property, the identifiers of the failed constraint tests on the property. -
constraints
The constraints involved in the validation session indexed by their identifiers. -
isValid
A convenience function to determine whether or not all validations passed.
Validators
Straints is bundled with validator.js and uses it by default (i.e., if no validator
is provided in the configuration).
You may use any other validator implementation you wish. Just note that Straints will pass the test value as the first argument to the test method, followed by any parameters.
What Else?
Updates
Check out the changelog.
Tests
Under construction, but what there is of it can be invoked with
node test
Feedback
For bugs or feature suggestions please use the issue tracker.
Bugs will be fixed as quickly as possible.
License
Finally
Happy Validating!