So, you have written a large OpenAPI spec. At this point, you may have considered dividing it into smaller separate files, but you don't know where to start. While there are a few articles on the internet on how to split an OpenAPI specification, there aren't many recommendations on how to structure the project.

In this article, I break the typical PetStore example into smaller files, so that you can do the same with your specification.

Note: Are you starting a new documentation project from scratch? At the end of the post you will find a boilerplate ready to be adapted.

1. Reusing definitions

It is easier to start splitting an OpenAPI spec if you are already reusing definitions. Imagine that you have documented a couple of endpoints when you notice that you are defining the same response twice.

paths: /pets/{petId}: get: summary: Info for a specific pet operationId: showPetById parameters: - name: petId in: path required: true description: The id of the pet to retrieve schema: type: string responses: '200': description: Expected response to a valid request content: application/json: schema: type: object required: - id - name properties: id: type: integer format: int64 name: type: string tag: type: string

Following this example, you could define a general Pet object under component/schemas and reuse the DTO for other responses with the keyword $ref.

paths: /pets/{petId}: get: summary: Info for a specific pet operationId: showPetById parameters: - name: petId in: path required: true description: The id of the pet to retrieve schema: type: string responses: '200': description: Expected response to a valid request content: application/json: schema: $ref: "#/components/schemas/Pet" components: schemas: Pet: type: object required: - id - name properties: id: type: integer format: int64 name: type: string tag: type: string

Let's make the petId parameter reusable as well. In the following example, we are moving the parameter from the path to the definitions sections, using once again$ref to import the object.

paths: /pets/{petId}: get: summary: Info for a specific pet operationId: showPetById parameters: - $ref: '#/components/parameters/petId' [...] components: parameters: petId: name: petId in: path required: true description: The id of the pet to retrieve schema: type: string

2. Importing definitions from a file

Now that you are reusing definitions, the file will be shorter without having started the split yet. At this moment, you might already be pleased with the organization of the file, so you could decide to end reading the article here. However, if you want to organize the OpenAPI specification by moving parts of the spec into other files, keep reading!

Do you remember the keyword we have been using to reuse content? $ref also permits us to import parts of the specification from other sources.

Here's an example: Create a new file schemas/Pet.yaml for Pet object and move the definition to the new file.

// schemas/Pet.yaml type: object required: - id - name properties: id: type: integer format: int64 name: type: string tag: type: string

Now, point $ref to the new file's location.

// openapi.yaml $ref: "./schemas/Pet.yaml"

Do the same for the other schemas and parameters that you might want to split into separate files.

3. Dividing, even more, the specification

Until now, we have seen how mainly schemas and responses can be reorganized. But if you have several endpoints, you will continue having a large file.

To organize the paths in multiple files, create a file per path. Taking /pets/{petId} path as example:

//paths/showPeyById.yaml get: summary: Info for a specific pet operationId: showPetById parameters: - $ref: '#/components/parameters/petId'

Then, import each path definition from the main OpenAPI spec as we have been doing with the schemas and parameters.

4. Structuring the project

Let's go one step further. But first, I have to warn you before continuing that:

Warning: This section does not follow the OpenAPI specification recommendations.

It is possible to split even more our project to achieve a better organization, to end having a tiny main file like the following one.

// openapi.yaml openapi: "3.0.0" info: version: 1.0.0 title: Swagger Petstore description: Multi-file boilerplate for OpenAPI Specification. license: name: MIT servers: - url: http://petstore.swagger.io/v1 paths: $ref: "./paths/_index.yaml" components: parameters: $ref: "./parameters/_index.yaml" schemas: $ref: "./schemas/_index.yaml" responses: $ref: "./responses/_index.yaml"

This could be achieved by defining _index.yaml files are containing the definitions for each section. For instance, ./schemas/_index.yaml would look like:

Pet: $ref: "./Pet.yaml" Pets: $ref: "./Pets.yaml" Error: $ref: "./Error.yaml"

The official docs consider this approach as wrong because the $ref keyword should only be used to import objects that support the use of the keyword explicitly, and this is not the case. However, there should not be any issues with other tools that follow the specification strictly because, in the next section, we are compiling all the files back into one.

5. From many files to one, again

Some of the tooling related to OpenAPI supports just a single file. To continue using the spec with those tools, it is interesting to compile all the different files we have created with the help of a command-line tool.

1. Open a new terminal. Then, install the package swagger-cli globally:

npm install -g swagger-cli

2. Run the command to merge all the files into one:

swagger-cli bundle openapi3.yaml --outfile _build/openapi3.yaml --type yaml

Putting all together

No one really wants to do a change in a file with +5000 lines of code without tests 🙈. By splitting a large OpenAPI spec into multiple files, your project becomes much more maintainable and the documentation journey enjoyable. In my case, I have also noticed that other developers are more willing to contribute and propose changes to the document when this is properly organized.

Lastly, I'm sharing with you a repository with the PetStore example divided into multiple files.

Repository: openapi-boilerplate

Feel free to reuse the project to define your OpenAPI spec or to review how everything described in this article has been put into practice.

Do you want to make your OpenAPI specification maintainable? Send me a message, and let's find out how we can enhance your documentation process together.