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

json-2-joi

v3.0.1

Published

Converts json-schema to Joi schema. (Enhanced enjoi)

Downloads

8

Readme

Test npm version

json-2-joi

Converts a JSON schema to a Joi schema for object validation.

Change based on the forked version (enjoi):

  • Provided refineDescription(schema), allowNull, forbidArrayNull, strictRequired, strictEnum, enableEnum, customizedNullValues options

  • Change cycle reference resovling without self-generating id and rely on $id if defined in JSON schema

  • Support const keyword in JSON schema

  • Support example setting for Joi based on JSON schema's examples, default or enum

  • Support contains keyword in JSON schema using Joi's has method

  • Support $anchor keyword in JSON schema

  • Support providing joiOptions for JOI default instance creation.

  • Support directly passing a Joi instance for schema building.

  • Remove defaults API. Please refer to section Reuse JOI Schema Resolver for similar usage.

  • Remove strictMode and useDefaults option.

  • Big Change on $ref and subSchemas. Please refer to test/test-references.js for detail usage. Currently, four format of $ref are supported:

    • id
    • [baseUri]#anchor
    • [baseUri]#/$defs/shared
    • [baseUri]#/$defs/shared/properties/level1

    subSchemas will be modified for two reason:

    • If any subSchema does not have $id field, it will be added using the corresponding key in subSchemas object.
    • New key-value pair will be added into it if the schema or any its subschema being parsed has $id field.

Usage

Schema Support

json-2-joi does not support all of json-schema.

Here is a list of some known missing keyword support still being worked on:

  • object:patternProperties - unsupported due to Joi limitations.

Please file issues for other unsupported features.

API

  • json2Joi.schema(schema [, options, joiInstance])
    • schema - a JSON schema or a string type representation (such as 'integer').
    • options - an (optional) object of additional options such as subSchemas and custom types.
    • joiInstance - a (optional) Joi instance to be used. When passed, it will be used directly. extensions option does not take effect and the Object/Array auto coerce extensions will not be included as well.
  • json2Joi.resolver(options [, joiInstance]) - Creates a schema resolver based on options. joiInstance is same as above.

Options

  • subSchemas - an (optional) object with keys representing schema ids, and values representing schemas.
  • extensions - an array of extensions to pass joi.extend.
  • joiOptions - Options directly passed to Joi for instance creation. If the noDefaults is set true, default() will NOT be called and applied to Joi schema.
  • refineDescription(schema) - an (optional) function to call to apply to the JSON schema so that it's possible to return customized description.
  • refineType(type, format) - an (optional) function to call to apply to type based on the type and format of the JSON schema.
  • refineSchema(joiSchema, jsonSchema) - an (optional) function to call to apply to adjust Joi schema base on the original JSON schema. Primary use case is handling nullable flag in OpenAPI 3.0
  • allowNull - Default as false. When true and the field has no enum setting, null value will be allowed.
  • customizedNullValues - Default as [null]. When provided, the value in the array will be treated valid null value when schema is 'null' type or other allowing null case.
  • forbidArrayNull - Default as true. When false and allowNull is true, null value will be allowed for array type field.
  • strictArrayRequired - Default as false. When true, those required array-type fields must has at least 1 item and its item must not be null item, or value defined by customizedNullValues.
  • strictRequired - Default as false. When true, null (and empty string additionally for string type) will NOT be allowed for those required field in object.
  • strictEnum - Default as true. When false, and allowNull is true, null (and empty string additionally for string type) will be added to enum list.
  • enableEnum - Default as true. When false, enum restriction will not be added to Joi Schema.

Example:

const Joi = require('joi');
const Json2Joi = require('json-2-joi');

const schema = Json2Joi.schema({
    type: 'object',
    properties: {
        firstName: {
            description: 'First name.',
            type: 'string'
        },
        lastName: {
            description: 'Last name.',
            type: 'string'
        },
        age: {
            description: 'Age in years',
            type: 'integer',
            minimum: 1
        }
    },
    'required': ['firstName', 'lastName']
});

const { error, value } = schema.validate({firstName: 'John', lastName: 'Doe', age: 45});

joiOptions

When this option is provided, a Joi instance will be created as joiInstance = Joi.defaults((schema) => schema.options(joiOptions)); for building Joi Schema.

As a result:

  • unknown setting (originally set by schema.additionalProperties !== false) of the schema will take effect only if allowUnknown is NOT provided in joiOptions.
  • Original useDefaults option will be replaced by noDefaults in joiOptions.
  • Original strictMode option will be replaced by convert in joiOptions.

Sub Schemas

Sub-schemas can be provided through the subSchemas option for $ref values to lookup against.

Example:

const schema = Json2Joi.schema({
    $id: 'top',
    type: 'object',
    properties: {
        product: {
            $ref: 'prod'
        },
        suggestions: {
            type: 'array',
            items: {
                type: 'object',
                properties: {
                    product: {
                        $ref: 'product'
                    },
                    weight: {
                        $ref: 'measurement'
                    }
                }
            }
        }
    }
}, {
    subSchemas: {
        measurement: {
            type: 'object',
            properties: {
                quantity: {
                    $anchor: 'subNumber',
                    type: 'number'
                },
                unit: {
                    type: 'string'
                }
            }
        },
        product: {
            $id: 'prod',
            type: 'object',
            properties: {
                isUsed: {
                    $anchor: 'subBoolean',
                    type: 'boolean'
                },
                isDangerous: {
                    $ref: '#subBoolean'
                },
                width: {
                    $ref: 'measurement'
                },
                length: {
                    $ref: '#/properties/width'
                },
                price: {
                    $ref: 'measurement#subNumber'
                }
            }
        }
    }
});

Reuse JOI Schema Resolver

Sometimes, we might have a base set of subSchemas which is used for building a large number of JSON schema and so we do not want to parse these common subSchemas again and again. We can first construct a JOI Schema Resolver first and then convert other JSON one by one.


const json2Joi = Json2Joi.resolver({
    subSchemas: {
        measurement: {
            type: 'object',
            properties: {
                quantity: {
                    $anchor: 'subNumber',
                    type: 'number'
                },
                unit: {
                    type: 'string'
                }
            }
        }
    }
});

const weightSchema = json2Joi.convert({
    type: 'object',
    properties: {
        weight: {
            $ref: 'measurement'
        }
    }
});

const lengthSchema = json2Joi.convert({
    type: 'object',
    properties: {
        length: {
            $ref: 'measurement'
        }
    }
});

Override options during conversion

When reusing the Resolver and calling convert API, it's possible that different schema might have different requirement.

It's possible to override these options for each conversion:

  • refineType
  • refineSchema
  • refineDescription
  • allowNull
  • forbidArrayNull
  • strictRequired
  • strictArrayRequired
  • strictEnum
  • enableEnum
  • noDefaults (Copied from joiOptions for the construction time)

However, the overriden options do not have impact on the subSchemas as they are preprocessed during Resolver construction.

const subSchemas = {
    measurement: {
        type: 'object',
        properties: {
            quantity: {
                type: 'number',
                enum: [0, 1]
            }
        }
    }
};
const enjoi = Enjoi.resolver({
    subSchemas,
    enableEnum: false
});

const jsonSchema = {
    type: 'object',
    properties: {
        weight: {
            type: 'object',
            properties: {
                quantity: {
                    type: 'number',
                    enum: [0, 1]
                }
            }
        },
        length: {
            $ref: 'measurement'
        }
    }
};

const schema1 = enjoi.convert(jsonSchema, { enableEnum: true });
const schema2 = enjoi.convert(jsonSchema);

t.ok(schema1.validate({ weight: { quantity: 2 } }).error, 'follow overridden options');
t.ok(!schema1.validate({ length: { quantity: 2 } }).error, 'no impact on subschemas');
t.ok(!schema2.validate({ weight: { quantity: 2 } }).error, 'still use original options');

Custom Types

Custom types can be provided through the extensions option.

const schema = Json2Joi.schema({
    type: 'thing'
}, {
    extensions: [{
        type: 'thing',
        base: Joi.any()
    }]
});

Also with functions.

const schema = Json2Joi.schema({
    type: 'thing'
}, {
    extensions: [{
        type: 'thing',
        validate(value, helpers) {
            if (value !== 'foobar') {
                return { value, errors: helpers.error('thing.foobar') };
            }
        },
        messages: {
            'thing.foobar': '{#label} must be \'foobar\''
        }
    }]
});

Refine Type

You can use the refine type function to help refine types based on type and format. This will allow transforming a type for lookup.

const schema = Json2Joi.schema({
    type: 'string',
    format: 'email'
}, {
    extensions: [{
        type: 'email',
        base: Joi.string().email()
    }],
    refineType(type, format) {
        if (type === 'string' && format === 'email') {
            return 'email';
        }
    }
});

This can be used in conjunction with function based extensions for additional logic:

const schemaDesc = {
    type: 'string',
    format: 'email',
    'x-test': true
}
const schema = Json2Joi.schema(schemaDesc, {
    extensions: [{
        type: 'email',
        validate(value, helpers) {
            const validator = schemaDesc['x-test'] ? Joi.string().email().equal('[email protected]') : Joi.string().email();
            const validation = validator.validate(value);
            if (validation.error) {
                return { value, errors: validation.error };
            }
        }
    }],
    refineType(type, format) {
        if (type === 'string' && format === 'email') {
            return 'email';
        }
    }
});

Extensions

Refer to Joi documentation on extensions: https://hapi.dev/module/joi/api/?v=17#extensions