Writing OpenAPI (Swagger) Specification Tutorial Series - Part 8
Splitting specification file
By Arnaud Lauret, August 2, 2016
With previous posts we have learned to produce an OpenAPI specification containing all OpenAPI specification subtleties. Some specification files may become quite large or may contain elements which could be reused in other APIs. Splitting a specification file will help to keep it maintainable by creating smaller files and also help to ensure consistency throughout APIs by sharing common elements.
Writing OpenAPI (Swagger) Specification Tutorial Series
This tutorial teaches everything about the OpenAPI 2.0 Specification (fka. as Swagger), most of what you’ll read here can still be applied on version 3.
If you’re a bit lost in the specification (version 2 or 3), take a look at the OpenAPI Map:
In previous parts we’ve learned to create highly accurate API description which can become quite large or may contain elements that can be reused, in this eighth part we’ll learn how to split an OpenAPI specification file into smaller and reusable elements.
JSON Pointers
In part 3 - Simplifying spefication file we have learned how to simplify the specification by creating reusable elements. In the example below, the Person
definition is defined once in definitions
and used as
- A body parameter in
POST /persons
- A response schema in
GET /persons/{username}
- A sub-elements in
Persons
definition
swagger: "2.0"
info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification
schemes:
- https
host: simple.api
basePath: /openapi101
paths:
/persons:
get:
summary: Gets some persons
description: Returns a list containing all persons. The list supports paging.
parameters:
- name: pageSize
in: query
description: Number of persons returned
type: integer
- name: pageNumber
in: query
description: Page number
type: integer
responses:
200:
description: A list of Person
schema:
$ref: "#/definitions/Persons"
post:
summary: Creates a person
description: Adds a new person to the persons list.
parameters:
- name: person
in: body
description: The person to create.
schema:
$ref: "#/definitions/Person"
responses:
204:
description: Persons succesfully created.
400:
description: Persons couldn't have been created.
/persons/{username}:
get:
summary: Gets a person
description: Returns a single person for its username.
parameters:
- name: username
in: path
required: true
description: The person's username
type: string
responses:
200:
description: A Person
schema:
$ref: "#/definitions/Person"
404:
description: The Person does not exists.
definitions:
Person:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
Persons:
type: array
items:
$ref: "#/definitions/Person"
To use the Person
definition in these different places, we use a JSON Pointer (defined by RFC6901):
This pointer describes a path in the document, pointing to Person
in definitions
which is as the root (#
) of the current document.
But JSON pointers are not only meant to point something within the current document, they can be used to reference something in another document.
Basic splitting
Let’s see how we can split the file we created in part 3
Referencing a local file
We can create a person.yaml
file containing the Person
definition:
Person:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
Then we can remove the Person
definition in definitions
and replace all existing references to person #definitions/Person
by a reference to Person
in the person.yaml
file:
swagger: "2.0"
info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification
schemes:
- https
host: simple.api
basePath: /openapi101
paths:
/persons:
get:
summary: Gets some persons
description: Returns a list containing all persons. The list supports paging.
parameters:
- name: pageSize
in: query
description: Number of persons returned
type: integer
- name: pageNumber
in: query
description: Page number
type: integer
responses:
200:
description: A list of Person
schema:
$ref: "#/definitions/Persons"
post:
summary: Creates a person
description: Adds a new person to the persons list.
parameters:
- name: person
in: body
description: The person to create.
schema:
$ref: "person.yaml#/Person"
responses:
204:
description: Persons succesfully created.
400:
description: Persons couldn't have been created.
/persons/{username}:
get:
summary: Gets a person
description: Returns a single person for its username.
parameters:
- name: username
in: path
required: true
description: The person's username
type: string
responses:
200:
description: A Person
schema:
$ref: "person.yaml#/Person"
404:
description: The Person does not exists.
definitions:
Persons:
type: array
items:
$ref: "person.yaml#/Person"
Editing splitted local files with the online editor
Tools will look for the referenced file (person.yaml
) in the same directory as the file containing the reference.
But when you use the online editor it does not make sense, such local reference cannot be resolved.
Fortunately the editor propose a configuration allowing to set a server to resolve these local references. This configuration is accessible in the Preferences->Preferences menu:
The Pointer Resolution Base Path configuration is on the bottom of the configuration screen:
All you need to do is put the referenced yaml files into a web server (with CORS activated) and modify the editor’s configuration to point this server.
http-server a lightweight web server
You can use http-server a lightweight node.js web server:
You now can start a web service on any folder:
or
By default, http-server
listens on 8080 port, files will be accessble through http://localhost:8080/<path to file within folder>
.
The --cors
flag is used to activate CORS directive and allow XHR request to this local webserver from the online editor page.
Without CORS activated, the editor will not be able to download files.
Modifying online editor configuration
Once the web server is started you can modifiy the editor to set the URL to http://localhost:8080/
:
Files referenced with $ref: <filename>#/example
will be downloaded from http://localhost:8080/<filename>
.
Once this is done, you may need to do a force refresh (clean cache) of the editor’s the page to get the green bar.
Cache
When you add a reference to a new file (that was not already reference), this new file may not be downloaded automatically resulting in errors (Reference could not be resolved: newfile.yaml
). You may need to refresh the editor’s page with cache cleaning to solve this error.
Folders
You’re under no obligation to put all sub-files on a “root” level, you can store them in differents sub-folders.
Reference to a file in a folder
Let’s move the person.yaml
file into a sub-folder folder
, the new reference will be like this:
File referencing a file from an upper folder
You can also reference a file outside the current folder. Let’s create a persons.yaml
file into a another-folder
folder:
This file reference the person.yaml
file using ..
to get to the upper level.
To use persons.yaml
, we proceed just like with person.yaml
. We remove the Persons
definition from the main file and we replace its reference #/definitions/Persons
by another-folder/persons.yaml#Persons
.
Here’s the full file with all definitions externalized (the definitions
section has been removed):
swagger: "2.0"
info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification
schemes:
- https
host: simple.api
basePath: /openapi101
paths:
/persons:
get:
summary: Gets some persons
description: Returns a list containing all persons. The list supports paging.
parameters:
- name: pageSize
in: query
description: Number of persons returned
type: integer
- name: pageNumber
in: query
description: Page number
type: integer
responses:
200:
description: A list of Person
schema:
$ref: "another-folder/persons.yaml#/Persons"
post:
summary: Creates a person
description: Adds a new person to the persons list.
parameters:
- name: person
in: body
description: The person to create.
schema:
$ref: "folder/person.yaml#/Person"
responses:
204:
description: Persons succesfully created.
400:
description: Persons couldn't have been created.
/persons/{username}:
get:
summary: Gets a person
description: Returns a single person for its username.
parameters:
- name: username
in: path
required: true
description: The person's username
type: string
responses:
200:
description: A Person
schema:
$ref: "folder/person.yaml#/Person"
404:
description: The Person does not exists.
Referencing a remote files
As you may have guess while modifying the references in the specification and editor configuration, it is also possible to reference a remote file.
Remote files
All we need to do is to put the full file’s URL in the reference:
Remember that the server MUST have CORS activated to allow the editor to download the file.
We have launched a web server on 8080 port, so all we have to do is add http://localhost:8080/
to the references to Person
:
Remote files containing local references
What happen if the remote file reference a local file? (Main file -> Remote file -> Local file
).
The parser will seek this “local” file on the remote server providing the remote file.
If we replace the local to Persons
by a remote reference…
… the person.yaml
file referenced “locally” in the persons.yaml
file will be loaded from http://localhost:8080
which has served the persons.yaml
.
Remote files containing remote references
If a remote file contains a remote reference (Main file -> Remote file -> Remote file
), it will be resolved like in 2.4.1 Remote files.
We can replace the person.yaml
file local reference by a remote reference in persons.yaml
:
Multiple items in a single sub-file
We have put Person
and Persons
definitions in separate files: this is not an obligation. You can put more than one item in a single file, you only need to use the right JSON Pointer.
Person and Person in a single file
If we concatenate person.yaml
and persons.yaml
into a single file called definitions.yaml
:
Person:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
Persons:
type: array
items:
$ref: "#/Person"
Note that in Persons
the reference to Person
is now #/Person
.
In the main file we only need to change the filename when reference these two definitions:
Organizing content in a sub-file
Within the sub-file you can organize the content as you wish. Here Person
is in SomeDefinitions
and Persons is in OtherDefinitions
:
SomeDefinitions:
Person:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
OtherDefinitions:
Persons:
type: array
items:
$ref: "#/SomeDefinitions/Person"
Note that in Persons
the reference to Person
is now #/SomeDefinitions/Person
.
In the main file we have to modify the path to get the item in the sub-file for these two definitions:
Definitions, Responses, Parameters
What we have done with Person
and Persons
definitions can be done with any reusable items (i.e. using $ref
) such as responses and parameters in the Open API Specification file.
OpenAPI chainsaw massacre
Thanks to Mohsen Azimi’s post I’ve discovered that using sub-files for definitions, responses and parameters which obviously use $ref JSON Pointers is not the only way of using sub-files. You can use sub-files for almost anything in the specification.
We will split the huge file created in previous post about documentation.
Let the chainsaw massacre begin
Let’s start with the info
section:
swagger: '2.0'
info:
version: 1.1.0
title: Simple API
description: |
A simple API to learn how to write OpenAPI Specification.
This file uses almost every single aspect of the [Open API Specification](https://openapis.org/).
This API will use JSON.
JSON looks like this:
```JSON
{
"key": "value",
"anotherKey": "anotherValue"
}
```
termsOfService: http://simple.api/terms-of-service
contact:
name: John Doe
url: http://simple.api/contact
email: [email protected]
license:
name: Apache-2.0
url: http://www.apache.org/licenses/LICENSE-2.0
We can put its whole content in a file called info.yaml
:
version: 1.1.0
title: Simple API
description: |
A simple API to learn how to write OpenAPI Specification.
This file uses almost every single aspect of the [Open API Specification](https://openapis.org/).
This API will use JSON.
JSON looks like this:
```JSON
{
"key": "value",
"anotherKey": "anotherValue"
}
```
termsOfService: http://simple.api/terms-of-service
contact:
name: John Doe
url: http://simple.api/contact
email: [email protected]
license:
name: Apache-2.0
url: http://www.apache.org/licenses/LICENSE-2.0
And reference it just like this in info
:
The wizard of resolution
We can do the same thing with paths
, definitions
, responses
and parameters
: copy the section content in a sub-file (paths.yaml, definitions.yaml, responses.yaml and parameters.yaml) and reference it in the main file:
paths:
$ref: paths.yaml
definitions:
$ref: definitions.yaml
responses:
$ref: responses.yaml
parameters:
$ref: parameters.yaml
If you remember previous posts, these sections references each other in many ways, for example in paths.yaml
:
The username
parameter is no longer in the main file and is not in the paths.yaml
file but in the parameters.yaml
file:
How could the main file be considered valid in the editor (or in any tool parsing the file)? It’s simply because the sub-files are loaded and then the whole content (file + sub-files) is validated.
A less rough split
We can use JSON pointers for almost anything in the specification as as long as the $ref JSON pointer reference something corresponding to the expected object (or value) in the OpenAPI specification.
Referencing object in custom structure
And as seen earlier in this post we can put many items in a sub-file.
The documentation.yaml
file contains the data for externalDocs
and tags
(note the custom structure on line 1 and 6):
external:
description: |
**Complete** documentation describing how to use this API
url: http://doc.simple.api/
categories:
- name: Persons
description: Everything you need to handle `users` and `friends`
externalDocs:
description: People category documentation
url: http://doc.simple.api/people
- name: Items
description: Everything you need to handle items collected by users
externalDocs:
description: Items category documentation
url: http://doc.simple.api/items
- name: Media
description: Everything you need to handle images
externalDocs:
description: Media category documentation
url: http://doc.simple.api/media
- name: JSLess
description: Specific operations for JS less consumers
externalDocs:
description: JS Less Consumers documentation
url: http://doc.simple.api/jsless
These data are referenced this way in the main file:
The security.yaml
file contains the data for securityDefinitions
and security
(note the custom structure on line 20):
securityDefinitions:
OauthSecurity:
description: New Oauth security system. Do not use MediaSecurity or LegacySecurity.
type: oauth2
flow: accessCode
authorizationUrl: 'https://oauth.simple.api/authorization'
tokenUrl: 'https://oauth.simple.api/token'
scopes:
admin: Admin scope
user: User scope
MediaSecurity:
description: Specific media security for backward compatibility. Use OauthSecurity instead.
type: apiKey
in: query
name: media-api-key
LegacySecurity:
description: Legacy security system for backward compatibility. Use OauthSecurity instead.
type: basic
defaultSecurity:
- OauthSecurity:
- user
- LegacySecurity: []
Referencing a string or a simple list
It also work for simpler value like a string or a list of string. The schema
, host
and basepath
values can be moved into a single file security.yaml
(note the custom structure on line 3 and 4):
And referenced like this
schemes:
$ref: endpoint.yaml#/schemes
host:
$ref: endpoint.yaml#/server_and_port
basePath:
$ref: endpoint.yaml#/path
Reusing a value
As long as the API consumes and produces the same media types, we define a single value in the mediatypes.yaml
file:
And then we reference this single value in both produces
and consumes
:
A smarter split
The API we built with the previous parts could be divided in four parts:
- Common items like headers, media types, security, definitions, parameters and responses
- Persons operations, parameters, responses and definitions
- Legacy operations
- Images operations
We can create 4 sub-files and reference them from a main file.
commons.yaml
In the commons.yaml
file we put every items that can be reused across other files:
securityDefinitions:
OauthSecurity:
description: New Oauth security system. Do not use MediaSecurity or LegacySecurity.
type: oauth2
flow: accessCode
authorizationUrl: 'https://oauth.simple.api/authorization'
tokenUrl: 'https://oauth.simple.api/token'
scopes:
admin: Admin scope
user: User scope
MediaSecurity:
description: Specific media security for backward compatibility. Use OauthSecurity instead.
type: apiKey
in: query
name: media-api-key
LegacySecurity:
description: Legacy security system for backward compatibility. Use OauthSecurity instead.
type: basic
defaultSecurity:
- OauthSecurity:
- user
- LegacySecurity: []
defaultMediatypes:
- application/json
- application/x-yaml
defaultHeaders:
X-Rate-Limit-Remaining:
description: How many calls consumer can do
type: integer
X-Rate-Limit-Reset:
description: When rate limit will be reset
type: string
format: date-time
parameters:
userAgent:
name: User-Agent
description: All API consumers MUST provide a user agent
type: string
in: header
required: true
pageSize:
name: pageSize
in: query
description: Number of items returned
type: integer
format: int32
minimum: 0
exclusiveMinimum: true
maximum: 100
exclusiveMaximum: false
multipleOf: 10
default: 20
pageNumber:
name: pageNumber
in: query
description: Page number
type: integer
default: 1
responses:
Standard500ErrorResponse:
description: An unexpected error occured.
headers:
$ref: '#/defaultHeaders'
schema:
$ref: '#/definitions/Error'
TotallyUnexpectedResponse:
description: A totally unexpected response
headers:
$ref: '#/defaultHeaders'
definitions:
ErrorMessage:
title: MultiDeviceErrorMessage
description: An error message with a long and a short description
required:
- longMessage
- shortMessage
properties:
longMessage:
description: A long error description
type: string
shortMessage:
description: A short error description
type: string
MultilingualErrorMessage:
title: MultiLingualMultiDeviceErrorMessage
description: An multilingual error message (hashmap) with a long and a short description
additionalProperties:
$ref: '#/definitions/ErrorMessage'
properties:
defaultLanguage:
$ref: '#/definitions/ErrorMessage'
example:
defaultLanguage:
longMessage: We're deeply sorry but an error occured
shortMessage: Error
fr:
longMessage: Nous sommes désolé mais une erreur est survenu
shortMessage: Erreur
Error:
title: MultiLingualMultiDeviceError
description: Give full information about the problem
required:
- code
- message
properties:
code:
description: A human readable code (death to numeric error codes!)
type: string
enum:
- DBERR
- NTERR
- UNERR
example: UNERR
message:
$ref: '#/definitions/MultilingualErrorMessage'
Paging:
required:
- totalItems
- totalPages
- pageSize
- currentPage
properties:
totalItems:
type: integer
totalPages:
type: integer
pageSize:
type: integer
currentPage:
type: integer
images.yaml
In the images.yaml
file we put both /images
and /images/{id}
paths informations:
images:
parameters:
- $ref: 'commons.yaml#/parameters/userAgent'
post:
summary: Uploads an image
description: Upload an image, will return an image id.
operationId: storeImage
externalDocs:
description: How to upload media
url: http://doc.simple.api/media/upload
tags:
- Media
security:
- MediaSecurity: []
consumes:
- multipart/form-data
parameters:
- name: image
in: formData
type: file
responses:
'200':
description: Image's ID
schema:
properties:
imageId:
type: string
headers:
$ref: commons.yaml#/defaultHeaders
'500':
$ref: 'commons.yaml#/responses/Standard500ErrorResponse'
default:
$ref: 'commons.yaml#/responses/TotallyUnexpectedResponse'
images-imageId:
parameters:
- $ref: 'commons.yaml#/parameters/userAgent'
get:
summary: Gets an image
description: Return an image
operationId: readImage
tags:
- Media
parameters:
- name: imageId
in: path
required: true
type: string
produces:
- image/png
- image/gif
- image/jpeg
- application/json
- application/x-yaml
responses:
'200':
description: The image
headers:
$ref: commons.yaml#/defaultHeaders
'404':
description: Image do not exists
headers:
$ref: commons.yaml#/defaultHeaders
'500':
$ref: 'commons.yaml#/responses/Standard500ErrorResponse'
default:
$ref: 'commons.yaml#/responses/TotallyUnexpectedResponse'
Note that all references to common items point to commons.yaml
.
legacy.yaml
Same for the /js-less-consumer-persons
path we put in legacy.yaml
file in js-less-consumer-persons
:
js-less-consumer-persons:
parameters:
- $ref: 'commons.yaml#/parameters/userAgent'
post:
summary: Creates a person
description: For JS-less partners
operationId: createUserJS
deprecated: true
tags:
- JSLess
- Persons
security:
- OauthSecurity:
- admin
- LegacySecurity: []
consumes:
- application/x-www-form-urlencoded
produces:
- text/html
parameters:
- name: username
in: formData
required: true
pattern: '[a-z0-9]{8,64}'
minLength: 8
maxLength: 64
type: string
- name: firstname
in: formData
type: string
- name: lastname
in: formData
type: string
- name: dateOfBirth
in: formData
type: string
format: date
responses:
'204':
description: Person succesfully created.
headers:
$ref: 'commons.yaml#/defaultHeaders'
'400':
description: Person couldn't have been created.
headers:
$ref: 'commons.yaml#/defaultHeaders'
'500':
description: An error occured.
headers:
$ref: 'commons.yaml#/defaultHeaders'
default:
$ref: 'commons.yaml#/responses/TotallyUnexpectedResponse'
persons.yaml
All persons items go in the persons.yaml
:
- each path go in its own named section (like
persons-username
for/persons/{username}
) - all persons specific definitions goes in
definitions
(and can be referenced with#definition/name
) - all persons specific responses goes in
responses
(and can be referenced with#responses/name
) - all persons specific parameters goes in
parameters
(and can be referenced with#parameters/name
)
persons:
parameters:
- $ref: 'commons.yaml#/parameters/userAgent'
get:
summary: Gets some persons
description: Returns a list containing all persons. The list supports paging.
operationId: searchUsers
tags:
- Persons
parameters:
- $ref: 'commons.yaml#/parameters/pageSize'
- $ref: 'commons.yaml#/parameters/pageNumber'
- $ref: '#/parameters/includeNonVerifiedUsers'
- $ref: '#/parameters/sortPersons'
responses:
'200':
description: A list of Person
schema:
$ref: '#/definitions/Persons'
headers:
$ref: commons.yaml#/defaultHeaders
'500':
$ref: 'commons.yaml#/responses/Standard500ErrorResponse'
default:
$ref: 'commons.yaml#/responses/TotallyUnexpectedResponse'
post:
summary: Creates a person
description: Adds a new person to the persons list.
operationId: createUser
tags:
- Persons
security:
- OauthSecurity:
- admin
- LegacySecurity: []
parameters:
- name: person
in: body
required: true
description: The person to create.
schema:
$ref: '#/definitions/Person'
responses:
'204':
description: Person succesfully created.
headers:
$ref: commons.yaml#/defaultHeaders
'400':
description: Person couldn't have been created.
headers:
$ref: commons.yaml#/defaultHeaders
'500':
$ref: 'commons.yaml#/responses/Standard500ErrorResponse'
default:
$ref: 'commons.yaml#/responses/TotallyUnexpectedResponse'
persons-username:
parameters:
- $ref: '#/parameters/username'
- $ref: 'commons.yaml#/parameters/userAgent'
get:
summary: Gets a person
description: Returns a single person for its username.
operationId: readPerson
tags:
- Persons
responses:
'200':
description: A Person
schema:
$ref: '#/definitions/Person'
headers:
$ref: commons.yaml#/defaultHeaders
'404':
$ref: '#/responses/PersonDoesNotExistResponse'
'500':
$ref: 'commons.yaml#/responses/Standard500ErrorResponse'
default:
$ref: 'commons.yaml#/responses/TotallyUnexpectedResponse'
delete:
summary: Deletes a person
description: Delete a single person identified via its username
operationId: deletePerson
tags:
- Persons
responses:
'204':
description: Person successfully deleted.
headers:
$ref: commons.yaml#/defaultHeaders
'404':
$ref: '#/responses/PersonDoesNotExistResponse'
'500':
$ref: 'commons.yaml#/responses/Standard500ErrorResponse'
default:
$ref: 'commons.yaml#/responses/TotallyUnexpectedResponse'
persons-username-friends:
parameters:
- $ref: '#/parameters/username'
- $ref: 'commons.yaml#/parameters/userAgent'
get:
summary: Gets a person's friends
description: Returns a list containing all persons. The list supports paging.
operationId: readPersonsFriends
tags:
- Persons
parameters:
- $ref: 'commons.yaml#/parameters/pageSize'
- $ref: 'commons.yaml#/parameters/pageNumber'
- $ref: '#/parameters/includeNonVerifiedUsers'
- $ref: '#/parameters/sortPersons'
responses:
'200':
description: A person's friends list
schema:
$ref: '#/definitions/PagedPersons'
headers:
$ref: commons.yaml#/defaultHeaders
'404':
$ref: '#/responses/PersonDoesNotExistResponse'
'500':
$ref: 'commons.yaml#/responses/Standard500ErrorResponse'
default:
$ref: 'commons.yaml#/responses/TotallyUnexpectedResponse'
persons-username-collecting-items:
parameters:
- $ref: '#/parameters/username'
- $ref: 'commons.yaml#/parameters/userAgent'
get:
summary: Gets a person's collecting items list
description: |
Returns a list containing all items this person is looking for.
The list supports paging.
operationId: readPersonsCollectingItems
tags:
- Items
parameters:
- $ref: 'commons.yaml#/parameters/pageSize'
- $ref: 'commons.yaml#/parameters/pageNumber'
- $ref: '#/parameters/filterItemTypes'
responses:
'200':
description: A collected items list
schema:
$ref: '#/definitions/PagedCollectingItems'
headers:
$ref: commons.yaml#/defaultHeaders
examples:
application/json:
{
"totalItems": 10,
"totalPage": 4,
"pageSize": 3,
"currentPage": 2,
"items":
[
{
"itemType": "Vinyl",
"maxPrice": 20,
"imageId": "98096838-04eb-4bac-b32e-cd5b7196de71",
"albumName": "Captain Future Original Soundtrack",
"artist": "Yuji Ohno"
},
{
"itemType": "VHS",
"maxPrice": 10,
"imageId": "b74469bc-e6a1-4a90-858a-88ef94079356",
"movieTitle": "Star Crash",
"director": "Luigi Cozzi"
},
{
"itemType": "AudioCassette",
"maxPrice": 10,
"imageId": "b74469bc-e6a1-4a90-858a-88ef94079356",
"albumName": "Star Wars",
"artist": "John Williams"
}
]
}
'404':
$ref: '#/responses/PersonDoesNotExistResponse'
'500':
$ref: 'commons.yaml#/responses/Standard500ErrorResponse'
default:
$ref: 'commons.yaml#/responses/TotallyUnexpectedResponse'
definitions:
Person:
title: Human
description: A person which can be the user itself or one of his friend
required:
- username
properties:
firstName:
description: first name
type: string
example: John
lastName:
description: last name
type: string
example: Doe
username:
description: Username used to connect to the service
type: string
pattern: '[a-z0-9]{8,64}'
minLength: 8
maxLength: 64
example: john1doe6
dateOfBirth:
description: Date of birth
type: string
format: date
example: 1978-06-21
lastTimeOnline:
description: The last time this person was connected to the service as a
type: string
format: date-time
readOnly: true
example: 2016-06-10T12:36:58.014Z
avatarBase64PNG:
description: An avatar PNG image as a base64 encoded string ready to use as an src in img html tag
type: string
format: byte
default: data:image/png;base64,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
spokenLanguages:
$ref: '#/definitions/SpokenLanguages'
SpokenLanguages:
title: Languages
description: A hashmap of spoken languages
additionalProperties:
description: An additional spoken language
type: string
properties:
defaultLanguage:
description: Default spoken language
type: string
default: english
example:
defaultLanguage: french
it: italian
fr: french
Persons:
title: Humans
description: A list of users or friends
required:
- items
properties:
items:
description: Array containg the list
type: array
minItems: 10
maxItems: 100
uniqueItems: true
items:
$ref: '#/definitions/Person'
example:
- firstname: Robert
lastname": Doe
username": robdo
dateOfBirth: 1970-01-28
lastTimeOnline: 2016-04-10T14:36:58.014Z
- firstname: Jane
lastname: Doe
username: jdoe123
dateOfBirth: 1980-05-12
lastTimeOnline: 2016-05-12T19:23:59.014Z
CollectingItem:
discriminator: itemType
required:
- itemType
properties:
itemType:
description: |
An item can be of different type:
type | definition
-----|-----------
Vinyl| #/definitions/Vinyl
VHS | #/definitions/VHS
AudioCassette | #/definitions/AudioCassette
type: string
enum:
- AudioCassette
- Vinyl
- VHS
imageId:
type: string
maxPrice:
type: number
format: double
minimum: 0
maximum: 10000
exclusiveMinimum: true
exclusiveMaximum: false
Vinyl:
allOf:
- $ref: '#/definitions/CollectingItem'
- required:
- albumName
- artist
properties:
albumName:
type: string
artist:
type: string
VHS:
allOf:
- $ref: '#/definitions/CollectingItem'
- required:
- movieTitle
properties:
movieTitle:
type: string
director:
type: string
AudioCassette:
allOf:
- $ref: '#/definitions/CollectingItem'
- required:
- albumName
- artist
properties:
albumName:
type: string
artist:
type: string
PagedPersons:
allOf:
- $ref: '#/definitions/Persons'
- $ref: 'commons.yaml#/definitions/Paging'
PagedCollectingItems:
allOf:
- properties:
items:
type: array
minItems: 10
maxItems: 100
uniqueItems: true
items:
$ref: '#/definitions/CollectingItem'
- $ref: 'commons.yaml#/definitions/Paging'
responses:
PersonDoesNotExistResponse:
description: Person does not exist.
headers:
$ref: commons.yaml#/defaultHeaders
parameters:
username:
name: username
in: path
required: true
description: The person's username
type: string
includeNonVerifiedUsers:
name: includeNonVerifiedUsers
in: query
description: Result will not include non verified user by default if this parameter is not provided
type: boolean
default: false
allowEmptyValue: true
sortPersons:
name: sort
in: query
description: Result will be sorted by lastTimeOnline descending and username ascending by default if this parameter is not provided
type: array
uniqueItems: true
minItems: 1
maxItems: 3
collectionFormat: pipes
items:
type: string
pattern: '[-+](username|lastTimeOnline|firstname|lastname)'
default:
- -lastTimeOnline
- +username
filterItemTypes:
name: itemType
in: query
description: Filter collected items on their type
type: array
collectionFormat: multi
uniqueItems: true
items:
type: string
enum:
- AudioCassette
- Vinyl
- VHS
full main file
Here’s the full main file where we reference the 4 sub-files:
swagger: '2.0'
info:
version: 1.1.0
title: Simple API
description: |
A simple API to learn how to write OpenAPI Specification.
This file uses almost every single aspect of the [Open API Specification](https://openapis.org/).
This API will use JSON.
JSON looks like this:
```JSON
{
"key": "value",
"anotherKey": "anotherValue"
}
```
termsOfService: http://simple.api/terms-of-service
contact:
name: John Doe
url: http://simple.api/contact
email: [email protected]
license:
name: Apache-2.0
url: http://www.apache.org/licenses/LICENSE-2.0
externalDocs:
description: |
**Complete** documentation describing how to use this API
url: http://doc.simple.api/
tags:
- name: Persons
description: Everything you need to handle `users` and `friends`
externalDocs:
description: People category documentation
url: http://doc.simple.api/people
- name: Items
description: Everything you need to handle items collected by users
externalDocs:
description: Items category documentation
url: http://doc.simple.api/items
- name: Media
description: Everything you need to handle images
externalDocs:
description: Media category documentation
url: http://doc.simple.api/media
- name: JSLess
description: Specific operations for JS less consumers
externalDocs:
description: JS Less Consumers documentation
url: http://doc.simple.api/jsless
schemes:
- https
host: simple.api
basePath: /openapi101
consumes:
$ref: commons.yaml#/defaultMediatypes
produces:
$ref: commons.yaml#/defaultMediatypes
securityDefinitions:
$ref: commons.yaml#/securityDefinitions
security:
$ref: commons.yaml#/defaultSecurity
paths:
/persons:
$ref: 'persons.yaml#/persons'
/js-less-consumer-persons:
$ref: 'legacy.yaml#/js-less-consumer-persons'
'/persons/{username}':
$ref: 'persons.yaml#/persons-username'
'/persons/{username}/friends':
$ref: 'persons.yaml#/persons-username-friends'
'/persons/{username}/collecting-items':
$ref: 'persons.yaml#/persons-username-collecting-items'
/images:
$ref: 'images.yaml#/images'
/images/{imageId}:
$ref: 'images.yaml#/images-imageId'
Valid sub-files
If we put the last persons.yaml file content in the editor, it ends with these errors:
Splitting a huge Open API Specification file to keep it maintainable is a great idea but if you cannot validate sub-files against the specification, you gain almost nothing.
Making commons.yaml valid
If we put the commons.yaml file created earlier in the editor we get these errors:
- Missing required property: swagger
- Missing required property: info
- Missing required property: paths
- Additional properties not allowed: defaultHeaders,defaultMediatypes,defaultSecurity
Let’s see how we can fix these errors.
Missing swagger property
We just need to add this line on file’s top:
Missing info property
We add an short info
section after swagger
:
Missing paths property
As we do not define any path in this file we just need to add an empty paths
section:
Property defaultSecurity not allowed
As the defaultSecurity
correspond to the OpenAPI security
section we just need to rename it:
Invalid commons.yaml
file:
Valid commons.yaml
file:
Property defaultMediatypes not allowed
defaultMediaType
is not a valid OpenAPI property:
We will use produces
and consumes
to define the default media types. But as we want to define a single set of media type we will use this trick:
In the future if we want to define different sets of media types for produces and consumes, we’ll be ready.
Property defaultHeaders not allowed
defaultHeaders
is not a valid OpenAPI property:
defaultHeaders:
X-Rate-Limit-Remaining:
description: How many calls consumer can do
type: integer
X-Rate-Limit-Reset:
description: When rate limit will be reset
type: string
format: date-time
We will use a dummy response definition DefaultHeaders
to declare these default headers:
responses:
DefaultHeaders:
description: A dummy response to define default header
headers:
X-Rate-Limit-Remaining:
description: How many calls consumer can do
type: integer
X-Rate-Limit-Reset:
description: When rate limit will be reset
type: string
format: date-time
Of course we need to update common responses to use these headers:
Standard500ErrorResponse:
description: An unexpected error occured.
headers:
$ref: '#/responses/DefaultHeaders/headers'
schema:
$ref: '#/definitions/Error'
TotallyUnexpectedResponse:
description: A totally unexpected response
headers:
$ref: '#/responses/DefaultHeaders/headers'
Making persons.yaml valid
Just like with commons.yaml
, if we put persons.yaml
content in the editor we get some errors:
- Missing required property: swagger
- Missing required property: info
- Reference could not be resolved: commons.yaml#/defaultHeaders
- Missing required property: paths
- Additional properties not allowed: persons-username-collecting-items,persons-username-friends,persons-username,persons
Missing swagger and info properties
We just add swagger
and info
like for commons.yaml
:
swagger: "2.0"
info:
version: 1.0.0
title: Persons Sub-API
description: Persons operations, definitions, parameters and responses
securityDefinitions:
$ref: commons.yaml#/securityDefinitions
commons.yaml#/defaultHeaders reference could not be resolved
As the commons.yaml
structure has been modified concerning default headers we need to replace these references:
by this one:
Missing paths and additional properties not allowed
We have defined custom properties for each path relative to persons operations, therefore there is no paths
section and our custom properties (like persons
) are not allowed by the OpenAPI specification.
persons:
parameters:
- $ref: 'commons.yaml#/parameters/userAgent'
get:
summary: Gets some persons
description: Returns a list containing all persons. The list supports paging.
operationId: searchUsers
We need to add a paths
section and put all our path in it using the correct path value as key:
paths:
'/persons':
parameters:
- $ref: 'commons.yaml#/parameters/userAgent'
get:
summary: Gets some persons
description: Returns a list containing all persons. The list supports paging.
operationId: searchUsers
New errors: Security definition could not be resolved
Once this is done, 2 new errors appear:
- Security definition could not be resolved: OauthSecurity
- Security definition could not be resolved: LegacySecurity
Now that we have a valid structure, the paths have been parsed and the parser detected missing security definitions. To solve this error, we add these definitions by referencing the commons.yaml
file:
images.yaml and legacy.yaml
For images.yaml
and legacy.yaml
we do exactly the same things as we’ve done with persons.yaml
.
Here the images.yaml
file modified (partial view):
swagger: "2.0"
info:
version: 1.0.0
title: Images Sub-API
description: images operations
securityDefinitions:
$ref: commons.yaml#/securityDefinitions
paths:
/images:
Here the legacy.yaml
file modified (partial view):
swagger: "2.0"
info:
version: 1.0.0
title: Legacy Sub-API
description: Legacy operations
securityDefinitions:
$ref: commons.yaml#/securityDefinitions
paths:
/js-less-consumer-persons:
Updating the main file
Now that we have modified all sub-files, if we try to edit the main file we get these errors
- Reference could not be resolved: commons.yaml#/defaultMediatypes
- Reference could not be resolved: commons.yaml#/defaultSecurity
- Reference could not be resolved: persons.yaml#/persons
- Reference could not be resolved: images.yaml#/images-imageId
- Reference could not be resolved: persons.yaml#/persons-username
- Reference could not be resolved: persons.yaml#/persons-username-friends
- Reference could not be resolved: persons.yaml#/persons-username-collecting-items
- Reference could not be resolved: images.yaml#/images
- Reference could not be resolved: legacy.yaml#/js-less-consumer-persons
These errors are of 2 types:
- those due to the
commons.yaml
structure modification - and those due to the
persons.yaml
,images.yaml
andlegacy.yaml
paths structure modifications
Modifiying commons.yaml references
Before:
consumes:
$ref: commons.yaml#/defaultMediatypes
produces:
$ref: commons.yaml#/defaultMediatypes
securityDefinitions:
$ref: commons.yaml#/securityDefinitions
security:
$ref: commons.yaml#/defaultSecurity
After:
consumes:
$ref: 'commons.yaml#/consumes'
produces:
$ref: 'commons.yaml#/produces'
securityDefinitions:
$ref: 'commons.yaml#/securityDefinitions'
security:
$ref: 'commons.yaml#/security'
Modifying paths references
This is the trickyest part of this tutorial. To reference the /persons
path in persons.yaml
we used persons.yaml#/persons
:
But persons.yaml
structure has changed from :
persons:
parameters:
- $ref: 'commons.yaml#/parameters/userAgent'
get:
summary: Gets some persons
description: Returns a list containing all persons. The list supports paging.
operationId: searchUsers
to:
paths:
'/persons':
parameters:
- $ref: 'commons.yaml#/parameters/userAgent'
get:
summary: Gets some persons
description: Returns a list containing all persons. The list supports paging.
operationId: searchUsers
The new reference should be something like persons.yaml#/paths//persons
but if we try it, the editor shows an error Reference could not be resolved: persons.yaml#/paths//persons. The /
in /persons
needs to be escaped, how can we do that?
Evaluation of each reference token begins by decoding any escaped character sequence. This is performed by first transforming any occurrence of the sequence ‘~1’ to ‘/’ RFC6901
Before modification:
paths:
/persons:
$ref: 'persons.yaml#/persons'
/js-less-consumer-persons:
$ref: 'legacy.yaml#/js-less-consumer-persons'
'/persons/{username}':
$ref: 'persons.yaml#/persons-username'
'/persons/{username}/friends':
$ref: 'persons.yaml#/persons-username-friends'
'/persons/{username}/collecting-items':
$ref: 'persons.yaml#/persons-username-collecting-items'
/images:
$ref: 'images.yaml#/images'
/images/{imageId}:
$ref: 'images.yaml#/images-imageId'
After modification (all /
in reference name have been replaced by ~1
):
paths:
/persons:
$ref: 'persons.yaml#/paths/~1persons'
/js-less-consumer-persons:
$ref: 'legacy.yaml#/paths/~1js-less-consumer-persons'
'/persons/{username}':
$ref: 'persons.yaml#/paths/~1persons~1{username}'
'/persons/{username}/friends':
$ref: 'persons.yaml#/paths/~1persons~1{username}~1friends'
'/persons/{username}/collecting-items':
$ref: 'persons.yaml#/paths/~1persons~1{username}~1collecting-items'
/images:
$ref: 'images.yaml#/paths/~1images'
/images/{imageId}:
$ref: 'images.yaml#/paths/~1images~1{imageId}'
And now the main file and all its sub-files are considered valid by the editor.
Conclusion
You are now ready to split any OpenAPI specification file into valid sub-files in any possible ways. In this 8th part you’ve learned how to use JSON pointers in almost any places in an OpenAPI specification to references items from other files and how to create valid sub-files containing partial information. In the next final part (at last) we will learn how to extend the OpenAPI specification.