Swagger Inheritance and Composition

In my "simplified" API, all responses are derived (inherit) from a base "response" class. The response class is composed of a header filled with metadata, and the body which contains the core data the the user is requesting. The response (in JSON) is laid out such that all the metadata is on the first "layer" and the body is a single attribute called "body" as such

response
|--metadata attribute 1 (string/int/object)
|--metadata attribute 2 (string/int/object)
|--body (object)
    |--body attribute 1 (string/int/object)
    |--body attribute 2 (string/int/object)

I have tried to define this relationship in swagger with the following JSON:

{
    ...
    "definitions": {
        "response": {
            "allOf": [
                {
                    "$ref": "#/definitions/response_header"
                },
                {
                    "properties": {
                        "body": {
                            "description": "The body of the response (not metadata)",
                            "schema": {
                                "$ref": "#/definitions/response_body"
                            }
                        }
                    }
                }
            ]
        },
        "response_header": {
            "type": "object",
            "required": [
                "result"
            ],
            "properties": {
                "result": {
                    "type": "string",
                    "description": "value of 'success', for a successful response, or 'error' if there is an error",
                    "enum": [
                        "error",
                        "success"
                    ]
                },
                "message": {
                    "type": "string",
                    "description": "A suitable error message if something went wrong."
                }
            }
        },
        "response_body": {
            "type": "object"
        }
    }
}

I then try to create different responses by creating the various body/header classes that inherit from body/header, and then create child response classes that are composed of the relevant header/body classes (shown in source code at bottom). However, I am certain that either this is the wrong way to do things, or that my implementation is incorrect. I have been unable to find an example of inheritance in the swagger 2.0 specification (shown below) but have found an example of composition.

enter image description here

I am pretty certain that this "discriminator" has a large part to play, but not sure what I need to do.

Question

Could someone show me how one is supposed to implement composition+inheritance in swagger 2.0 (JSON), preferably by "fixing" my example code below. It would also be great if I could specify an ErrorResponse class that inherits from response where the "result" attribute in the header is always set to "error".

{
    "swagger": "2.0",
    "info": {
        "title": "Test API",
        "description": "Request data from the system.",
        "version": "1.0.0"
    },
    "host": "xxx.xxx.com",
    "schemes": [
        "https"
    ],
    "basePath": "/",
    "produces": [
        "application/json"
    ],
    "paths": {
        "/request_filename": {
            "post": {
                "summary": "Request Filename",
                "description": "Generates an appropriate filename for a given data request.",
                "responses": {
                    "200": {
                        "description": "A JSON response with the generated filename",
                        "schema": {
                            "$ref": "#/definitions/filename_response"
                        }
                    }
                }
            }
        }
    },
    "definitions": {
        "response": {
            "allOf": [
                {
                    "$ref": "#/definitions/response_header"
                },
                {
                    "properties": {
                        "body": {
                            "description": "The body of the response (not metadata)",
                            "schema": {
                                "$ref": "#/definitions/response_body"
                            }
                        }
                    }
                }
            ]
        },
        "response_header": {
            "type": "object",
            "required": [
                "result"
            ],
            "properties": {
                "result": {
                    "type": "string",
                    "description": "value of 'success', for a successful response, or 'error' if there is an error",
                    "enum": [
                        "error",
                        "success"
                    ]
                },
                "message": {
                    "type": "string",
                    "description": "A suitable error message if something went wrong."
                }
            }
        },
        "response_body": {
            "type": "object"
        },
        "filename_response": {
            "extends": "response",
            "allOf": [
                {
                    "$ref": "#definitions/response_header"
                },
                {
                    "properties": {
                        "body": {
                            "schema": {
                                "$ref": "#definitions/filename_response_body"
                            }
                        }
                    }
                }
            ]
        },
        "filename_response_body": {
            "extends": "#/definitions/response_body",
            "properties": {
                "filename": {
                    "type": "string",
                    "description": "The automatically generated filename"
                }
            }
        }
    }
}

Diagram Update

To try and clarify what I want, I have created the very basic diagram below which aims to show that all responses are instantiations of the "response" object that have been built by (composition) using any combination of response_header and response_body objects. The response_header and response_body objects can be extended and inserted into any response object, which is done in the case of a filename_response which uses the filename_response_body child of the base response_body class. Both error and successful responses use the "response" object.

enter image description here


As a beginner in swagger I don't find the official documentation about polimorphism and composition easy to undestand, because it lacks an example. When I searched the net, there are lots of good examples refering to swagger 1.2 when extends was valid.

For swagger 2.0 I found a good example in swagger spec sources on github via this google group

Based on above sources, here is a short valid inheritance example in YAML:

definitions:
  Pet:
    discriminator: petType
    required:
      - name
      - petType # required for inheritance to work
    properties:
      name: 
        type: string
      petType:
        type: string
  Cat:
    allOf:
      - $ref: '#/definitions/Pet' # Cat has all properties of a Pet
      - properties: # extra properties only for cats
          huntingSkill:
            type: string
            default: lazy
            enum:
              - lazy
              - aggressive
  Dog:
    allOf:
      - $ref: '#/definitions/Pet' # Dog has all properties of a Pet
      - properties: # extra properties only for dogs
          packSize:
            description: The size of the pack the dog is from
            type: integer

I've found that composition works fine even without definition of discriminator.

For example, base Response:

definitions:
  Response:
    description: Default API response
    properties:
      status:
        description: Response status `success` or `error`
        type: string
        enum: ["success", "error"]
      error_details:
        description: Exception message if called
        type: ["string", "object", "null"]
      error_message:
        description: Human readable error message
        type: ["string", "null"]
      result:
        description: Result body
        type: ["object", "null"]
      timestamp:
        description: UTC timestamp in ISO 8601 format
        type: string
    required:
      - status
      - timestamp
      - error_details
      - error_message
      - result

Is rendered as:

Response visualization

And we can extend it to refine custom schema of result field:

  FooServiceResponse:
    description: Response for Foo service
    allOf:
      - $ref: '#/definitions/Response'
      - properties:
          result:
            type: object
            properties:
              foo_field:
                type: integer
                format: int32
              bar_field:
                type: string
        required:
          - result

And it will be correctly rendered as:

FooServiceResponse visualization

Note, that allOf is enough for this to work and no discriminator field is used. This is good, because it works and this is important, as I think, tools will be able to generate code without discriminator field.


All the answers here are excellent already, but I just want to add a minor note about composition versus inheritance. According to the Swagger/OpenAPI Spec, to implement composition, using the allOf property is enough, as @oblalex correctly points out. However, to implement inheritance, you need to use allOf with discriminator, as in the example by @TomaszSętkowski.

Also, I found some more Swagger examples of both composition and inheritance at API Handyman. They're part of an excellent Swagger/OpenAPI tutorial series by Arnaud Lauret that I think everyone should check out.


The Swagger 2.0 standard example you have shared depicts a composition relationship, specifically it captures an "is a kind of" super-type/sub-type relationship however it is not polymorphism in and of itself.

It would be if you could reference the base definition of Pet as an input parameter, then pick Cat or enter a Cat JSON object as the value for the input request, and have this acceptable to Swagger UI.

I could not get this to directly work.

The best I could get working was to set additionalProperties to true on the base object (e.g. Pet), specify Pet using JSON pointer reference as the input schema, and finally copy and paste my Cat JSON value object into Swagger UI. Since the additional properties are allowed Swagger UI generated a valid input request payload.