Joi: Is there no way to serialize a Joi schema without losing anything?

Created on 26 Jan 2018  路  9Comments  路  Source: sideway/joi

Hi, how can I serialize a Joi schema? If I convert back/forth from JSON Schema, I feel like there are cases where information may be lost in complex schemas. JSON.stringify(schema) doesn't work either. Seems like there should be some way to do this, but I can't find anything.

support

Most helpful comment

describe() and build(). They are not documented yet but lots of examples in the tests.

This is huge. I currently have a use case where I'm ingesting a variety of survey results from different websites. Each one has different inclusion/exclusion criteria.

With this, I can now store all of my inclusion/exclusion rules as serialized Joi models in my database. That way, I can have a single applicant sorting lambda retrieve the appropriate validation rules and evaluate all entries.

example

    const maxAge = 80;
    var earliestDate = new Date();
    earliestDate.setFullYear(earliestDate.getFullYear() - maxAge );

    // create a valid date example
    var validDate = new Date();
    validDate.setFullYear(validDate.getFullYear() - (maxAge -1) );

    // create our Joi model
    const ExampleModel = Joi.object().keys({
        firstName: Joi.string().optional(),
        lastName: Joi.string().optional(),
        DOB: Joi.date().iso().greater(earliestDate).optional(),
    });

    // create the model description object
    const modelDescription = ExampleModel.describe();
    // demonstrate that we can stringify this object and possibly store it in a DB
    const stringified = JSON.stringify(modelDescription);
    console.log('the stringified model is :', stringified);
    // parse the model schema description into an object again
    const reObjectified = JSON.parse(data);
    // take the schema description object and create a Joi model from it
    const RebuiltModel = Joi.build(reObjectified);

    const invalidParticipant = {
        firstName: "John",
        lastName: "Smith",
        DOB: earliestDate, // applicant is too old
    };
    const validParticipant = {
        firstName: "Jack",
        lastName: "Doe",
        DOB: validDate,
    };
    const invalidValidationResult = RebuiltModel.validate(invalidParticipant);
    if (invalidValidationResult.error) {
        console.log(`${invalidParticipant.firstName} should NOT be allowed to proceed because :`, invalidValidationResult.error);
    } else {
        console.log('An invalid participant was let through!!!!!');
    }

    const validValidationResult = RebuiltModel.validate(validParticipant);
    if (validValidationResult.error) {
        console.log(`${validParticipant.firstName} was rejected!?!?!!?!`);
    } else {
        console.log(`${validParticipant.firstName} should be allowed to proceed`);
    }

You could even store your Joi schema and have your frontend access it to do FE validation. Forget writing your form validation in two places. Just store it and make it available to FE and BE.

All 9 comments

.describe() is your best bet.

How do I deserialize a describe() output?

There is no way to go Joi schema -> serialized description -> re-constituted Joi schema that I am aware of.

I can confirm all that. It's not really joi's job to do serialization, an external module can perfectly accomplish that task if someone cares enough to put in the work.

If someones is looking for a solution, I wrote a module for this: joi-serialization.

It covers any of joi's functionality, except references and extensions. Be aware that in some cases the serialized object cannot be stringified loslessly, since it can contain references like e.g. a Symbol. For symbols there is an option to pass your own Symbol map on deserialization.

@lal12 this is now built-in in v16

@lal12 this is now built-in in v16

I wasn't aware of that, thanks.

Which api? I didn't found it in the docs.

describe() and build(). They are not documented yet but lots of examples in the tests.

describe() and build(). They are not documented yet but lots of examples in the tests.

This is huge. I currently have a use case where I'm ingesting a variety of survey results from different websites. Each one has different inclusion/exclusion criteria.

With this, I can now store all of my inclusion/exclusion rules as serialized Joi models in my database. That way, I can have a single applicant sorting lambda retrieve the appropriate validation rules and evaluate all entries.

example

    const maxAge = 80;
    var earliestDate = new Date();
    earliestDate.setFullYear(earliestDate.getFullYear() - maxAge );

    // create a valid date example
    var validDate = new Date();
    validDate.setFullYear(validDate.getFullYear() - (maxAge -1) );

    // create our Joi model
    const ExampleModel = Joi.object().keys({
        firstName: Joi.string().optional(),
        lastName: Joi.string().optional(),
        DOB: Joi.date().iso().greater(earliestDate).optional(),
    });

    // create the model description object
    const modelDescription = ExampleModel.describe();
    // demonstrate that we can stringify this object and possibly store it in a DB
    const stringified = JSON.stringify(modelDescription);
    console.log('the stringified model is :', stringified);
    // parse the model schema description into an object again
    const reObjectified = JSON.parse(data);
    // take the schema description object and create a Joi model from it
    const RebuiltModel = Joi.build(reObjectified);

    const invalidParticipant = {
        firstName: "John",
        lastName: "Smith",
        DOB: earliestDate, // applicant is too old
    };
    const validParticipant = {
        firstName: "Jack",
        lastName: "Doe",
        DOB: validDate,
    };
    const invalidValidationResult = RebuiltModel.validate(invalidParticipant);
    if (invalidValidationResult.error) {
        console.log(`${invalidParticipant.firstName} should NOT be allowed to proceed because :`, invalidValidationResult.error);
    } else {
        console.log('An invalid participant was let through!!!!!');
    }

    const validValidationResult = RebuiltModel.validate(validParticipant);
    if (validValidationResult.error) {
        console.log(`${validParticipant.firstName} was rejected!?!?!!?!`);
    } else {
        console.log(`${validParticipant.firstName} should be allowed to proceed`);
    }

You could even store your Joi schema and have your frontend access it to do FE validation. Forget writing your form validation in two places. Just store it and make it available to FE and BE.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kevbook picture kevbook  路  4Comments

ashrafkm picture ashrafkm  路  3Comments

normancarcamo picture normancarcamo  路  3Comments

PaunPrashant picture PaunPrashant  路  3Comments

n-sviridenko picture n-sviridenko  路  3Comments