Parameter Serialization
- Explode
- Style
- Path Parameters
- Query Parameters
- Header Parameters
- Cookie Parameters
- Examples and Recommendations
Parameters not only define what inputs your API accepts, they also define the format your API expects to receive them in, i.e. how you would like it serialized.
There are two keywords concerning serialization:
Explode #
explode defines whether parameters should be broken into logical components.
It takes a boolean value:
- If
true; a parameter with multiple values will be serialized as if each of its values were separate parameters.- What separates each parameter is determined by the
style.
- What separates each parameter is determined by the
- If
false; a parameter is a single parameter, regardless of how many values it has.
In practice, this means only parameters of type:array or type:object are affected by explode.
- For an array, each value becomes its own parameter.
- For an object, each key-value pair is concatenated into its own parameter as “key=value”.
- For any
styleother thanform, if the value is an empty string, then it drops the equals and becomes “key”
- For any
For a more verbose description of explode, refer to RFC6750’s Variable Expansion.
Its default value depends on the style of serialization:
explode:trueis the default forstyle:formexplode:falsefor anything else.
Style #
style defines how your API expects the parameter to be serialized.
It takes a string value: The options defined depend on the location your parameter is in:
in:pathdefaults tosimplebut can also belabelormatrix.in:querydefaults toformbut can also bespaceDelimited,pipeDelimitedordeepObject.in:headerdefaults tosimple.in:cookiedefaults toform.
Each style will be explained in more depth per location; examples will make use of the following two parameters.
“pets” which depending on its type has one of the following values:
bool -> true
int -> 2
string -> "dog"
array -> ["cat","dog"]
object -> {"age":2,"type":"dog"}
“hats” which depending on its type has one of the following values:
bool -> false
int -> 1,
string -> "fedora"
array -> ["fedora"]
object -> {"type":"fedora"}
Path Parameters #
For parameters in:path there are three defined values for style:
simple: defined by RFC6750’s Simple String Expansion.label: defined by RFC6750’s Label Expansion with Dot-Prefix.matrix: defined by RFC6750’s Path-Style Parameter Expansion.
The defaults in:path are:
style:simpleexplode:false
Every style in:path follows RFC6750 so the effects of explode are well-defined by RFC6570’s Variable Expansion.
Simple #
style:simple with its default of explode:false, would serialize your parameters like this:
empty |
bool |
int |
string |
array |
object |
|---|---|---|---|---|---|
| true | 2 | dog | cat,dog | age,2,type,dog |
- Single values are unchanged.
- An
arraywith multiple values is concatenated into a comma-delimited list. - An
objecthas its key-value pairs concatenated into comma-delimited pairs, then each pair is concatenated into a comma-delimited list.
If you set explode:true, then the seperator used is also a comma: “,”:
empty |
bool |
int |
string |
array |
object |
|---|---|---|---|---|---|
| true | 2 | dog | cat,dog | age=2,type=dog |
- Single values remain unchanged.
- Surprisingly, an
arraywith multiple values seems unchanged. Though it treated each value as a separate parameter, it still had to separate them with a comma. So it still ends up as a comma-delimited list. - To understand what happened for an
objectlooking back at the rules onexplodewe see it concatenates key-value pairs into their own parameters as “key=value”. Then it has to separate each parameter with a comma.
Label #
style:label with its default of explode:false, would serialize your parameters like this:
empty |
bool |
int |
string |
array |
object |
|---|---|---|---|---|---|
| .true | .2 | .dog | .cat,dog | .age,2,type,dog |
Everything is the same as style:simple except all parameters were prefixed with “.”.
If you set explode:true, then the seperator used is a period: “.”:
empty |
bool |
int |
string |
array |
object |
|---|---|---|---|---|---|
| .true | .2 | .dog | .cat.dog | .age=2.type=dog |
- Single values remain unchanged.
- An
arraybecomes a period-delimited list. - An
objectconcatenates key-value pairs into their own parameters as “key=value”. Then it separates each parameter with a period.
Matrix #
style:matrix with its default of explode:false, would serialize your parameters like this:
empty |
bool |
int |
string |
array |
object |
|---|---|---|---|---|---|
| ;pets=true | ;pets=2 | ;pets=dog | ;pets=cat,dog | ;pets=age,2,type,dog |
Everything is the same as style:simple except all parameters were prefixed with a semicolon: “;pets=” where “pets” is the parameter’s name.
If you set explode:true, then the seperator used is a semicolon: “;”.
empty |
bool |
int |
string |
array |
object |
|---|---|---|---|---|---|
| ;pets=true | ;pets=2 | ;pets=dog | ;pets=cat;pets=dog | ;age=2;type=dog |
- Single values remain unchanged.
- An
arrayhas its values treated as separate parameters. Because they’re now treated separately, every value is prefixed with “;pets=” - An
objectis the exception, it does not get prefixed with “pets=”, but it still has to separated by a semicolon: “;”.
Query Parameters #
For parameters in:query there are four defined values for style.
form: It is defined by RFC6750’s Form-Style Query Expansion, if there are multiplespaceDelimited: An addition by popular demand.pipeDelimited: An addition by popular demand.deepObject: An addition by popular demand.
The defaults in:query are:
style:formexplode:true
Only style:form follows RFC6750 so the effects of explode are only well-defined by RFC6570’s Variable Expansion for style:form.
An informal, general rule of thumb is:
- Query strings start with a question-mark, this is how you separate the first
queryparameter from the rest of the URI. - Subsequent parameters
in:queryare separated by an ampersand “&”.
Just be aware that spaceDelimited, pipeDelimited and deepObject are not defined by RFC6750.
There are caveats to their usage, if you intend to use them, make sure you read their sections carefully.
Form #
With style:form, if you set explode:false, would serialize your parameters like this:
empty |
bool |
int |
string |
array |
object |
|---|---|---|---|---|---|
| ?pets=true | ?pets=2 | ?pets=dog | ?pets=cat,dog | ?pets=age,2,type,dog | |
| ?pets=true&hats=false | ?pets=2&hats=1 | ?pets=dog&hats=fedora | ?pets=cat,dog&hats=fedora | ?pets=age,2,type,dog&hats=type,fedora |
You’ll notice this looks almost identical to style:matrix.
There’s only one difference to be
- If it’s the first parameter, the separator from the rest of the URI by a question-mark like above
?pets=true - If it’s the second parameter, the separator is an ampersand, you might have a query string like this
?hats=false&pets=true
If you stick with the default of explode:true, then the seperator used is also a comma: “,”:
empty |
bool |
int |
string |
array |
object |
|---|---|---|---|---|---|
| ?pets=true | ?pets=2 | ?pets=dog | ?pets=cat&pets=dog | ?age=2&type=dog | |
| ?pets=true&hats=false | ?pets=2&hats=1 | ?pets=dog&hats=fedora | ?pets=cat&pets=dog&hats=fedora | ?age=2&type=dog&type=fedora |
Notice one example is highlighted in red. The OpenAPI Specification states that A Unique Parameter is a combination of name and (in).
Both “pets” and “hats” would be considered unique parameters, but they both have the property “type”. When explode is true their properties are serialized as if they were separate parameters. It is as if we have two different parameters both with name:type, in:query, they are no longer unique and one cannot be unambiguously distinguished from the other.
This conflict is entirely avoided if you explicitly set explode:false on parameters of type:object, but if that’s not an option, remain vigil for possible conflicts.
Space Delimited #
style:spaceDelimited with its default of explode:false, would serialize your parameters like this:
array |
object |
|---|---|
| ?pets=cat%20dog | ?pets=age%202%20type%20dog |
| ?pets=cat%20dog&hats=fedora | ?pets=age%202%20type%20dog&hats=type%20fedora |
It’s basically identical to style:form with explode:false. The difference being, the separator used is not a comma, but a percent-encoded space “%20”.
You’ll notice there are no examples for any type that would be a single value. This is because its behaviour is undefined for single values. One could assume it would be identical to style:form, but if your parameter is going to be a single value, there is no need to explicitly define it as spaceDelimited.
style:spaceDelimited is not defined by RFC6750 and there is no defined behaviour for explode:true. You could assume it would be identical to the well-defined in:query default of style:form with explode:true. That said, if you’re making that assumption, you’re better off leaving it on the well-defined default.
Pipe Delimited #
style:pipeDelimited with its default of explode:false, would serialize your parameters like this:
array |
object |
|---|---|
| ?pets=cat%7Cdog | ?pets=age%7C2%7Ctype%7Cdog |
| ?pets=cat%7Cdog&hats=fedora | ?pets=age%7C2%7Ctype%7Cdog&hats=type%7Cfedora |
It’s basically identical to style:form with explode:false. The difference being, the separator used is not a comma, but a percent-encoded pipe “%7C”.
You may be able to use a normal pipe “|” but it is not in the list of RFC3986’s Unreserved Characters. As such, it may work in some environments, and not in others.
If you still choose to use non-percent-encoded pipes, it would look like this:
array |
object |
|---|---|
| ?pets=cat|dog | ?pets=age|2|type|dog |
| ?pets=cat|dog&hats=fedora | ?pets=age|2|type|dog&hats=type|fedora |
You’ll notice there are no examples for any type that would be a single value. This is because its behaviour is undefined for single values. One could assume it would be identical to style:form, but if your parameter is going to be a single value, there is no need to explicitly define it as spaceDelimited.
style:pipeDelimited is not defined by RFC6750 and there is no defined behaviour for explode:true. You could assume it would be identical to the well-defined in:query default of style:form with explode:true. That said, if you’re making that assumption, you’re better off leaving it on the well-defined default.
Deep Object #
style:deepObject is undefined for its default of explode:false. You must explicitly specify explode:true for any defined behaviour.
You may be able to use a normal square brackets “[” and “]” but they are in the list of RFC3986’s Reserved Characters. As such, it may not work in some environments.
object |
|---|
| ?pets[age]=2&pets[type]=dog |
| ?pets[age]=2&pets[type]=dog&hats[type]=fedora |
For maximum interoperability it is safer to have them percent-encoded:
- “%5B” for “[”
- “%5D” for “]”.
object |
|---|
| ?pets%5Bage%5D=2&pets%5Btype%5D=dog |
| ?pets%5Bage%5D=2&pets%5Btype%5D=dog&hats%5Btype%5D=fedora |
Unsurprisingly, it only has defined behaviour for an object. This style is quite different from any other, even with explode:true the name, key and value are all specified. This makes it useful for avoiding the potential name conflicts objects could cause with style:form, explode:true.
Just bear in mind the name is misleading, despite being called a deepObject, there is no defined behaviour for nested arrays or objects. This is the same for every style in:query.
Header Parameters #
For parameters in:header there is only one defined value for style: simple.
Naturally, the default value is style:simple, with explode:false.
It is the same definition as it would be in:path except there is a major caveat to be aware of:
- Headers do not require any percent encoding in the same way a URI string would, so it cannot follow the same definitions laid out by RFC6750.
For this reason it is not recommended to rely on style, explode and schema.
For parameters in:header it is recommended to make use of the parameter’s content field instead of schema. Then use a media type such as text/plain and require the application to assemble the correct string. This will be the recommended approach as of OpenAPI Version 3.1.1, with more detail available in Appendix D: Serializing Headers and Cookies.
Cookie Parameters #
For parameters in:cookie there is only one defined value for style: form.
Naturally, the default value is style:form, with explode:true.
It is the same definition as it would be in:query except there are several major caveats to be aware of:
- Cookies do not require any percent encoding in the same way a URI string would, so it cannot follow the same definitions laid out by RFC6750.
- The first parameter is not prefixed with a question-mark “?” like it would
in:query. - Any subsequent parameters are not separated by an ampersand “&” like they would
in:query.- Subsequent parameters
in:cookieare separated by a semicolon followed by a space “; “.
- Subsequent parameters
As such style:form in:cookie is somewhat confusing, and less accurate the more parameters you have to serialize. For this reason it is not recommended to rely on style, explode and schema.
For parameters in:cookie it is recommended to make use of the parameter’s content field instead of schema. Then use a media type such as text/plain and require the application to assemble the correct string. This will be the recommended approach as of OpenAPI Version 3.1.1, with more detail available in Appendix D: Serializing Headers and Cookies.
Examples and Recommendations #
General Guide Lines #
Location #
If a parameter is needed across many paths, or contains sensitive information; it may be sensible to include in:header or in:cookie.
- If the parameter needs to persist across sessions, keep it
in:cookie.
If a parameter is only needed in specific paths, it may be sensible include in:path or in:query.
It is easier to provide parameters in a URL. Requiring headers, cookies or a requestBody generally make requests more difficult.
Keep it simple; if it’s sensible to include a parameter in:path or in:query, do so.
Style #
For parameters in:path or in:query; the defaults exist for a reason, they’re well-defined, versatile and simple.
For parameters in:header or in:cookie; the defaults work to an extent, but the variations on their syntax are beyond the scope of the OpenAPI Specification and what can be described through style. The recommended approach is to forgo style and schema in favour of using content with a media type such as text/plain.
Optional Boolean #
Looking at the Train Travel API, we can make a GET request to find available trips, based on our criteria:
/trips:
get:
...
parameters:
...
- name: dogs
in: query
description: Only return trips where dogs are known to be allowed
required: false
schema:
type: boolean
default: false
The parameter is simple, it could be formatted anywhere without issue.
It doesn’t need to persist between sessions, so it doesn’t need to be in:cookie.
It’s specific to this path, so there’s not much benefit in sticking it in:header.
It’s optional, so it cannot be in:path.
No style has been mentioned, nor explode. But the parameter is in:query so we know the default is style:form and explode:true. We would expect a URLs like this:
User without a dog: /trips
User with a dog: /trips?dogs=true
We could set explode:false but Explode has no effect on parameters that are not arrays or objects. This would be extra documentation with no gain, leaving it as the default keeps your specification concise.
We could not use any other style available to Query Parameters as only style:form can be used with parameters that are not arrays or objects.
Required String #
Looking at the Train Travel API once more, we can get the details of specific bookings:
/bookings/{bookingId}:
parameters:
- name: bookingId
in: path
required: true
description: The ID of the booking to retrieve.
schema:
type: string
format: uuid
example: 1725ff48-ab45-4bb5-9d02-88745177dedb
get:
...
Again the parameter is simple, it could be formatted anywhere without issue.
It doesn’t need to persist between sessions, so it doesn’t need to be in:cookie.
It’s specific to this path, so there’s not much benefit in sticking it in:header.
It’s required, so it could be in:path or in:query.
Because parameters in:path are always required:true, it is the most intuitive place to stick a required parameter.
By default this be style:simple and explode:false, looking like this: /bookings/1725ff48-ab45-4bb5-9d02-88745177dedb
It could have a different style like so:
style:label:bookings/.1725ff48-ab45-4bb5-9d02-88745177dedbstyle:matrix:bookings/;bookingId=1725ff48-ab45-4bb5-9d02-88745177dedb
List of Strings #
What if we could filter trips that stop at a specified list of stations?
It’s specific to this path, so we should keep it in the URL for simplicity. That means in:path or in:query.
Not every user knows the station they want, they may simply be looking for the closest stop to their actual destination. This parameter should be optional, so it cannot be in:path.
We could put it in:path and it would look like so:
/trips:
get:
...
parameters:
...
- name: stations
in: query
description: Only return trips that stop at these stations
required: false,
schema:
type: array
items:
type: string
Now our URL will look like this:
- Users with specific station in mind:
/trips?stations=gatwick&stations=london - Users with only one station in mind:
/trips?stations=london
AnyOf Object or String #
/trips:
get:
...
parameters:
...
- name: station
description: Only return trips that stop at your preferred station, if none, use to fallback if provided.
in: query
style: deepObject
explode: true
required: false
schema:
anyOf:
- type: object
required:
- preferred
properties:
preferred:
type: string
fallback:
type: string
- type: string
Here I’ve stated that my schema can be anyOf the following: an object or a string, in style:deepObject. You may have spotted the problem already:
- If our user specifies an object, this works as expected:
/trips?station[preferred]=gatwick&station[fallback]=london. - What if our user specifies a string? It’s undefined,
deepObjectonly has defined behaviour for objects.
You cannot apply style on a per-schema basis. Your style needs to work for all possible variations of your parameter.
If you intend to use anyOf, allOf or oneOf make doubly sure your choice of style works for every option.
As always, the best option is to minimise your use of complex parameters, keep it simple.