mikaelvesavuori/mikrovalid

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# mikrovalid

**MikroValid is the minimalist, smart, and easy way to validate objects on both the client and server-side.**

![Build Status](https://github.com/mikaelvesavuori/mikrovalid/workflows/main/badge.svg)

[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=mikaelvesavuori_mikrovalid&metric=alert_status)](https://sonarcloud.io/dashboard?id=mikaelvesavuori_mikrovalid)

[![codecov](https://codecov.io/gh/mikaelvesavuori/mikrovalid/graph/badge.svg?token=2P5YYO89J2)](https://codecov.io/gh/mikaelvesavuori/mikrovalid)

[![Maintainability](https://api.codeclimate.com/v1/badges/d42f8b255d893431050f/maintainability)](https://codeclimate.com/github/mikaelvesavuori/mikrovalid/maintainability)

---

MikroValid is the JSON validator that cuts out all the bullshit:

- Dead easy, no proprietary stuff — uses simple JSON objects for schemas and input
- Doesn't pollute your code with "convenient" APIs
- Minimalist approach that will work for the majority of conventional-type objects
- Meant to work effortlessly in both client- and server-side environments
- Tiny (~2.2 KB gzipped), which is ~7-80x smaller than common, popular options
- Zero dependencies
- Has 100% test coverage

## Usage

### Basic importing and usage

```typescript
// ES5 format
const { MikroValid } = require('mikrovalid');
// ES6 format
import { MikroValid } from 'mikrovalid';

const mikrovalid = new MikroValid();

const schema = {
  properties: {
    personal: {
      name: {
        type: 'string'
      },
      required: ['name']
    },
    work: {
      office: {
        type: 'string'
      },
      currency: {
        type: 'string'
      },
      salary: {
        type: 'number'
      },
      required: ['office']
    },
    required: ['personal', 'work']
  },
};

const input = {
  personal: {
    name: 'Sam Person'
  },
  work: {
    office: 'London',
    currency: 'GBP',
    salary: 10000
  }
};

const generatedSchema = mikrovalid.schemaFrom(input); // <-- OPTIONAL: You can also generate a schema directly from your input!

const { success, errors } = mikrovalid.test(schema, input);

console.log('Was the test successful?', success);
```

### Warnings and silent mode

By default you will get warnings and non-critical message output. If you want to silence these message, you can instantiate MikroValid by passing `true` for the `isSilent` option, like so:

```ts
const mikrovalid = new MikroValid(true);
```

### Errors

The `errors` object includes an aggregation of any errors, both those relating to field-level validation and for inline failures emitted when not having required keys or having excess keys.

Since version `1.0.3` both error formats have the same shape:

```json
[{ "key": "blip", "value": 123, "success": false, "error": "Invalid type" }]
```

### Using schemas

The format is inspired by (but is not the same as, nor compliant with) [JSON Schema](https://json-schema.org).

The general shape it uses is:

```json
{
  "properties": {
    "username": {
      "type": "string"
    },
    "required": ["username"]
  }
}
```

A valid input for this particular schema is:

```json
{
  "username": "Sam Person"
}
```

Using the `schemaFrom()` method, you can easily generate schemas for your input. This is especially useful in a programmatic environment in which you can't decide or know before-hand what schema to create. Note that the generated schemas should work for the majority of cases, but you should try this functionality out before relying fully on it.

#### Properties

`properties` is the only **required** root-level object. Each key describes a property of the expected input. In the example, `name` is of the type `string`. Note that you never repeat the `properties` keyword—it's used only in the root.

#### Allowing or disallowing additional properties

By default, unknown properties will be allowed and valid. Setting `additionalProperties` to `false` enables you to disallow any unlisted properties.

Since version `1.0.10` it _only_ works in the direct scope of its location, as per below:

```json
{
  "properties": {
    "first": {
      "type": "string"
    },
    "second": {
      "type": "string"
    },
    "third": {
      "type": "string"
    },
    "additionalProperties": false
  }
}
```

A payload like this...

```json
{
  "first": "the first",
  "second": "the second",
  "third": "the third",
  "fourth": "the fourth"
}
```

...would therefore break the validation.

The same can be done with nested objects, by setting `additionalProperties` in the scope of the object:

```json
{
  "properties": {
    "blip": {
      "type": "string"
    },
    "inside": {
      "type": "object",
      "thing": {
        "type": "string"
      },
      "additionalProperties": false
    }
  }
}
```

So this would not work:

```json
{
  "blip": "beep bloop",
  "inside": {
    "thing": "scary monster",
    "somethingThatIsNotAllowed": "...?"
  }
}
```

#### Required

For each level of nesting, including within objects, a `required` key with an array of strings _may_ be used to describe properties that must exist at that location.

Even the lowest-level `required` will be _within_ the `properties` key after version `1.0.10`.

This example requires both the `personal_data` object, as well as the inner `name` string:

```json
{
  "properties": {
    "personal_data": {
      "type": "object",
      "name": { "type": "string" },
      "required": ["name"]
    },
    "required": ["personal_data"]
  }
}
```

#### Types

The `type` is the only **required** item-level object. Allowed types are:

- `string`
- `number`
- `boolean`
- `object`
- `array`

You can require basic validation of `array` items by setting the expected type in `items.type`:

```json
{
  "properties": {
    "books": {
      "type": "array",
      "items": {
        "type": "object"
      }
    }
  }
}
```

For this schema, a valid input could for example be something like:

```json
{
  "books": [{ "author": "Cormac McCarthy" }, { "author": "William Blake" }]
}
```

Note that this will not work for mixed arrays or for any deeper inspection of object properties.

#### Multiple types

You can also pass in an array of types if you want to verify that the input corresponds to at least one valid type.

```json
{
  "properties": {
    "field": {
      "type": ["string", "boolean", "number"]
    }
  }
}
```

#### Formats

You can use a number of special keywords to specify expectations on the input. These are:

- `alphanumeric`
- `date` (YYYY-MM-DD)
- `email`
- `hexColor`
- `numeric`
- `url`

Usage is as simple as:

```json
{
  "properties": {
    "username": {
      "type": "string",
      "format": "email"
    }
  }
}
```

#### Deeply nested objects

This example shows 3 levels of nesting with objects.

```json
{
  "properties": {
    "things": {
      "type": "object",
      "required": ["nestedThings"],
      "nestedThings": {
        "type": "object",
        "required": ["deeperThings"],
        "deeperThings": {
          "type": "object",
          "required": ["something"],
          "something": "number"
        }
      }
    },
    "required": ["things"]
  },
}
```

#### Minimum length

```json
{
  "properties": {
    "username": {
      "type": "string",
      "minLength": 20
    }
  }
}
```

#### Maximum length

```json
{
  "properties": {
    "username": {
      "type": "string",
      "maxLength": 2
    }
  }
}
```

#### Minimum value

```json
{
  "properties": {
    "phone": {
      "type": "number",
      "minValue": 1000
    }
  }
}
```

#### Maximum value

```json
{
  "properties": {
    "phone": {
      "type": "number",
      "minValue": 1000
    }
  }
}
```

#### Matches regular expression pattern

You can provide your own regular expressions to match for.

```json
{
  "properties": {
    "runtime": {
      "type": "string",
      "matchesPattern": /^(nodejs20|python3.7)$/
    }
  }
}
```

## License

MIT. See `LICENSE` file.