api-guidelines

Swagger Coding Style Guidelines

Table of Contents

Introduction

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).

Models

Model names

Model names should be simple, descriptive, and meaningful to developers. Model names should be in “upper camel case”.

Good:

Bad:

Combine models when practical

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.

Descriptions

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:

Bad:

Use well-defined property types

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 reserved words

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.

Proper use of required

Mark a property as “required” if and only if it will be present and not null for every instance of the model.

Order of properties

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.

Order of properties in body parameter models

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:

Sibling elements for refs

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).

Use of discriminator field

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.

Operations

Order of operations

Define basic/common operations before advanced/rare operations. Typically this order should match the order of operations in the API Reference.

Summary and description

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.

OperationId

Every operation should have a unique operationId. In the Swagger specification, operationIds are optional, but if specified, must be unique. For SDK generation, operationIds 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:

Explicitly specify consumes type(s)

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.

Explicitly specify produces type(s)

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

Use well-defined parameter types

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.

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.",
{

Use refs for common parameters

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.

Specify common parameters for a path in the path definition

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.

Parameter order

List parameters in the order they shall appear in the SDKs:

Do not explicitly define a content-type header parameter

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.

Do not explicitly define a accept-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.

Models for optional body parameters

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.

Conventions / Annotations for SDK generation

Title and version

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.

Host name for default service URL

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.

Error response models

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."
        }
      }
    }

Dynamic properties in models

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).

Alternate names for properties or parameters

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.

Excluding operations from the SDKs

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

Excluding a parameter from the SDKs

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

Excluding SDK support for some response types

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.

Array item names

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.

Java builders

Models that should have an associated builder object in Java should be annotated with "x-java-builder": true.

Content-type description and required

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.

Accept description and required

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.

File content types

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.

Filenames

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.

VCAP_SERVICES

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.

Version dates

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.