In the previous article, I wrote about our approach to development centered around the OpenAPI schema.
A common pain point when writing OpenAPI or Swagger files is that the file tends to balloon in size relative to its actual content. (There aren't that many endpoints, yet it ends up hundreds of lines long 😇)
I consider this problem of files growing too large to be fairly serious, so I've been thinking about splitting the schema into multiple files using $ref.
That said, it seems like there currently aren't many tools that integrate with OpenAPI and can resolve file references, so I've also been thinking it might be more convenient to keep everything in a single file after all.
So, I tried out a method for managing the schema as split files while ultimately combining them into a single schema.
Managing the schema by splitting it into files
First, let's look at how to manage the schema by splitting it into files. I've put together an example schema in a repository, which I'll use as a reference here.
The starting point is root.yml, and the other files are organized into directories according to the namespaces of the OpenAPI Object, with each file referencing others via relative paths.
From root.yml, we reference the files defined for each path. Definitions for /users go into ./paths/users.yml, and definitions for /users/{user_id} go into ./paths/users-by-id.yml.
# root.yml
paths:
/users:
$ref: ./paths/users.yml
/users/{user_id}:
$ref: ./paths/users-by-id.yml
Under each path, the parameters and responses for each method are described.
# paths/users.yml
get:
operationId: usersGet
summary: get user list
tags: [user]
parameters:
- $ref: ../components/parameters/page.yml
- $ref: ../components/parameters/per_page.yml
responses:
200:
description: ok
content:
application/json:
schema:
$ref: ../components/schemas/user_list.yml
post:
operationId: usersPost
summary: create new user
tags: [user]
requestBody:
description: user information
required: true
content:
application/json:
schema:
$ref: ../components/schemas/user_input.yml
responses:
201:
description: created
content:
application/json:
schema:
$ref: ../components/schemas/user.yml
Commonly used parameters reference files defined under components/parameters, and objects returned in request bodies or responses reference files defined under components/schemas.
Splitting files this way makes it clear where to add or edit things when you need to update the schema.
Combining the schema
The approach described so far makes the schema easier to manage, but ultimately it's nice to have the schema as a single file.
So, we use openapi-generator to combine the schema.
openapi-generator can generate client-facing code and the like based on a schema, and if you look at the generator types available via the command, there are openapi and openapi-yaml (not mentioned in the README?).
DOCUMENTATION generators:
- cwiki
- dynamic-html
- html
- html2
- openapi
- openapi-yaml
Using this generator, you can produce a schema in JSON or YAML with all file references expanded, based on root.yml.
# Output the combined schema in JSON format
$ openapi-generator generate -g openapi -i root.yml -o generated
# Output the combined schema in YAML format
$ openapi-generator generate -g openapi-yaml -i root.yml -o generated
This lets us manage the schema as split files while ultimately being able to output it as a single combined schema. As far as I can tell, the file references are being expanded correctly.
A side note
Is OpenAPITools official?
Some articles introduce openapi-generator as if it were an official tool, but if you look at the GitHub organization, it says the following:
NOTE: This organization is not affiliated with OpenAPI Initiative (OAI)
So it doesn't appear to be official, but development seems active, and it seems to be relatively widely used among similar tools, so for now I plan to keep using it.
Is it painful to write a schema once you've split it into files?
Using Swagger Editor or various editor plugins makes editing easier, since they validate the schema and offer key completion as you write.
However, once you split the schema into files, no single file constitutes a valid OpenAPI schema on its own, so it can be tough unless you already have a decent understanding of how to write a schema.
At the point where openapi-generator combines the files, obvious schema errors become apparent, but not being able to tell before then is a bit painful.
Right now I run the generator every time the schema changes, so it would be nice to be able to detect schema changes and edit with a live preview.
- Addendum
I tried using chokidar to detect changes to the split schema files and run openapi-generator.
Running the watch npm script below will detect changes to the schema definition YAML files and generate the combined schema. Please install the necessary packages such as openapi-generator and npm-run-all.
{
"scripts": {
"validate-generated-schema": "openapi-generator validate -i generated/openapi/openapi.yaml",
"run-generator": "openapi-generator generate -g openapi-yaml -i root.yml -o generated",
"generate": "npm-run-all -s run-generator validate-generated-schema",
"watch": "chokidar 'root.yml' '**/*.yml' -c 'yarn generate'"
}
}
Also, if you want to edit the schema as a single file, Stoplight Studio looks like a good option too.