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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lms5400/easy-validation

v3.0.0

Published

Simple JSON schema validation. Ideal for request payloads

Readme

Simple JavaScript/TypeScript Object & JSON Validator

This package is a lightweight schema validator that is useful for asserting keys and types inside an object or a JSON response, often from HTTP requests. It allows you to reject requests with incomplete or malformed payloads before passing the data along to other parts of the system. It is also helpful for providing users with feedback about what is malformed.

This package includes TypeScript type definitions out of the box.

This package pairs especially well with MongoDB because, after validation, you can confidently insert the JSON objects into the database.

Installation

npm install @lms5400/easy-validation

Simple Example

import { validateData, types, conditions } from 'easy-validation';

const schema = {
  id: types.isNumeric.and(conditions.required),
  name: types.isString.and(conditions.required),
  attributes: {
    height: types.isNumeric,
    weight: types.isNumeric,
    age: types.isInteger,
  },
};

router.post("/test", async (req, res) => {
  const result = await validateData(schema, req.body);
  
  if (result !== true) {
    res.status(400).json(result);
    return;
  }

  // Perform an action ...

  res.status(200).send("success");
});

Sample Requests and Responses

The validation result is either true or an array of errors. Each error contains a unique key and an error message.

Example 1:

Method: POST
Body: {}

[
  {"key": "id", "error": "value is required but missing"},
  {"key": "name", "error": "value is required but missing"}
]

Example 2:

Method: POST
Body: {"id":"10","name":"me","attributes":{"height":"20","weight":"hello"}}

[
  {"key": "id", "error": "value is not a number"},
  {"key": "attributes.height", "error": "value is not a number"},
  {"key": "attributes.weight", "error": "value is not a number"}
]

Example 3:

Method: POST
Body: {"id":10,"name":"me","attributes":{"height":20,"weight":0}}

success

Note: To use the results in a UI such as in a React frontend app, its helpful to use toKeys() to convert the array of errors to a key value map. See Mapping back to UI fields below.

Types and Conditions

All schema values must resolve to a type. Optionally, each type may have one or more conditions attached using the and() function to further specify constraints.

For example, you may require a string, but it must also be non-empty and mandatory (i.e., not missing). This can all be achieved using conditions.

Example:

const schema = types.isString.and(conditions.notEmpty, conditions.required);
const result = validateData(schema, "hello world");

Available Types

isBoolean

isNumeric

isInteger

isString

isFunction

isObject

isArray

isAnyOf([
  param1: type, param2: type, ...
])

isCustom((value: any) => true | "error message")

Available Conditions

// Applicable to all types
required

// Applicable to string types only
notEmpty

// Applicable to array or number types only. Pass undefined for either parameter to remove the relative limit.
range(lowerLimit: number|undefined, upperLimit: number|undefined)

// Applicable to string types only
inList([string1: string, string2: string, ...]) 

// Applicable to array types only
ofType(type)

// Applicable to object types only
ofShape(shape: object | typeof isObject)  

Object Literals vs. isObject

There is a key difference between using an object literal and isObject. While an object literal defines the shape, isObject allows additional conditions to be attached.

Example:

import { types, conditions, validateData } from 'easy-validation';

const schema1 = {
  name: types.isString,
  props: {
    x: types.isNumeric,
    y: types.isNumeric,
    z: types.isNumeric,
  }
};

const schema2 = {
  name: types.isString,
  props: types.isObject.and(
    conditions.ofShape({
      x: types.isNumeric,
      y: types.isNumeric,
      z: types.isNumeric,
    }),
    conditions.required  // This makes a difference!
  )
};

const data = {};

await validateData(schema1, data); // true
await validateData(schema2, data); // [{ key: 'props', error: 'value is required but missing' }]

While both schemas validate the props object, if props is omitted, schema1 does not trigger an error, whereas schema2 does.

Wildcards

In some cases, it may be helpful to create a schema for UUIDs or unknown key values. A wildcard allows validation of dynamic keys inside an object. This is useful for accessing data in O(1) time using an ID instead of searching through an array.

Note: Wildcards must be the only key in an object schema for each respective level.

import { conditions, types, validateData } from 'easy-validation';

const sampleData = {
  ids: {
    '1001': { name: 'name1' },
    '1002': { name: 'name2' }
  }
};

const schema = {
  ids: {
    '*': types.isObject.and(
      conditions.ofShape({
        name: types.isString.and(conditions.required)
      }),
      conditions.required
    )
  }
};

const result = await validateData(schema, sampleData);

A Larger Example

This is a more complete sample of what the API might look like in practice.

const {types, conditions, validateData} = require('easy-validation');

const color = {
  red: types.isInteger.and(conditions.range(0,255), conditions.required),
  green: types.isInteger.and(conditions.range(0,255), conditions.required),
  blue: types.isInteger.and(conditions.range(0,255), conditions.required),
  alpha: types.isNumeric.and(conditions.range(0,1), conditions.required),
}

const defaultColor = types.isString.and(conditions.inList([
  'red', 
  'blue', 
  'green', 
  'yellow', 
  'black'
]));

const schema = {
  name: types.isString.and(conditions.required),
  description: types.isString,
  colors: types.isArray.and(conditions.ofType(
    types.isAnyOf([
      // array items can be a color or enumerated string
      color, 
      defaultColor]) 
    ),
    conditions.required
  ),
  properties: types.isObject.and(
    conditions.ofShape({
      length: types.isNumeric.and(conditions.range(0,100), conditions.required),
      width: types.isNumeric.and(conditions.range(0,100), conditions.required),
      height: types.isNumeric.and(conditions.range(0,100), conditions.required),
    }),
    conditions.required
  )
}

const sampleData = {
  name: "mycolor",
  colors: [{red:100, green:255, blue:0, alpha: 0.5}, 'yellow', 'black', 'orange'],
  properties: {
    length: 5.5,
    width: 20.1,
    height: 55.9
  }
}

const result = await validateData(schema, sampleData);
res.status(200).json(result);

result:

[
  {
    "key":"colors.3",
    "error":"value failed to match one of the the allowed types"
  }
]

Asynchronous usage and custom conditions

Use isCustom to pass asynchronous functions to value validation. This is useful for performing a database query to validate an ID asynchronously. Alternatively you may also create any custom condition you wish and add it to the and() parameters for a given type. Conditions are asynchronously by nature too.

  const {types, conditions, validateData} = require('easy-validation');

  async function validateKey(value) {
    try {
      await myLookupFunction(value); // this could be a lookup in a database for example
      return true;
    }
    catch (e) {
      return `value ${value} not found`
    }

  }
  const schmea = {
    id: types.isCustom(validateKey).and(conditions.required),
  }
  const result = await validateData(schema, {
    id: 123467
  });
  

Mapping back to UI fields

In many cases, it is helpful to map errors back to UI fields of nested properties. However, using an array of errors in such cases is not ideal. To simplify this process, we can convert the array into key-value pairs since all keys are unique.

In the partial example below, the error will be displayed when returned from the service; otherwise, it will not. This approach can also work for arrays, as the array index is reported back. You can further enhance this by creating encapsulated validation components for form fields.

React example:

import { toKeys } from 'easy-validation'

const [fields setFields]  = useState({})
const [validationErrors setValidationErrors]  = useState({})

...

<form>
  ...
  <div>
    <label for="my-height-field">Height</label>
    <input id="my-height-field" type="text" defaultValue={fields.attributes.height}>
    <span style={{color: 'red'}}>{validationErrors['attributes.height'] || ''}</span>
  </div>
  ...
</form>

... 

try {
  // some API endpoint that validates the payload
  await this.$axios.$post("...", fields);
} catch(err) {
  // transform the array of errors to key:value errors that can be indexed into
  const keyErrors = toKeys(err.response.data)
  
  setValidationErrors(keyErrors); 
}

Future Enhancements

  • Custom error messages for each type
  • Support for nullable types
  • Add safeString condition
  • Add support for data translators