Forum Discussion

silverbullet2's avatar
silverbullet2
New Contributor
3 years ago

OpenAPI Discrimintors

I have been trying to figure out how to handle a situation where a query string parameter changes the response object.  Here's my example:
The default response is this:
endpoint: http://myapi.com/pets

response: 

 

{
pet:{
name: "barry",
color: "white",
age: 17
}
}

 

 

It can also have a query string parameter where it adjusts the response as follows:

endpoint: http://myapi.com/pets?attributes=name,color

response: 

 

 

{
pet:{
name: "barry"
}
}

 

 

 

It also supports comma separated lists for the parameter as well like this:

endpoint: http://myapi.com/pets

response: 

 

 

{
pet:{
name: "barry",
color: "white"
}
}

 

 

 

Is there a way to describe this scenario using discriminators?  I know that I can list the responses as optional, but that wasn't ideal.

 

Thanks!

  • Hi silverbullet2,

     

    I’m not 100% sure what you are trying to achieve in your example.

     

    Are you trying to allow the consumer specify the properties that they are interested in and only return those properties?

    `http://myapi.com/pets` --> return all pets according to the defined response schema

    `http://myapi.com/pets?attributes=name,color` --> return all pets and only return the `name` and `color` property for each pet

     

    Or are you trying to return two or more different types of objects (aka polymorphism)? If so then you would use `oneOf` or `anyOf` to describe those schemas. If so, then you may want to use a discriminator. You can find some examples of that at https://swagger.io/specification/#discriminator-object 

     

    Or are you looking for inheritance by defining a base schema, and then inherit from it using `allOf`? You can also use a discriminator if needed.

     

    An rough example of the inheritance with a discriminator could be to have a base "Insurance" schema and then to extend the insurance schema using `allOf`:

     

    type: object
    description: Insurance
    discriminator:
      propertyName: insuranceType
      mapping:
        house: '#/components/schemas/HouseInsurance'
        car: '#/components/schemas/CarInsurance'
        travel: '#/components/schemas/TravelInsurance'
    properties:
      policyNumber:
        description: The insurance policy number
        type: string
        example: IXP12345678
      startAt:
        type: string
        format: date-time
        pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[012][0-9]:[0-5][0-9]:[0-5][0-9]Z$'
        description: The date-time from which this policy becomes active
      endAt:
        type: string
        format: date-time
        pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[012][0-9]:[0-5][0-9]:[0-5][0-9]Z$'
        description: The date-time from which this policy ceases

     

     

     

    allOf:
      - $ref: ./Insurance.yaml
      - type: object
        description: Car Insurance
        properties:
          make:
            description: The make of care
            type: string
            example: Nissan
          range:
            description: car model range
            type: string
            example: hatchback
          model:
            description: car model
            type: string
            example: micra
          registrationNumber:
            description: the registration number of the vehicle   
            type: string
            example: 00G1011     
          value:
            type: number
            format: double
            description: The value of the vehicle
            example: 2000.0 

     

     

    In general, discriminators can add complexity to the design and they must be used in conjunction with `anyOf`, `oneOf`, `allOf`. The discriminator must apply to the same level of the schema it is declared.

     

    • silverbullet2's avatar
      silverbullet2
      New Contributor

      frankkilcommins, I am trying to implement the first option you gave.  I want to allow the consumer to specify the properties they are interested in and only return those properties.  Is there a way to utilize discriminators for that or what would be the best way to accomplish this?

      • frankkilcommins's avatar
        frankkilcommins
        Staff

        Hi silverbullet2 

         

        Discriminators would generally not be used where you want to allow the consumer describe the shape of the response representation. Think of them more as a shortcut to possibly allow a consumer make a choice from a selection of possible pre-defined schemas. This does not really fit into the example you describe as you just want to reduce the representation of a single schema.

         

        There is no definitive right way to achieve your goal, but a common technique is to have a query parameter (named something like ‘fields’ or ‘properties’ or ‘attributes’ or ‘select’) to allow a consumer specify a subset of properties they would like to be returned with a successful response, rather than the full representation of the resource.

         

        It’s up to you as the API provider to decide what query parameter naming to choose, and to decide if you limit to root level properties or also enable the consumer to select subset of properties within nested objects or arrays that may be returned in the representation. If you’re going to allow selection to subsets within nested objects or arrays, then it would be common to apply a sub-selector expression (generally this would be based on XPath syntax or object.member dot notation)

         

        Some examples:

        http://someapiurl.com?fields=color,name –> return all pets and only the color and name properties

        http://someapiurl.com?fields=color,name,location/city OR http://someapiurl.com?fields=color,name,location.city –> return all pets and only the color and name root level properties as well as the city property from the location object.

        http://someapiurl.com?fields=color,name,hobbies(id, name) –> return all pets and only return the color, name properties, and just the id and name properties for items in the hobbies array

         

        It’s also relatively common that providers leverage bits and pieces from specifications like OData, SCIM or syntaxes like Lucene for advanced filtering/querying (these approaches can increase complexity however).

         

        Things to keep in mind:

        • URLs have limits so work to ensure that consumers can not hit URL limits (good practice is to ensure that any combination of params will not result in length greater than ~2000 characters)
        • You will need to validate request to ensure that the selector parameter is valid and return the appropriate 400 series HTTP Status code if it contains an error or is otherwise invalid

         

        If you wanted to go full query orientated, then you could consider GraphQL which has it’s own pro’s an con’s.

         

        In general, I would always promote good design where the minimal representation to address the business goal is returned. Separate your APIs into separate resources (or sub-resources), giving your consumer the ability to make more concise calls. Progressively enhance the consumer experience with options to expand or embed additional resources when appropriate.

         

        Ensure you leverage HTTP caching when/where possible to ensure that consumers can cache representations while its safe to do so.

         

        I hope this helps you achieve what you need to do with your API.