Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/troygoode/node-tsa
Guard your REST API with a bit of fascism.
https://github.com/troygoode/node-tsa
Last synced: about 6 hours ago
JSON representation
Guard your REST API with a bit of fascism.
- Host: GitHub
- URL: https://github.com/troygoode/node-tsa
- Owner: troygoode
- License: mit
- Created: 2012-06-08T05:09:34.000Z (over 12 years ago)
- Default Branch: master
- Last Pushed: 2013-08-14T12:02:18.000Z (over 11 years ago)
- Last Synced: 2024-11-04T22:53:54.233Z (12 days ago)
- Language: JavaScript
- Homepage: https://github.com/TroyGoode/node-tsa
- Size: 250 KB
- Stars: 9
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.markdown
- License: LICENSE
Awesome Lists containing this project
README
# tsa
*Guard your REST API with a bit of fascism.*
TSA is a node.js library designed to take JSON input and:
* filter it against a whitelist
* validate it
* transform it
* provide default valuesIt has been designed with usage in an Express-based JSON REST API in mind, and allows you to easily pass it into your route as middleware.
[![NPM](https://nodei.co/npm/tsa.png?downloads=true&stars=true)](https://nodei.co/npm/tsa/)
[![build status](https://secure.travis-ci.org/TroyGoode/node-tsa.png)](http://travis-ci.org/TroyGoode/node-tsa)
## Table of Contents
* [Installation](#installation)
* [Usage](#usage)
* Usage : [Using TSA Directly](#using-tsa-directly)
* Usage : [Using TSA via Express Middleware](#using-tsa-via-express-middleware)
* [Creating Guards](#creating-guards)
* Creating Guards : [Nested Guards](#nested-guards)
* Creating Guards : [Required Properties](#required-properties)
* Creating Guards : [Optional Properties](#optional-properties)
* Creating Guards : [Whitelisting](#whitelisting)
* Creating Guards : [Default Values](#default-values)
* Creating Guards : [Built-In Validations](#built-in-validations)
* Creating Guards : [Custom Validations](#custom-validations)
* Creating Guards : [Transformations](#transformations)
* Creating Guards : [Sanitization](#sanitization)
* Creating Guards : [Rename Properties](#rename-properties)
* Creating Guards : [Combinations](#combinations)
* Creating Guards : [Error Handling](#error-handling)
* [Test](#test)
* [License](#license)
* [Author](#author)## Installation (via [npm](https://npmjs.org/package/tsa))
```bash
$ npm install tsa
```## Usage
### Using TSA Directly
Create a guard:
```javascript
var tsa = require('tsa');
var guard = tsa({
property1: tsa.required()
, property2: tsa.optional()
, property3: tsa.default('blah')
});
```Validate input against guard:
```javascript
var input = {
property1: 'foo'
, property4: 'bar'
};
guard().frisk(input, function(err, result){
// err === null
// result.property1 === 'foo'
// result.property2 === undefined
// result.property3 === 'blah'
// result.property4 === undefined
});
```### Using TSA via Express Middleware
Create a guard:
```javascript
var tsa = require('tsa');
var guard = tsa({
property1: tsa.required()
, property2: tsa.optional()
});
```Ensure you're using express's body parser:
```javascript
app.use(express.bodyParser());
```Add that guard's middleware to your route:
```javascript
app.post('/foo', guard().middleware(), function(req, res){
// req.body is the whitelisted, validated, transformed version of the input from req.body
});app.error(function(err, req, res, next){
// err is an array of errors generated by the guard
});
```Alternatively you can handle the errors on a per-route basis instead of globally:
```javascript
app.post('/foo', guard().middleware(function(err, req, res, next){
// return a 400, show an error page, ignore by calling next, whatever
}), function(req, res){
// req.body is the whitelisted, validated, transformed version of the input from req.body
});
```## Creating Guards
### Nested Guards
```javascript
var address = tsa({
street1: tsa.required()
, street2: tsa.optional()
});
var person = tsa({
name: tsa.required()
, address: address()
});
```Nested guards can also be created inline:
```javascript
var person = tsa({
name: tsa.required()
, address: tsa({
street1: tsa.required()
, street2: tsa.optional()
})()
});
```You can validate/transform/etc nested guards either at the definition level, or the usage level:
```javascript
// example of adding validations to guard definition
var address = tsa({
street1: tsa.required()
, street2: tsa.optional()
}, {validate: someValidationFunction});
``````javascript
// example of adding validations to guard usage
var person = tsa({
name: tsa.required()
, address: address({validate: aDifferentValidationFunction})
});
```### Required Properties
```javascript
var guard = tsa({
property1: tsa.property({ required: true }) // or: tsa.required()
});
var input = {};
guard().frisk(input, function(err, result){
// err === instanceof Array
// err[0] === {key: 'property1', error: 'Required property property1 not supplied.'}
});
```You can provide a custom error message like so:
```javascript
var guard = tsa({
example1: tsa.property({ required: 'fail!' })
, example2: tsa.required('fail!')
});
```### Optional Properties
```javascript
var guard = tsa({
property1: tsa.property() // or: tsa.optional()
});
var input = {};
guard().frisk(input, function(err, result){
// err === null
// result === {}
});
```### Whitelisting
```javascript
var guard = tsa({
property1: tsa.required()
});
var input = {
property1: 'foo'
, property2: 'bar'
};
guard().frisk(input, function(err, result){
// result.property1 === 'foo'
// result has no property2 key
});
```### Default Values
```javascript
var guard = tsa({
foo: tsa.property({ default: 'bar' }) // or: tsa.default('bar')
});
var input = {};
guard().frisk(input, function(err, result){
// err === null
// result.foo === 'bar'
});
```Optionally, the default value can be a function which will be executed by tsa:
```javascript
var now = function(){
return new Date();
};
var guard = tsa({
foo: tsa.property({ default: now }) // or: tsa.default(now)
});
var input = {};
guard().frisk(input, function(err, result){
// err === null
// result.foo === a Date object
});
```### Built-In Validations
TSA ships with a few validations built-in. Here are some examples:
```javascript
var guard = tsa({
foo: tsa.require({ validate: tsa.validate.boolean() })
, bar: tsa.require({ validate: tsa.validate.boolean('The value "%1" is not a boolean.') }) // <- custom error message
, baz: tsa.require({ validate: tsa.validate.true() })
, boo: tsa.require({ validate: tsa.validate.false() })
});
```
```javascript
var guard = tsa({
foo: tsa.require({ validate: tsa.validate.numeric() })
, foo2: tsa.require({ validate: tsa.validate.numeric('fail!') }) // <- custom error message
, bar: tsa.require({ validate: tsa.validate.range(0, 10) })
, bar2: tsa.require({ validate: tsa.validate.range(0, 10, {
invalid: 'custom error message'
, below: 'custom error message'
, above: 'custom error message'
}) })
, baz: tsa.require({ validate: tsa.validate.min(0) })
, baz2: tsa.require({ validate: tsa.validate.min(0, {
invalid: 'custom error message'
, below: 'custom error message'
}) })
, boo: tsa.require({ validate: tsa.validate.max(10) })
, boo2: tsa.require({ validate: tsa.validate.max(10,
invalid: 'custom error message'
, above: 'custom error message'
}) })
});
```
```javascript
var guard = tsa({
foo: tsa.require({ validate: tsa.validate.regex(/^bar$/g) })
, foo: tsa.require({ validate: tsa.validate.regex(/^bar$/g, 'fail!') }) // <- custom error message
});
```### Custom Validations
```javascript
var mustBeUpper = function(input, cb){
if(input.toUpperCase() === input){
cb(); // yes, this is uppercase
}else{
cb('not uppercase!'); // oh noes!
}
};
var guard = tsa({
foo: tsa.property({ validate: mustBeUpper }) // or: tsa.validate(mustBeUpper)
});
var input = { foo: 'bar' };
guard().frisk(input, function(err, result){
// err[0] === {key: 'foo', error: 'not uppercase!'}
// result === null
});
```Your custom validations can return multiple errors, if necessary:
```javascript
var myValidationFunction = function(input, cb){
if(...){
cb(); // passed!
}else{
cb(['error message 1', 'error message 2']); // failed...
}
};
```### Transformations
```javascript
var toUpper = function(input, cb){
cb(null, input.toUpperCase());
};
var guard = tsa({
foo: tsa.property({ transform: toUpper }) // or: tsa.transform(toUpper)
});
var input = { foo: 'bar' };
guard().frisk(input, function(err, result){
// err === null
// result.foo === 'BAR'
});
```### Sanitization
Sanitizing a property runs a validation function against it, but rather than failing the guard if an error is reported that property is simply thrown away in the case of an error:
```javascript
var mustBeUpper = function(input, cb){
if(input.toUpperCase() === input){
cb(); // yes, this is uppercase
}else{
cb('not uppercase!'); // oh noes!
}
};
var guard = tsa({
foo: tsa.property({ sanitize: mustBeUpper }) // or: tsa.sanitize(mustBeUpper)
, fizz: tsa.sanitize(mustBeUpper)
});
var input = { foo: 'bar', fizz: 'BANG' };
guard().frisk(input, function(err, result){
// err === null
// result.foo === undefined
// result.fizz === 'BANG'
});
```Note that you can run TSA's built-in validations through sanitize:
```javascript
var guard = tsa({
foo: tsa.sanitize(tsa.validate.regex(/^bar$/g))
});
```### Rename Properties
```javascript
var guard = tsa({
foo: tsa.property({ rename: 'bar' }) // or: tsa.rename('bar')
});
var input = { foo: 'blah' };
guard().frisk(input, function(err, result){
// result.foo === undefined
// result.bar === 'blah'
});
```### Combinations
You can combine any and all of the above like so:
```javascript
var toUpper = function(input, cb){
cb(null, input.toUpperCase());
};
var guard = tsa({
foo: tsa.property({ required: true, transform: toUpper })
// or: tsa.required({ transform: toUpper })
// or: tsa.transform(toUpper, {required: true})
});
var input = { foo: 'bar' };
guard().frisk(input, function(err, result){
// err === null
// result.foo === 'BAR'
});
```### Error Handling
Errors for nested structures are returned like so:
```javascript
[
{key: 'first', error: 'Required property not provided.'}
, {key: 'address', error: [
{key: 'street1', error: 'Required property not provided.'}
, {key: 'zip', error: 'Required property not provided.'}
]}
]
```While this is a very structured format, it isn't always the easiest for
doing things like highlighting form fields that have errors. In those
situations you can pass the error structure into the `tsa.flattenErrors`
method to get back something like this:```javascript
[
{key: 'first', error: 'Required property not provided.'}
, {key: 'address[street1]', error: 'Required property not provided.'}
, {key: 'address[zip]', error: 'Required property not provided.'}
]
```Passing `{hash: true}` into `tsa.flattenErrors` as the second argument results in:
```javascript
{
'first': ['Required property not provided.']
, 'address[street1]': ['Required property not provided.']
, 'address[zip]': ['Required property not provided.']
}
```## Test
Run tests via mocha:
```bash
$ npm install -g mocha
$ git clone git://github.com/TroyGoode/node-tsa.git tsa
$ cd tsa/
$ npm install
$ mocha
```Run example web app:
```bash
$ git clone git://github.com/TroyGoode/node-tsa.git tsa
$ cd tsa/
$ npm install
$ cd example/
$ npm install
$ npm start
$ open http://localhost:3000
```## License
[MIT License](http://www.opensource.org/licenses/mit-license.php)
## Author
[Troy Goode](https://github.com/TroyGoode) ([[email protected]](mailto:[email protected]))