api-check
If that build is ever red or the coverage is ever less than 100% then I want you to
flame me on twitter (@kentcdodds) and be sure to mention how disappointed
@josepheames would be in me
It's like ReactJS propTypes
without React. Actually,
it's very heavily inspired by this concept. It's purpose is for normal JavaScript functions rather than just React
Components.
Installation
$ npm i -S api-check
or $bower i -S api-check
api-check utilizes UMD, so you can:
var apiCheck = require('api-check')(/* your custom options, checkers*/);
Also available as an AMD module or as apiCheck
on global
Example
Note, there are a bunch of tests. Those should be instructive as well.
var myApiCheck = require('api-check')({
output: {
prefix: 'app/lib Name',
suffix: 'Good luck!',
docsBaseUrl: 'http://www.example.com/error-docs#'
},
verbose: false
}, {
});
function foo(bar, foobar) {
myApiCheck.warn([myApiCheck.number, myApiCheck.arrayOf(myApiCheck.string)], arguments);
}
foo(3, ['a','b','c']);
foo('whatever', false);
var myCheck = require('api-check')({
output: {
prefix: 'myApp',
suffix: 'see docs -->',
docsBaseUrl: 'http://example.com/error-descriptions#'
}
});
function doSomething(person, options, callback) {
myCheck.warn([
myCheck.shape({
name: myCheck.shape({
first: myCheck.string,
last: myCheck.string
}),
age: myCheck.number,
isOld: myCheck.bool,
walk: myCheck.func,
ipAddress: function(val, name, location) {
if (!/(\d{1,3}\.){3}\d{1,3}/.test(val)) {
return myCheck.utils.getError(name, location, 'ipAddress');
}
},
childrenNames: myCheck.arrayOf(myCheck.string).optional
}),
myCheck.any.optional,
myCheck.func
], arguments, {
prefix: 'doSomething',
suffix: 'Good luck!',
urlSuffix: 'dosomething-api-check-failure'
});
}
var person = {
name: {
first: 'Matt',
last: 'Meese'
},
age: 27,
isOld: false,
ipAddress: '127.0.0.1',
walk: function() {}
};
function callback() {}
var options = 'whatever I want because it is an "any" type';
console.log('Successful call');
doSomething(person, options, callback);
console.log('Successful call (without options)');
doSomething(person, callback);
console.log('Failed call (without person)');
doSomething(callback);
person.ipAddress = 'Invalid IP Address!!!';
console.log('Failed call (invalid ip address)');
doSomething(person, options, callback);
var libCheck = apiCheck();
function bar(a) {
var errorMessage = libCheck(apiCheck.string, arguments);
if (!errorMessage) {
} else if (typeof errorMessage === 'string') {
}
}
bar('hello!');
Differences from React's propTypes
Differences in Supported Types noted below with a *
- All types are required by default, to set something as optional, append
.optional
- checkApi.js does not support
element
and node
types - checkApi.js supports a few additional types
object
fails on null. Use object.nullOk
if you don't want that
Similarities to React's propTypes
This project was totally written from scratch, but it (should) support the same api as React's propTypes
(with the
noted difference above). If you notice something that functions differently, please file an issue.
apiCheck(), apiCheck.warn(), and apiCheck.throw()
These functions do the same thing, with minor differences. In both the warn
and throw
case, a message is generated
based on the arguments that the function was received and the api that was defined to describe what was wrong with the
invocation.
In all cases, an object is returned with the following properties:
argTypes (arrayOf[Object])
This is an array of objects representing the types of the arguments passed.
apiTypes (arrayOf[Object])
This is an object representing the types of the api. It's a whole language of its own that you'll hopefully get after
looking at it for a while.
failed (boolean)
Will be false when the check passes, and true when it fails
passed (boolean)
Will be true when the check passes, and false when it fails
message (string)
If the check failed, this will be a useful message for display to the user. If it passed, this will be an empty string
Also note that if you only have one argument, then the first argument to the apiCheck
function can simply be the
checker function. For example:
apiCheck(apiCheck.bool, arguments);
The second argument can either be an arguments-like object or an array.
Supported types
array
apiCheck.array([]);
apiCheck.array(23);
bool
apiCheck.bool(false);
apiCheck.bool('me bool too?');
func
apiCheck.func(function() {});
apiCheck.func(new RegExp());
func.withProperties *
Not available in React's propTypes
var checker = apiCheck.func.withProperties({
type: apiCheck.oneOfType([apiCheck.object, apiCheck.string]),
help: apiCheck.string.optional
});
function winning(){}
winning.type = 'awesomeness';
checker(winning);
function losing(){}
checker(losing);
number
apiCheck.number(423.32);
apiCheck.number({});
object *
null
fails, use object.nullOk
to allow null to pass
apiCheck.object({});
apiCheck.object([]);
apiCheck.object(null);
object.nullOk *
Not available in React's propTypes
apiCheck.object.nullOk({});
apiCheck.object.nullOk([]);
apiCheck.object.nullOk(null);
string
apiCheck.string('I am a string!');
apiCheck.string([]);
range
apiCheck.range(0, 10)(4);
apiCheck.rang(-100, 100)(500);
greaterThan
apiCheck.greaterThan(100)(200);
apiCheck.greaterThan(-10)(-20);
apiCheck.greaterThan(50)('Frogs!');
lessThan
apiCheck.lessThan(100)(50);
apiCheck.lessThan(-10)(0);
apiCheck.lessThan(50)('Frogs!');
instanceOf
apiCheck.instanceOf(RegExp)(new RegExp);
apiCheck.instanceOf(Date)('wanna go on a date?');
oneOf
apiCheck.oneOf(['Treek', ' Wicket Wystri Warrick'])('Treek');
apiCheck.oneOf(['Chewbacca', 'Snoova'])('Snoova');
oneOfType
apiCheck.oneOfType([apiCheck.string, apiCheck.object])({});
apiCheck.oneOfType([apiCheck.array, apiCheck.bool])('Kess');
arrayOf
apiCheck.arrayOf(apiCheck.string)(['Huraga', 'Japar', 'Kahless']);
apiCheck.arrayOf(
apiCheck.arrayOf(
apiCheck.arrayOf(
apiCheck.number
)
)
)([[[1,2,3], [4,5,6], [7,8,9]], [[1,2,3], [4,5,6], [7,8,9]]]);
apiCheck.arrayOf(apiCheck.bool)(['a', 'b', 'c']);
typeOrArrayOf *
Not available in React's propTypes
Convenience checker that combines oneOfType
with arrayOf
and whatever you specify. So you could take this:
apiCheck.oneOfType([
apiCheck.string, apiCheck.arrayOf(apiCheck.string)
]);
with
apiCheck.typeOrArrayOf(apiCheck.string);
which is a common enough use case to justify the checker.
apiCheck.typeOrArrayOf(apiCheck.string)('string');
apiCheck.typeOrArrayOf(apiCheck.string)(['array', 'of strings']);
apiCheck.typeOrArrayOf(apiCheck.bool)(['array', false]);
apiCheck.typeOrArrayOf(apiCheck.object)(32);
objectOf
apiCheck.objectOf(apiCheck.arrayOf(apiCheck.bool))({a: [true, false], b: [false, true]});
apiCheck.objectOf(apiCheck.number)({a: 'not a number?', b: 'yeah, me neither (◞‸◟;)'});
shape *
Note: React propTypes
does support shape
, however it does not support the strict
option
If you add .strict
to the shape
, then it will enforce that the given object does not have any extra properties
outside those specified in the shape
. See below for an example...
apiCheck.shape({
name: checkers.shape({
first: checkers.string,
last: checkers.string
}),
age: checkers.number,
isOld: checkers.bool,
walk: checkers.func,
childrenNames: checkers.arrayOf(checkers.string)
})({
name: {
first: 'Matt',
last: 'Meese'
},
age: 27,
isOld: false,
walk: function() {},
childrenNames: []
});
apiCheck.shape({
mint: checkers.bool,
chocolate: checkers.bool
})({mint: true});
Example of strict
var strictShape = apiCheck.shape({
cookies: apiCheck.bool,
milk: apiCheck.bool,
popcorn: apiCheck.bool.optional
}).strict;
strictShape({
cookies: true,
milk: true,
popcorn: true,
candy: true
});
strictShape({
cookies: true,
milk: true
});
Note, you can also append .optional
to the .strict
(as in: apiCheck.shape({}).strict.optional
)
shape.onlyIf *
Not available in React's propTypes
This can only be used in combination with shape
apiCheck.shape({
cookies: apiCheck.shape.onlyIf(['mint', 'chips'], apiCheck.bool)
})({cookies: true, mint: true, chips: true});
apiCheck.shape({
cookies: apiCheck.shape.onlyIf(['mint', 'chips'], apiCheck.bool)
})({chips: true});
apiCheck.shape({
cookies: apiCheck.shape.onlyIf('mint', apiCheck.bool)
})({cookies: true});
shape.ifNot *
Not available in React's propTypes
This can only be used in combination with shape
apiCheck.shape({
cookies: apiCheck.shape.ifNot('mint', apiCheck.bool)
})({cookies: true});
apiCheck.shape({
cookies: apiCheck.shape.ifNot(['mint', 'chips'], apiCheck.bool)
})({cookies: true, chips: true});
requiredIfNot *
Not available in React's propTypes
This can only be used in combination with shape
checker = checkers.shape({
foobar: checkers.shape.requiredIfNot(['foobaz', 'baz'], checkers.bool),
foobaz: checkers.object.optional,
baz: checkers.string.optional,
foo: checkers.string.optional
});
checker({
foo: [1, 2],
foobar: true
});
checker({foo: 'bar'});
all
Not available in React's propTypes
This can only be used in combination with shape.requiredIfNot
checker = checkers.shape({
foobar: checkers.shape.requiredIfNot.all(['foobaz', 'baz'], checkers.bool),
foobaz: checkers.object.optional,
baz: checkers.string.optional,
foo: checkers.string.optional
});
checker({
foo: [1, 2]
});
checker({
foo: [1, 2],
foobar: true
});
checker({
foo: [1, 2],
baz: 'foo'
});
args *
Not available in React's propTypes
This will check if the given item is an arguments
-like object (non-array object that has a length property)
function foo(bar) {
apiCheck.args(arguments);
}
apiCheck.args([]);
apiCheck.args({});
apiCheck.args({length: 3});
apiCheck.args({length: 'not-number'});
any
apiCheck.any({});
apiCheck.any([]);
apiCheck.any(true);
apiCheck.any(false);
apiCheck.any();
apiCheck.any.optional();
apiCheck.any(3);
apiCheck.any(3.1);
apiCheck.any(3.14);
apiCheck.any(3.141);
apiCheck.any(3.1415);
apiCheck.any(3.14159);
apiCheck.any(3.141592);
apiCheck.any(3.1415926);
apiCheck.any(3.14159265);
apiCheck.any(3.141592653);
apiCheck.any(3.1415926535);
apiCheck.any(3.14159265359);
apiCheck.any(jfio,.jgo);
Custom Types
You can specify your own type. You do so like so:
var myCheck = require('api-check')({
output: {prefix: 'myCheck'}
});
function ipAddressChecker(val, name, location) {
if (!/(\d{1,3}\.){3}\d{1,3}/.test(val)) {
return apiCheck.utils.getError(name, location, ipAddressChecker.type);
}
};
ipAddressChecker.type = 'ipAddressString';
function foo(string, ipAddress) {
myCheck.warn([
myCheck.string,
ipAddressChecker
], arguments);
}
Then, if you invoked that function like this:
foo('hello', 'not-an-ip-address');
It would result in a warning like this:
myCheck apiCheck failed! `Argument 1` passed, `value` at `Argument 2` must be `ipAddressString`
You passed:
[
"hello",
"not-an-ip-address"
]
With the types:
[
"string",
"string"
]
The API calls for:
[
"String",
"ipAddressString"
]
There's actually quite a bit of cool stuff you can do with custom types checkers. If you want to know what they are,
look at the tests or file an issue for me to go document them. :-)
apiCheck.utils
When writing custom types, you may find the utils
helpful. Please file an issue to ask me to improve documentation for
what's available. For now, check out api-check-utils.test.js
Customization
Note, obviously, these things are specific to apiCheck
and not part of React propTypes
When you create your instance of apiCheck
, you can configure it with different options as part of the first argument.
config.output
You can specify some extra options for the output of the message.
var myApiCheck = require('api-check')({
output: {
prefix: 'Global prefix',
suffix: 'global suffix',
docsBaseUrl: 'https://example.com/errors-and-warnings#'
},
verbose: false,
disabled: false
});
You can also specify an output
object to each apiCheck()
, apiCheck.throw()
, and apiCheck.warn()
request:
myApiCheck(apiCheck.bool, arguments, {
prefix: 'instance prefix:',
suffix: 'instance suffix',
urlSuffix: 'example-error-additional-info'
});
A failure with the above configuration would yield something like this:
Global prefix instance prefix {{error message}} instance suffix global suffix https://example.com/errors-and-warnings#example-error-additional-info
As an alternative to urlSuffix
, you can also specify a url
:
myApiCheck(apiCheck.bool, arguments, {
url: 'https://example.com/some-direct-url-that-does-not-use-the-docsBaseUrl'
});
getErrorMessage
This is the method that apiCheck uses to get the message it throws or console.warns. If you don't like it, feel free to
make a better one by simply: apiCheck.getErrorMessage = function(api, args, output) {/* return message */}
handleErrorMessage
This is the method that apiCheck uses to throw or warn the message. If you prefer to do your own thing, that's cool.
Simply apiCheck.handleErrorMessage = function(message, shouldThrow) { /* throw or warn */ }
Disable apiCheck
It's a good idea to disable the apiCheck in production. You can disable your own instance of apiCheck
as part of
the options
, but it's probably just better to disable apiCheck
globally. I recommend you do this before you (or
any of you dependencies) create an instance of apiCheck
. Here's how you would do that:
var apiCheck = require('api-check');
apiCheck.globalConfig.disabled = true;
Credits
This library was written by Kent C. Dodds. Again, big credits go to the team working
on React for thinking up the api. This library was written from scratch, but I'd be lying if I didn't say that I
referenced their functions a time or two.