npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

schema-fns

v0.1.5

Published

Composable schema functions for input transformation and validation

Downloads

2

Readme

schema-fns

Composable schema functions for input transformation and validation

Installation

yarn add schema-fns
# or
npm install schema-fns

Overview

schema-fns lets you build composable schemas for input transformation and validation. The most important export is the schema() function that's used to define schemas. It's modeled after the Express middleware model of layers of handler functions and therefore accepts as parameters any number of functions or schema objects. Those functions have a signature of (value, update, error), where:

  • value is the input value to be transformed and validated,
  • update is a function used to return a modified input value, and
  • error is a function used to create.

Input transformation

Let's see it in action. The first thing you can do with schema-fns is transform input. The following example uses two transformation functions, one to convert the value to a string, and another to append a "1". Given the input 42, the value becomes "421":

const { schema } = require("schema-fns");

const MySchema = schema(
  function (value, update, error) {
    update(String(value));
  },
  function (value, update, error) {
    update(value + "1");
  },
);

const { value } = MySchema.validate(42);
console.log(value); //=> "421"

For simple value tranformations, there's an adapter function mapAdapter() that converts mapping functions to a form that works for schema-fns. The same example above can be written as:

const { schema, mapAdapter } = require("schema-fns");

const MySchema = schema(
  mapAdapter(value => String(value)),
  mapAdapter(value => value + "1"),
);

const { value } = MySchema.validate(42);
console.log(value); //=> "421"

Input validation

The next interesting thing you can do is validate input values. And the simplest way is to use built-in validation functions. Calling .validate(input) returns an object with:

  • valid: true if there were no validation errors, false otherwise
  • value: the transformed value
  • errors: an array of any validation errors

.validate(value)

Here's an example that validates that a value is an object.

const { schema, is } = require("schema-fns");

const MySchema = schema(
  is(Object),
);

const { valid, value, errors } = MySchema.validate(42);
console.log(valid); //=> false
console.log(value); //=> 42 (because the value was never updated)

The errors array will look like the following:

[{
  code: "is.type", // the validation error code
  path: [], // path taken to get from the input to the value where the error happened
  message: "wrong type: expected object", // the default
  value: 42, // the value validated
  expectedType: "object", // each validator may include additional context
}]

.test(value)

In addition to .validate(), there is a .test() function that just returns the value of valid:

const { schema, is } = require("schema-fns");

const MySchema = schema(
  is(Object),
);

console.log(MySchema.test(42)); //=> false
console.log(MySchema.test({})); //=> true
console.log(MySchema.test("hi")); //=> false

.assert(value)

Finally, if instead of receiving the transformed value and errors, .assert() will throw an error if validation fails:

const { schema, is } = require("schema-fns");

const MySchema = schema(
  is(Object),
);

MySchema.assert(42); // throws
MySchema.assert("hi"); // throws
MySchema.assert({}); // doesn't throw

hasKeys(...keyNames)

Let's look at more validation functions. hasKeys() checks that keys are present on some object:

const { schema, hasKeys } = require("schema-fns");

const MySchema = schema(
  hasKeys("foo", "bar"),
);

const { valid, value, errors } = MySchema.validate({});
console.log(valid); //=> false
console.log(value); //=> {}

The errors array will look like:

[{
  code: "key.missing",
  path: [],
  message: `expected key "foo" missing`,
  key: "foo",
  value: {},
}, {
  code: "key.missing",
  path: [],
  message: `expected key "bar" missing`,
  key: "bar",
  value: {},
}]

key(name, ...fns)

The key() function is used to focus in on one key of an object and apply schema functions to it. The first argument is the key name and the remaining arguments are schema functions.

const { schema, key, mapAdapter } = require("schema-fns");

const MySchema = schema(
  key(
    "name",
    mapAdapter(value => `Hello, ${value}!`),
    mapAdapter(value => String(value).toUpperCase()),
  ),
);

const { valid, value, errors } = MySchema.validate({ name: "World" });

console.log(valid); //=> true
console.log(value); //=> { "name": "HELLO, WORLD!" }
console.log(errors); //=> []

It can also be used for validation:

const { schema, is, key } = require("schema-fns");

const MySchema = schema(
  key("foo", is(Object)),
);

const { valid, value, errors } = MySchema.validate({ foo: 42 });
console.log(valid); //=> false
console.log(value); //=> { foo: 42 }

The errors array will look like:

[{
  code: "is.type",
  path: ["foo"],
  message: "wrong type: expected object",
  expectedType: "object",
  value: 42,
}]

Of course, key() is recursive:

const { schema, is, key } = require("schema-fns");

const MySchema = schema(
  key(
    "name",
    is(Object),
    key("first", is(String)),
  ),
);

const { valid, value, errors } = MySchema.validate({
  name: {
    first: 42,
  },
});

console.log(valid); //=> false
console.log(value); //=> { name: { first: 42 } }

The errors array will look like:

[{
  code: "is.type",
  path: ["name", "first"],
  message: "wrong type: expected string",
  expectedType: "string",
  value: 42,
}]

items(...fns)

The items() function is used to transform and validate all items in an array. Here's a transformation example that doubles numbers in an array and then repeats the digits:

const { schema, items, mapAdapter } = require("schema-fns");

const MySchema = schema(
  items(
    mapAdapter(value => value * 2),
    mapAdapter(value => String(value) + String(value)),
    mapAdapter(Number)
  ),
);

const { valid, value, errors } = MySchema.validate([ 1, 2, 3 ]);

console.log(valid); //=> true
console.log(value); //=> [ 22, 44, 66 ]);

It can be used for validation as well:

const { schema, is, items } = require("schema-fns");

const MySchema = schema(
  items(is(Number)),
);

const { valid, value, errors } = MySchema.validate([ 1, 2, "3", 4, "5" ]);

console.log(valid); //=> false
console.log(value); //=> [ 1, 2, "3", 4, "5" ]

The errors array will look like:

[{
  code: "is.type",
  path: [2],
  message: "wrong type: expected number",
  expectedType: "number",
  value: "3",
}, {
  code: "is.type",
  path: [4],
  message: "wrong type: expected number",
  expectedType: "number",
  value: "5",
}]

Custom input validation

Writing custom validators is as simple as calling the error() function, passing a string error code and a context object with additional information useful for debugging or messaging to an end user.

const { schema, is, items } = require("schema-fns");

const MySchema = schema(
  notFoo(),
);

// fails validation if the value equals "foo", "FOO", etc
function notFoo() {
  return function (value, update, error) {
    if (String(value).toLowerCase() === "foo") {
      // first argument is a unique error code
      // second argument is a context object
      error("not.foo", {
        message: `not foo expected, got ${value}`,
        value,
      });
    }
  };
}

console.log(MySchema.test("foo")); //=> false
console.log(MySchema.test("fOO")); //=> false
console.log(MySchema.test("FOo")); //=> false
console.log(MySchema.test(42)); //=> true
console.log(MySchema.test({})); //=> true

Credit

Inspired by Joi and superstruct.