All Articles

A Guide To Creating Joi Schemas From JavaScript Objects


Machine

Problem

Summary

You want to create a joi schema from a JavaScript object e.g. an existing API response, but you really don’t want to type out each key’s validation rules one-by-one.


Solution

Summary

Use joi-machine.

joi-machine is great plugin that creates joi schemas directly from a given JavaScript object.

Let’s take the example where you want to create joi schemas for existing APIs. All you need to do to is call your own API and feed the response into joi-machine - it will do the rest for you**.

Here’s how to get it setup in TypeScript.

npm i joi-machine concat-stream js-beautify --save npm i @types/concat-stream @types/js-beautify --save-dev

Code Example

import concatStream from 'concat-stream';
// @ts-ignore -- no types are available for joi-machine
import joiMachine from 'joi-machine';
import jsBeautify from 'js-beautify';
import fs from 'fs';

interface GenericStringKeyObject { [key: string]: unknown }
interface GenericNumberKeyObject { [key: number]: unknown }
interface CreateSchemaOptions {
  stdOut: boolean;
  fileNameAndPath: string;
}

let stdOut: boolean | undefined;
let fileNameAndPath: string | undefined;

// Callback function to prettify the output
const handleOutput = (data: Buffer) => {
  const beautifiedJoiSchema = jsBeautify(data.toString())

  if (stdOut) {
    console.log(beautifiedJoiSchema);
  }

  if (fileNameAndPath) {
    fs.writeFileSync(fileNameAndPath, beautifiedJoiSchema)
  }
}

// Generate the joi schema from an incoming object
export const generateSchema = async (objToSchemify: GenericStringKeyObject | GenericNumberKeyObject, options?: CreateSchemaOptions) => {
  stdOut = options?.stdOut
  fileNameAndPath = options?.fileNameAndPath

  const generator = joiMachine.obj()
  generator.pipe(concatStream({ encoding: 'string' }, handleOutput))
  generator.write(objToSchemify)
  generator.end()
}

// My JavaScript object
const myObj = {
  stringKey: 'string',
  numberKey: 1,
  objectKey: {
    str: 'str'
  },
  arrayKey:[
    'string1',
    'string2'
  ]
}

generateSchema(myObj)

If you run the file, the following schema will be output to your chosen file and / or the terminal:

Joi.object().keys({
    stringKey: Joi.string(),
    numberKey: Joi.number().integer(),
    objectKey: Joi.object().keys({
        str: Joi.string()
    }),
    arrayKey: Joi.array().items(Joi.string(), Joi.string())
})

You’ll see here that the entire joi validation schema has been created for strings, numbers, objects and arrays.

I’ve published the code above as an npm package at joi-schema-to-file

** joi-machine creates a validation schema for every item in an array even if each of those items is identical; if items are similar (e.g. objects in an array with similar keys) it also creates a new schema for every entry in the array. This results in some manual de-duplication and merging if keys are unpredictable.

** If a key has a null value, you will get a joi.any


Problem Solved

If you have any questions you’d like to ask me about this post, feel free to reach me on Twitter or Github.