Ali Raza November 17, 2022

One of the most popular formats available to represent RESTful APIs is OpenAPI v3.0. Due to its high adoption rate, we notice great tooling support for this format where you can get varied services for your API. 

Such as, you can,

    • Auto-generate the client libraries (SDKs) in different languages for the API which abates the integration process for the developers.
    • Generate the developer experience portal for your API that helps developers understand the API better which in turn improves your API adoption rate.
    • Auto-generate the servers based on the OpenAPI spec if you are following the design-first approach.

There is a lot more that you can do with OpenAPI 3.0. Having said that, 

if your OpenAPI 3.0 specification is not compliant with the standards described by the OpenAPI initiative, you may come unstuck in utilizing all these tools and services.

This blog primarily focuses on  the best practices to write a better OpenAPI specification, leading to better API consumption and integration.

Therefore, in no time, we will be covering 14 best practices that one may follow in order to create an absolute OpenAPI specification for API consumption, assuming the specification already conforms to the OpenAPI standards.

1. No Empty Servers List

Make sure that the servers property is specified in the OpenAPI root object and that it is not null or empty. Server information is crucial for your API and it defaults to localhost or example domain in some tools that limit end users to consume your API.

Non-Compliant Example

openapi: 3.0.3

servers: []

Compliant Example

openapi: 3.0.3

servers:
- url: https://development.gigantic-server.com/v1
description: Development server

2. Titles, Names, And Summaries Should Not Exceed 50 Characters

Long titles, names, and summaries can compromise the user-friendliness of the generated developer portal. Moreover, some client (SDK) generators name code components after these contents. As a result, longer content can lead to issues in the code generation process. 

It's best to keep the length of the following properties to a maximum of 50 characters, wherever possible: 

    • Info Title
    • Server Variable Name
    • OperationId
    • Parameter Name
    • Header Name
    • Global Component Name
    • Schema Title
    • Schema Property Name
    • Security Scope Name
    • Security Scheme Name
    • Summary

Non-Compliant Example

openapi: 3.0.3
info:
title: This is a long info title which exceeds 50 characters length
version: …

Compliant Example

openapi: 3.0.3
info:
title: Info title length less than 50 characters
version: …

3. No Inline Schemas Definition

Inline schemas are schemas defined within a component, such as parameter, requestBody, response, and so on, but are not declared globally in the components/schemas section. 

In general, inline schemas are not encouraged in code generators and API portal generators since their names are inferred from the parent node in which they are defined inline. 

Names inferred in this manner may not be user-friendly and may conflict with other names resulting in name duplications. As a result, it is recommended that the schemas be defined globally in the components/schemas section with unique names and be used as references throughout the specification.

Non-Compliant Example

openapi: 3.0.3

paths:
/pet:
post:

requestBody:

content:
application/json:
schema:

required:
- name
type: object
properties:
id: …
name: …
photoUrls: …

Compliant Example

openapi: 3.0.3

paths:
/pet:
post:

requestBody:

content:
application/json:
schema:
$ref: "#/components/schemas/Pet"
components:

schemas:

Pet:

required:
- name
type: object
properties:
id: …
name: …
photoUrls: …

4. No Missing Example(s)

It is recommended that any examples given for parameters, media types, or schemas not be empty and null. Examples demonstrate the intended payload that an object is supposed to accept and can also be used to call an API endpoint. 

Additionally, some tools use these examples to automatically generate test cases for the API so make sure that the components above specify either a single or multiple example(s) using example or examples property, respectively.

Non-Compliant Example

openapi: 3.0.3

paths:
/pet:
put:

responses:
'200':

content:
description: …
application/json:
schema:
$ref: '#/components/schemas/Pet'
components:
schemas:
Pet:
required:
- name
type: object
properties:
id:
type: integer
format: int64
name:
type: string

Compliant Example

openapi: 3.0.3

paths:
/pet:
put: …
responses:
'200':
… content:
description: …
application/json:
schema:
$ref: '#/components/schemas/Pet'
examples:

value:
id: 10
name: doggies
components:
schemas:
Pet:
required:
- name
type: object
properties:
id:
type: integer
format: int64
example: 10
name:
type: string
example: doggie

5. No Invalid Examples 

Just specifying examples is not sufficient, they must be valid and conform to the schemas they are referring to; otherwise invalid examples may cause problems with some tooling or may even be ignored. 

Make sure that the examples you provided in the specification are valid and conform to the respective schemas.

Non-Compliant Example

components:
schemas: Pet:
required:
- name
type: object
properties:
id:
type: integer
format: int64
minimum: 50
example: 10
name:
type: string
minLength: 4
example: Ali

Compliant Example

components:
schemas:
Pet:
required:
- name
type: object
properties:
id:
type: integer
format: int64
minimum: 50
example: 500
name:
type: string
minLength: 4
example: Ali Raza

6. At Least One Security Scheme

In order to protect itself from outside cyberattacks, an API should define an authentication scheme and should authenticate endpoint requests made by end users. For OpenAPI specification, it is recommended to define at least one authentication scheme globally using the securitySchemes property in the components section and use it globally to authenticate all the endpoints or use it for specific endpoints.

Non-Compliant Example

openapi: 3.0.3
info: …
servers: …
paths: …
components:
schemas: …

Compliant Example

openapi: 3.0.3

security:
- petstore_auth:
- write:pets
- read:pets
paths: …
components:
securitySchemes:
petstore_auth:
type: oauth2
flows:
implicit:
authorizationUrl: https://petstore3.swagger.io/oauth/authorize
scopes:
write:pets: modify pets in your account
read:pets: read your pets

7. OperationId Is Required

A unique identifier must be specified for each Operation Object in the specification. This id is used by tools such as APIMatic Code Generator, APIMatic's API Developer Portal, and others to generate code components associated with an operation.

Non-Compliant Example

openapi: 3.0.3

paths:

/pet:
put:
tags: …
summary: …
description: …
requestBody: …
responses: …
security: …

Compliant Example

openapi: 3.0.3

paths:

/pet:
put:
tags: …
summary: …
operationId: updatePet
description: …
requestBody: …
responses: …
security: …

8. No Invalid OperationId

As stated above, in a complete API specification, an operationId serves as a unique identifier for an Operation Object. 

    • The id must be short and code-friendly. Only letters, numbers, underscores, and dashes should be used. 
    • There cannot be any leading or trailing white space in it.
    • Any prohibited filename characters are also not allowed because these ids are frequently used to name the files created as part of the code generation process.
    • Avoid using any erroneous characters because this id can also be used to build URLs in a portal generator.

Non-Compliant Example

openapi: 3.0.3

paths:

/pet:
put:
tags: …
summary: …
operationId: #{invalid<id>}
description: …
requestBody: …
responses: …
security: …

Compliant Example

openapi: 3.0.3

paths:

/pet:
put:
tags: …
summary: …
operationId: valid-id
description: …
requestBody: …
responses: …
security: …

9. 2XX Response Present for GET Operation 

GET operations are supposed to return some data upon a successful endpoint call. If the response status code is in the 2XX range (except 204), the content that is expected to be returned should be defined using the content property.

Non-Compliant Example

openapi: 3.0.3

paths:

/pet/findByStatus:
get:

responses:
'400':
description: Invalid status value

Compliant Example

openapi: 3.0.3

paths:

/pet/findByStatus:
get:

responses:
'200':

content:
description: …
application/json:
schema:
$ref: '#/components/schemas/Pet'

10. At Least One Operation Level Tag

Some tools use operation level tags in order to logically group all the operations. Tags facilitate operations discovery and consequently ease API consumption. The Operations Object must specify at least one or more tags using the tags property.

Non Compliant Example

openapi: 3.0.3

paths:

/pet:
put:
summary: …
operationId: …
description: …
requestBody: …
responses: …
security: …

Compliant Example

openapi: 3.0.3

tags:
- name: pet
description: Everything about your Pets
externalDocs:
description: Find out more
url: http://swagger.io
paths:

/pet:
put:
tags:
- pet

11. Parameters Should Be in Order

For Code Generators, it is recommended that optional parameters be specified after all the required parameters have been specified and not before or between required parameters.

Non Compliant Example

openapi: 3.0.3

paths:

/dummyEndpoint:
get:
parameters:
- name: param1

required: false
- name: param2

required: true
- name: param3

required: true
- name: param4

required: false

Compliant Example

openapi: 3.0.3

paths:

/dummyEndpoint:
get:
parameters:
- name: param2

required: true
- name: param3

required: true
- name: param1

required: false
- name: param4

required: false

12. Add Descriptions to the API Components

Adding descriptions to the OpenAPI components enables you to generate a descriptive API developer portal, detailed documentation, and much more for your API, which facilitates the developers to understand the API more easily.

Therefore, descriptions should be specified for the following OAS properties.

    • Info Description
    • Operation Description
    • Parameter Description
    • Response Description
    • Schema Description

And make sure that descriptions are not null or empty.

Non-Compliant Example

openapi: 3.0.3
info:
title: …
version: …

Compliant Example

openapi: 3.0.3
info:
title: …
version: …

description: Dummy info description.

13. Add Contact Information

An API specification must specify contact information using the Info Object’s Contact property. This information helps users who could run into problems using your API e.g. you can include contact details of your support team that your API consumers can reach out to.

Non Compliant Example

openapi: 3.0.3
info:
title: …
version: …

Compliant Example

openapi: 3.0.3
info:
 title: …
version: …

contact:
email: apiteam@swagger.io

14. Reuse Components to Avoid Huge OpenAPI Files

When you've finished writing the OpenAPI spec for a large or complex API, you may end up with a huge specification that makes it difficult to make any changes in the file.

In order to make your API specification maintainable and scalable, you need to reuse the components across your API specification file. You can simply specify the following properties globally in the components section and reuse them. This way you can avoid redundancy in the file and will end up with concrete information.

    • Schemas
    • Responses
    • Parameters
    • Examples
    • RequestBodies
    • Headers
    • SecuritySchemes
    • Links
    • Callbacks

It is quite possible that even by reusing the components across the file, you still have a large API specification file. In such cases, you need to split your single specification file into multiple files. You need to

    1. Exclude the global components such as schemas, responses, parameters, etc. in separate files with schemas.yaml/json, responses.yaml/json, parameters.yaml/json, etc. names respectively and modify their references.
    2. Create separate files for each endpoint route (path) and group them in directories.
    3. Reference these endpoint files in the root API specification file.

Split files structure for the Petstore API will look something like the following

.
├── api.yaml (root file)
├── paths/
│ ├── pet/
│ │ ├── pet-update.yaml
│ │ ├── pet-save.yaml
│ │ ├── pet-findByStatus.yaml
│ │ ├── pet-findByTags.yaml
│ │ ├── pet-findbyId.yaml
│ │ └── ...
│ ├── store/
│ │ ├── store-inventory-get.yaml
│ │ ├── store-order-save.yaml
│ │ └── ...
│ └── user/
│ ├── user-create.yaml
│ ├── user-createWithList.yaml
│ └── ...
└── components/
├── schemas.yaml
├── responses.yaml
├── parameters.yaml
├── examples.yaml
├── requestBodies.yaml
├── headers.yaml
├── securitySchemes.yaml
├── links.yaml
└── callbacks.yaml

You can find the split-out OpenAPI here.

These were the top practices that would help you improve your OpenAPI specification and would enable you to generate better client libraries (SDKs) and developer portals for your API using APIMatic’s Code Generator and Portal Generator respectively.

Visit www.apimatic.io or contact us for more information.