Open Api Specification to Joi objects with oas-to-joi

Open Api Specification to Joi objects with oas-to-joi

In this new article I will talk about a very useful library called "oas-to-joi" which allows to create Joi Schemas Objects using an Open Api Specification. It provides an easy way to get API validations rules automatically.

What is Open Api Specification?

Open Api Specification (OAS) from the Swagger Web Site definition is:

The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to HTTP APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection.

You can get an example of an OAS yaml file here

As a backend developers we use OAS to ensure that our APIs are consistent with the industry standards and easily integrate our applications with other services (like micr front-ends, mobile apps or another backend)

What is Joi ?

As the Joi's team defines it in their web site:

The most powerful schema description language and data validator for JavaScript

To ensure that all request from the API consumer are following the OAS definition, we usually define validations rules which helps us to check that the request complish the API contract. To avoid create manual rules, we can use "Joi" which provides several ways to validate different kind of data and types.

What is oas-to-joi ?

oas-to-joi is an open source nodejs library which takes an OAS yaml file and returns the Joi objects representation to implement the validations rules into the backend.

By using this library we save time and increase our productive because we avoid convert manually the OAS rules into the Joi validations

Now, let walk you through the library:

Project structure

Run the npm init command to create our package json file:

npm init --yes # this will trigger automatically populated initialization with default values

Now, install "Joi", "typescript", "ts-node" and "oas-to-joi" dependencies:

npm install joi
npm install --save-dev ts-node typescript oas-to-joi

We don't need to have oas-to-joi as a "production dependency" because this library was made to use while we are developing the API.

Then, create the following project structure:

.
├── main.ts
├── oas-to-joi-output
├── oas.yml
├── package-lock.json
└── package.json

Before try the library we have to create an OAS file, for this article we are going to use the one from the swagger website, you can copy from here and paste into the oas.yml file.

How to use it?

This library provide two options to be executed:

Option 1

Use the oas-to-joi cli:

  • --oas-file: Use this parameter to set the full path of the OAS file.

  • --output: Use this parameter to set the full path of the destination folder where the library will save the joi schemas.

# adjust the path according your Operating System
oas-to-joi --oas-file /Users/myuser/oas-to-joi-example/oas.yml --output /Users/myuser/oas-to-joi-example/oas-to-joi-output

Option 2

Create a new script in your package.json file:

// adjust the path according your Operating System
"scripts": {
    "oas-to-joi": "oas-to-joi --oas-file /Users/myuser/oas-to-joi-example/oas.yml --output /Users/myuser/oas-to-joi-example/oas-to-joi-output"
  },

After that, execute the new command in the terminal:

npm run oas-to-joi

Now, in the terminal we can see:

================================
 Dumping Joi Files ✨ 
================================

====================================
 Validating Open API Specifications 
====================================
Validation OK:
API name: Swagger Petstore - OpenAPI 3.0, Version:1.0.11 
-----------------------------------------------------------------------

Getting definitions... 
------------------------------
# -> Pet: 0.73 ms
# -> Category: 0.05 ms
# -> Tag: 0.07 ms
# -> Order: 0.32 ms
# -> User: 0.23 ms

Writing files... 
------------------------------
# -> update-pet.schema.ts: 2.38 ms
# -> add-pet.schema.ts: 0.58 ms
# -> place-order.schema.ts: 0.66 ms
# -> create-user.schema.ts: 0.61 ms
# -> create-users-with-list-input.schema.ts: 0.73 ms
# -> update-user.schema.ts: 0.61 ms
# -> category.schema.ts: 0.41 ms
# -> tag.schema.ts: 0.44 ms
# -> pet.schema.ts: 0.58 ms
# -> order.schema.ts: 0.40 ms
# -> user.schema.ts: 0.40 ms
Done (11) Files

And also, the output folder looks like:

.
├── joi
│   ├── add-pet.schema.ts
│   ├── category.schema.ts
│   ├── create-user.schema.ts
│   ├── create-users-with-list-input.schema.ts
│   ├── order.schema.ts
│   ├── pet.schema.ts
│   ├── place-order.schema.ts
│   ├── tag.schema.ts
│   ├── update-pet.schema.ts
│   ├── update-user.schema.ts
│   └── user.schema.ts
└── types
    ├── address.type.ts
    ├── api-response.type.ts
    ├── category.type.ts
    ├── customer.type.ts
    ├── order.type.ts
    ├── pet.type.ts
    ├── tag.type.ts
    └── user.type.ts
  • joi: This folders contains the validations rules which are created using the Joi syntax.

  • types: This folder is a bonus. It contains all the OAS schemas as Typescript types.

If we compare the Joi Schema file with the OAS file component, we can see:

  • Left side is the Open Api Specification of the Category schema

  • Right side is the Joi Schema Object which comes from the Category Schema specifications.

Validate data using Joi

The order.schema only allows quantity as a number and a limited list of status. For this example, we are using an "unknown" status and "string" as quantity.

Add this code into main.ts:

// main.ts:
import schema from "./oas-to-joi-output/joi/order.schema";

const validate = (data: any) => {
  const result = schema.validate(data, { abortEarly: false });
  console.log(result.error);
};

validate({ status: "unkown", quantity: "nan" });

Create a new command in package.json file:

scripts: {
...
"validate": "ts-node main.ts"
}

Run the new command

npm run validate

And the output should be something like that:

[Error [ValidationError]: "quantity" must be a number. "status" must be one of [placed, approved, delivered]] {
  _original: { status: 'unkown', quantity: 'nan' },
  details: [
    {
      message: '"quantity" must be a number',
      path: [Array],
      type: 'number.base',
      context: [Object]
    },
    {
      message: '"status" must be one of [placed, approved, delivered]',
      path: [Array],
      type: 'any.only',
      context: [Object]
    }
  ]
}

Conclusions

By using oas-to-joi library we can save time and effort when we need to "translate" OAS validation rules to javascript. Whenever the API Design is updated, we just run oas-to-joi to create the new version of Joi schemas.

Thanks for reading, see you in the next article.

Did you find this article valuable?

Support Max Martínez Cartagena by becoming a sponsor. Any amount is appreciated!