- Introduction
- Models
- Operations
- Parameters
- Conventions / Annotations for SDK generation
- Title and version
- Host name for default service URL
- Error response models
- Dynamic properties in models
- Alternate names for properties or parameters
- Excluding operations from the SDKs
- Excluding a parameter from the SDKs
- Excluding SDK support for some response types
- Array item names
- Java builders
- Content-type description and required
- Accept description and required
- File content types
- Filenames
- VCAP_SERVICES
- Version dates
The following are guidelines for writing API descriptions using Swagger. Of course, all Swagger API documents should conform to the Swagger/OpenAPI specification:
In addition, Watson APIs should adhere to the Watson Developer Cloud REST API guidelines:
The guidelines in this document extend and/or clarify the Swagger spec and API guidelines to address aspects of API specification related to the generation of client libraries suitable for distribution in a Software Development Kit (SDK).
Model names should be simple, descriptive, and meaningful to developers. Model names should be in "upper camel case".
Good:
Trait
Classifier
Bad:
TraitTreeNode
GetClassifiersTopLevelBrief
Use a single model to describe similar, compatible objects when practical. For example, if a "Foo" has 8 properties and a "FooPlus" has the same 8 properties as "Foo" but also two more, it is usually preferable to combine these two models by adding the two additional properties from "FooPlus" into "Foo" as optional properties.
Every model and property should have a description. These descriptions should match the API Reference descriptions wherever practical. Avoid describing a model as a "JSON object" since this will be incorrect for some SDKs. Rather, use the generic "object" in model descriptions.
Good:
- "An object containing request parameters."
Bad:
- "JSON object containing request parameters."
Model properties and parameters should have well-defined type and format information.
Only use combinations of type
and format
defined in the
OpenAPI Specification
Good:
"matching_results": {
"type": "integer",
"format": "int32"
},
Bad:
"matching_results": {
"type": "number",
"format": "integer"
},
In the "Bad" example, matching_results
may appear to be defined as an integer, but the integer
format is
not defined for type number
, so the actual type is a floating point (generic number).
The standard tools do not warn about this because the swagger spec does not restrict the values for "type" or "format".
Avoid using reserved words for model/property names (for example, error
, return
, type
, input
).
See Alternate names for properties or parameters below
for a way to deal with existing properties whose names are reserved words.
Mark a property as "required" if and only if it will be present and not null for every instance of the model.
The properties in a model definition should appear in the same order they should appear in the SDK. Typically important or fundamental properties should be listed first and ancillary properties appearing last. For example, if a model has an "id" property that uniquely identifies an instance of the model, that should generally appear earlier in the list of properties. As a corollary, required properties should generally appear before optional properties in the model definition.
Models that represent body parameters may be absorbed into the parameter list for the method for the request, so additional care is needed in defining these model:
- List all required properties before any optional properties.
- Add new optional properties to the end of the property list.
Be aware that the JSON Schema specification (an underlying element of the swagger/OpenAPI spec) does not allow "sibling" elements to a $ref. (See this swagger editor github issue).. This means that a property defined with a $ref cannot be given an alternate description (or any other attribute).
The discriminator
field of a model can be used to create a polymorphic relationship between models. The discriminator
should be specified on the superclass, although the value doesn't actually affect the relationship. The subclasses should use the allOf
property to reference the superclass and define any additional properties.
Example:
"Pet": {
"type": "object",
"discriminator": "pet_type",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer",
"format": "int32"
}
}
},
"Dog": {
"allOf": [
{
"$ref": "Pet"
},
{
"properties": {
"breed": {
"type": "string"
}
}
}
]
},
"Hamster": {
"allOf": [
{
"$ref": "Pet"
},
{
"properties": {
"fur_color": {
"type": "string"
}
}
}
]
}
Using discriminator
allows for the Pet
class to show up as a parent
property for both Dog
and Hamster
in the SDK generator. Similarly, allOf
will ensure that the resulting Dog
and Hamster
objects contain the properties derived from Pet
.
Define basic/common operations before advanced/rare operations. Typically this order should match the order of operations in the API Reference.
Each operation should have a summary and/or description. When both a summary and description are provided, the description should include additional detail about the operation behavior -- it should not simply restate the summary.
Every operation should have a unique operationId
.
In the Swagger specification, operationId
s are optional, but if specified, must be unique.
For SDK generation, operationId
s are used as the name of the method corresponding to the operation, so it is important to include these in the OpenAPI definition file.
The operationId
should be specific and descriptive. Here is the recommended convention:
- GET a single
Model
:getModel
- GET a list of
Model
:listModels
- POST a new
Model
:createModel
oraddModel
- POST a partial update to a
Model
:updateModel
- PUT a complete replacement to a
Model
:replaceModel
- DELETE a
Model
:deleteModel
All POST and PUT operations should explicitly specify their consumes
type(s).
Note that Swagger (v2) supports a global consumes
setting to specify the content type(s) consumed
by any API that does not explicitly override this value.
All operations should explicitly specify their produces
type(s).
Note that Swagger (v2) supports a global produces
setting to specify the content type(s) produced
by any API that does not explicitly override this value.
Parameters should have well-defined type and format information. Only use combinations of type
and format
defined in the OpenAPI Specification Parameter types are further constrained by their "in" property, as specified in OpenAPI Specification, Parameter Object.
Parameters that may have multiple values (for example, a comma-separated list of values) are best described as arrays of the base value type.
- In Swagger 2, the
collectionFormat
attribute specifies how the multiple values are represented. The default is comma-separated values (CSV). - In OpenAPI 3, the
style
attribute specifies serialization. For path parameters, the default is `"style": "simple", which indicates an array with CSV.
Good:
{
"name": "return",
"in": "query",
"type": "array",
"items": {
"type": "string"
},
"description": "A comma-separated list of the portion of the document hierarchy to return.",
{
Bad:
{
"name": "return",
"in": "query",
"type": "string",
"description": "A comma-separated list of the portion of the document hierarchy to return.",
{
For any parameters that appear on multiple operations,
create a named parameter in the parameters section of the swagger doc and
then use a $ref
to reference the parameter definition from every operation
that accepts this parameter.
Any parameter that appears on all operations of a particular path should be specified in the parameter list for the path rather than in the parameter list for each of the operations. This makes the API description more concise and easy to understand.
List parameters in the order they shall appear in the SDKs:
- List all required parameters before any optional parameters.
- For services that take a version parameter, list it first since it is always required.
- Add new optional parameters to the end of the parameter list.
Operations that consume multiple content types often use a "content-type" header parameter to specify
the content type of data provided.
However, this header parameter should not be coded explicitly in the swagger, since it is implicitly
specified by a consumes
setting with more than one value.
OpenAPI 3.0 requires that a "content-type" header parameter, if specified, must be ignored.
The Watson SDK generator ignores any explicitly coded content-type
header parameter.
Operations that produce multiple content types often use an "accept-type" header parameter to specify
the content type of data to be returned.
However, this header parameter should not be coded explicitly in the swagger, since it is implicitly
specified by a produces
setting with more than one value.
OpenAPI 3.0 requires that an "accept" header parameter, if specified, must be ignored.
The Watson SDK generator ignores any explicitly coded accept-type
header parameter.
Don't specify required properties in the schema of an optional body parameter. This is ambiguous and can lead to incorrect implementation on the client or server.
The SDK generator assumes that the title
in the info section is the service name (for example, "Conversation", and not "Conversation APIs" or some such)
and the version
truncated at the "." is the "V" version of the service.
In OpenAPI v2, the base URL for the service is specified in the combination of the host
and basePath
properties of the
API document. But Watson avoids using the host
properties because it interferes with the proxy server setup they have
created for the API explorer.
The x-watson-host
annotation can be added to the info
section of the API Doc to specify the
host value for the service in the even that this is not provided in the host
property.
The SDK generator assumes that Error responses are defined by a model whose name starts with "Error". The SDK generator does not generate models for Error responses -- they are handled inline.
The following is an example of a well-designed error response model:
"ErrorModel": {
"required": [
"code",
"error"
],
"properties": {
"code": {
"type": "integer",
"description": "The HTTP status code."
},
"error": {
"type": "string",
"description": "A message intended for users that describes the error that occurred."
},
"help": {
"type": "string",
"description": "A URL to documentation explaining the cause and possibly solutions for this error."
}
}
}
Some models may allow dynamic properties -- properties that are not explicitly named in the schema
(for example, input, output, and context in Conversation).
This should be explicitly specified with additionalProperties
in the model definition.
Note that additionalProperties
is treated differently by the swagger tools from its meaning in JSON schema.
In particular, additionalProperties
is the default in JSON Schema -- dynamic properties are allowed unless explicitly prohibited.
The swagger tools default the other way, assuming no dynamic properties unless additionalProperties
is specified
(see discussion here).
The x-alternate-name
annotation can be added to a property or parameter in the swagger to specify an
alternate name for that property or parameter in the SDKs.
The primary use for this annotation is to rename properties/parameters whose original name is
a reserved word in one of the SDK languages.
In certain cases, a property or parameter may need to be renamed only for one particular language.
To handle this situation, the generator also recognizes language-specific alternate name annotations
with the format x-<lang>-alternate-name
.
For example, use the annotation "x-java-alternate-name", "awesome_name"
to rename a property or parameter
to awesome_name
only in the Java SDK.
Be careful to avoid assigning an alternate name that is identical to the name or alternate name of another property of the model or parameter of the operation.
It may be desirable to exclude some operations from the generated SDKs. For example, Watson Tone Analyzer has GET and POST operations that perform essentially the same function. SDKs generally only implement the POST operation since it is more flexible and the complexities of coding the POST are hidden by the SDK.
Operations that should not have methods generated in the SDKs should be annotated with "x-sdk-exclude": true
In certain cases it is useful to exclude support for a particular parameter of an operation from the generated SDKs.
A specific example is the Transfer-Encoding
parameter of the Speech to Text service, where supporting
this parameter would require new streaming support not yet implemented in the SDKs.
Parameters that should not be supported in the SDKs should be annotated with "x-sdk-exclude": true
It may be appropriate to exclude support for some response types from the SDKs. In the "strongly-typed" languages, separate methods are generated for each major response type. This adds clutter and overhead to the SDKs in terms of test effort, etc. When the marginal value of a particular response type is low, it can be excluded from the SDKs.
Use the x-sdk-produces
annotation to specify a subset of the produces
values to be supported by the SDKs.
Swagger (v2.0) has no provision for giving a name to the elements of an array property. This is a problem for the SDK generator if it wants to create a method to add or access a single element of the array.
Specify the "x-item-name"
annotation on the array property with the desired item name.
Models that should have an associated builder object in Java should be annotated with "x-java-builder": true
.
For operations that accept multiple content-types, users may need to specify the content type of a request body explicitly using the "content-type" header parameter. However, "content-type" should not be explicitly defined as a parameter to the operation (see above), so there is no official mechanism to specify a custom description for the "content-type" header parameter when it may be needed.
Use the "x-content-type-description"
annotation on an operation to specify a custom description for
the "content-type" header parameter.
It is best practice to support requests without a "content-type" header by assuming a default content type, or by introspection of the content to determine its type. However, some operations may require the user to explicitly pass a "content-type" header.
Use the "x-content-type-required"
annotation on an operation to specify that an operation requires the request
to contain a "content-type" header parameter.
When this annotation is not present, the content-type parameter is defined as optional.
For operations that may return multiple content-types, users may need to specify the content type of the response explicitly using the "accept" header parameter. However, "accept" should not be explicitly defined as a parameter to the operation (see above), so there is no official mechanism to specify a custom description for the "accept" header parameter when it may be needed.
Use the "x-accept-description"
annotation on an operation to specify a custom description for
the "accept" header parameter.
It is best practice to support requests without an "accept" header by assuming a default content type for the response. However, some operations may require the user to explicitly pass an "accept" header.
Use the "x-accept-required"
annotation on an operation to specify that an operation requires the request
to contain an "accept" header parameter.
When this annotation is not present, the accept parameter is defined as optional.
Swagger (v2.0) has no provision for specifying the allowable content-types for files passed in multi-part form bodies. This information is needed by the generator to determine whether the SDK should support a fixed or user-supplied content_type for the file.
Use the "x-file-content-types"
annotation on a file parameter to specify an array of allowable content-types for the
file. The first value in this array will be used as the default content-type.
Some operations that accept a file parameter require a filename to be supplied along with the file contents. Typically this is because the service wants to store the filename as metadata associated with the file contents.
To specify that the service requires a filename to be provided along with the file contents, add the x-include-filename
annotation to the file parameter with the value true
.
Applications that run in Bluemix can obtain credentials for thier associated services from the VCAP_SERVICES
environment
variable.
Specify the x-vcap-service-name
annotation in the info section of the OpenAPI definition file with the name of the service
as it appears in the VCAP_SERVICES
environment variable to enable the SDK to obtain these credentials.
Some services accept version date parameters. The x-version-date
annotation in the info section of the OpenAPI definition file accepts a string with the most current version date.