After learning how to create an accurate data model, we continue to delve into the OpenAPI specification’s and discover how to describe tailor made API’s inputs and outputs.

Writing OpenAPI (fka Swagger) Specification tutorial

This tutorial is composed of several posts:

In previous parts (especially The basics and Simplifying specification file we have learned how to describe simple operations parameters and responses using inline definitions or high level ones. In this fifth part you will discover all the tips and tricks to describe highly accurate parameters and responses.

All tutorial’s files are available on GIST.

If you’re a bit lost in the specification, take a look at my [visual documentation:

Parameters

In this section you will learn to define:

  • Required or optional parameter
  • Parameter with default value
  • Parameter with empty value
  • Array parameter
  • Header parameter
  • Form parameter
  • File parameter
  • Parameter’s media type

Required or optional parameter

We already have used the required key word which is used to define a mandatory parameter or a mandatory value in definition.

Defining a required or optional parameter

In a parameter, required is an optional value which type is boolean. Its default value is false.

When used in a operation, the username parameter is mandatory:

When used in an operation, the pageSize parameter is NOT mandatory (required is not defined and therefore is false).

Defining a required or optional property in a definition used as a parameter

In a definition, required is an optional value which type is a list of string. This list contains the mandatory properties names. A property which is not referenced in this list is NOT mandatory. If required is not defined, all object properties are not mandatory. When this definition is used on a request, all required properties MUST be provided.

In the definition Person which is used as a body parameter in POST /persons, the username property is mandatory (present in required list) , all others are not mandatory (not referenced in required list).

Parameter with default value

By using the keyword default you can define a default value for a parameter or a default value for a property in a definition. This default value is the one that the server will use if none is provided. Using default does not make sense when a property or parameter is required.

Defining a parameter’s default value

On the parameter pageSize, we set its default value to 20. If this value is not provided, the server will then return pages containing 20 elements.

On the parameter pageNumber, we set its default value to 1. If this value is not provided, the server will then return the first page.

Defining a property’s default value in a definition used as a parameter

On the definition Person, the avatarBase64PNG property default value is a 64x64 pixels API Handyman PNG icon as a base64 string.

Default Avatar

Defining a default value in a hashmap definition used as a parameter

In previous post (Advanced data modeling) we’ve learned how to define hashmaps. We can set a default value in a an hashmap this way:

Just like we’ve learned on previous post, we define a property defaultLanguage on SpokenLanguages definition and today we set a default value for this property. Therefore is the property spokenLanguages of the Person definition (which is a SpokenLanguages) is not provided on POST /persons, its value will be {"defaultLanguage": "english"}

Parameter with empty value

If we want to add a filter parameter to include non verified users on GET /persons a first idea would be to have something like GET /persons?page=2&includeVerifiedUsers=true. But why should we have to set a value when the parameters name is sufficient to express what we want to do? Having GET /persons?page=2&includeVerifiedUsers would be much better.

To do that we just need to use the allowEmptyValue key word on the parameter’s description. We have also define a default value to false, therefore GET /persons?page=2&includeVerifiedUsers will include verified users and GET /persons?page=2 will not.

Array parameter

When it comes to sorting or filtering an API designer almost always ends asking himself: but how will I define an array parameter on a get request in my OpenAPI specification?

It’s easy, he just need to define an array type attribute and use the accurate collectionFormat:

collectionFormat   Description
csv (default value) Comma separated values foo,bar
ssv Space separated values foo bar
tsv Tab separated values foo\tbar
pipes Pipes separated values foo|bar
multi Corresponds to multiple parameter instances instead of multiple values for a single instance foo=bar&foo=baz. This is valid only for parameters in query or formData.

Defining a separated values parameter

If we want to sort a persons list on multiple parameters (username, firstname, lastname, lastTimeOnline) ascending or descending we could use something like GET /persons?sort=-lastTimeOnline|+firtname|+lastname. The sort parameters are in a pipe separated array named sort, each of them starting with + for an ascending sort or - for a descending one.

This sortparameter is defined with an array of string using a pipes collectionFormat: We can now have requests like this: GET /persons?sort=String1|String2|String3. But how do we set the sorting direction and how can we we enforce the values within the array?

In this specific use case we will define a pattern for the string within the array, just like we’ve learned on previous part when defining the username property. This pattern says that a value within the array may start with + (for ascending) and - (for descending) and be followed by username, firstname, lastname or lastTimeOnline. Our API is now ready to handle a GET /persons?sort=-lastTimeOnline|+firtname|+lastname request.

And icing on the cake, we can also define a default sort (last online time descending and username ascending) by adding a default value on the array level:

Defining a multiple values parameter

If we want to filter a persons collected items list on mutiple items types we could use something like GET /persons/apihandyman/collected-items?itemType=AudioCassette&itemType=Vinyl. The filter parameters are in a multi array named itemType, each value corresponding to one of the item’s type we want to filter on.

This filterItemTypes parameter is defined with an array of unique stringusing a multi colectionFormat:

And icing on the cake we enforce the possible values by defining an enum on the string within the array:

Parameter location is not only path, query or body

When describing a parameter, the keyword in is used to set its location. We already have seen in previous parts the most common values of in are:

  • path
  • query
  • body

But they are not the only ones, there two others:

  • header
  • formData

We’ll see in next sections how we can use them in an API definition to define

  • Header parameter
  • Form parameter
  • File parameter

Header parameter

Let’s say we want our API consumer’s to provide some informations about themselves by using the good old User-Agent HTTP header (for tracking, debugging, or whatever you want).

We define the parameter just like any other one, we just need to set the headervalue in in:

And we use it (on every path) just like any other parameter:

There’s absolutely no way of telling once and for all that all operations needs this header parameter (for now).

nb: This works also with custom HTTP header.

Form parameter

Let’s say we have a partner who need to use our API in a js-less-browser environment to create persons. He can only provide the creation information in a good old HTML form format:

No problem, we just need to define all form parameters with in sets to formData and setting the consumes media type to application/x-www-form-urlencoded:

File parameter

To define an operation which will accept a file as input parameter, you need to:

  • use multipart/form-data media type
  • set parameter’s in value to formData
  • set parameter’s type value to file

And what if I want to restrict file’s media type? Unfortunately, it’s not possible for now:

The spec doesn’t allow specifying a content type for specific form data parameters. It’s a limitation of the spec. Ron Ratovsky comment in Swagger UI 609 issue

Parameter’s media types

An API can consume various media types, the most common one is application/json, but it’s not the only one an API can use. The keyword consumes on root or operation level is used to describe the accepted media types list.

We set the global media-type on the root level of the OpenAPI document, here our API consumes JSON and YAML:

This settings can be overriden on the operation level by redefining the consumes list:

Responses

In this section you will learn to define:

  • Response without a body
  • Required or optional values in response
  • Response’s headers
  • Default response
  • Response’s media types

Response without a body

It’s a common feature in HTTP protocole and in REST API to have response without body. For example the 204 HTTP Status is used by a server which wants to indicate a succes without returning any content (or body).

To define a response without a body, all you have to do is set its status and description:

Required or optional values in response

Just like we’ve seen on parameters, we can define mandatory properties in definition used in responses. Mandatory properties are defined with the required list. A property referenced in this list MUST be sent by the server and a property absent from this list MAY be sent by the server.

When GET /persons/apihandyman returns a Person, this person will surely contains a username, but all other values may not be sent:

Response’s headers

The HTTP status and the body are not the only way to provide information on the result of an API call, HTTP headers can be used too.

Let’s say we want to offer something like the Twitter’s API rate limiting information to provide the remaining number of API calls and when the limit will be reset. We have to define two header X-Rate-Limit-Remaining and X-Rate-Limit-Reset and each API response:

Unfortunately there’s absolutely no way of defining such headers once and for all (for now).

Default response

The OpenAPI specification allow to define a default response on each operation.

This could be used to define a single generic response but your API definition would not be easily understandable. I’d prefer to use it in conjunction with a full description of which HTTP status an API handle, and then use the default response to say if you have any other HTTP status than the ones I described explicitely, it do not comes from this API.

A generic response defined once and for all in the responses section:

This response used as a default response on each operation:

Unfortunately there’s absolutely no way of defining a global response once and for all (for now).

Response’s media types

An API can produce various media types, the most common one is application/json, but it’s not the only one an API can use. The keyword produces on root or operation level is used to describe the returned media types list.

We set the global media-type on the root level of the OpenAPI document, here our API produces JSON and YAML:

This settings can be overriden on the operation level by redefining the local produces list, here’s an example on GET /images/{imageId} operation which return an image:

Note that the produces contains images media types but also json and yaml media types as if there’s a 500 error, the message will be return with one of those 2 media types.

How to use a single definition when output returns more data than input needs

And finally, as already seen on previous part in section 2.1, in a definition the readOnly property is set to true to indicate that this property MAY be sent in a response and MUST NOT be sent in a request.

It allows you to use a single definition when output returns more data than input needs.

In the definition Person which is both used as a part of the response in GET /persons and a body parameter in POST /persons the lastTimeOnline property has readOnly set to true. Therefore this property MUST NOT be provided on POST /persons and MAY be returned by the server on GET /persons:

Warning: SwaggerUI does not handle this yet (issue 884).

Conclusion

You are now a Jedi of API’s input and ouput definition with the OpenAPI specification. In next post you’ll learn to describe how your API is secured.