How is it possible to avoid the duplication of 401 error response for each api path? ( OpenApi 3 )

Viewed 522

I defined my authentication in security and components/securitySchemes. In the Swagger documentation about response, they provide this example:

paths:
  /something:
    get:
      ...
      responses:
        ...
        '401':
           $ref: '#/components/responses/UnauthorizedError'
    post:
      ...
      responses:
        ...
        '401':
          $ref: '#/components/responses/UnauthorizedError'
...
components:
  responses:
    UnauthorizedError:
      description: Authentication information is missing or invalid
      headers:
        WWW_Authenticate:
          schema:
            type: string

I have a lot more paths than two, and to access any of them, the client has to be authenticated. I would like to avoid the '401' definition for each path, and define it once globally, if it is possible somehow.

How is it possible to use this response for each path?

'401':
  $ref: '#/components/responses/UnauthorizedError'
2 Answers

You can't, if you want the response to show up on that particular endpoint.

I avoid the repetition in favour of important errors by not adding them to each endpoint and instead putting the well understood standard responses - 401, 403, 429, 404, 503 - on either a status endpoint or on the root method. eg:

  '/{foo}/{bar}/status':
    get:
      tags:
        - api
      responses:
        '200':
          description: successful response with a resource
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/statusResponse'
        '400':
          $ref: '#/components/responses/400_error_response'
        '401':
          $ref: '#/components/responses/401_error_response'
        '403':
          $ref: '#/components/responses/403_error_response'
        '404':
          $ref: '#/components/responses/404_error_response'          
        '500':
          $ref: '#/components/responses/500_error_response'

The boilerplate responses would then all refer to the standard responses.

Real endpoints then typically have a well defined response specific to that opearation and the things that may go specifically wrong.

  '/{foo}/{bar}/segment':
    post:
      summary: checks username is found or not
      description: |-
        checks username and returns 204 if found else 403
      operationId: checkUsername
      tags:
        - Login
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/segment_POST_request'
      responses:
        '200':
          $ref: '#/components/responses/segment_POST_200_response'
        '403':
          $ref: '#/components/responses/403_forbidden_response'
        '500':
          $ref: 
 '#/components/responses/500_internal_server_error_replayable_response'

One thing I ensure is for error responses, the possible error codes for that operation are included in the response, like so:

    500_internal_server_error_replayable_response:
      description: |-
        The following `500 Internal Server Error` errors can occur during the API processing.
        
        | errorCode | cause | details |
        | - | - | - |
        | `SOME.ERROR.500` | There was an error processing the request either in Foo or on a downstream system | A request to something failed, Bar or Baz however the error is recoverable and can be replayed |
        | `SOME.ERROR.014` | Baz returned a `404` during the migration operation, even though it has previously confirmed the member exists | This is a fatal error during the migration process and cannot be recovered. The request should not be replayed. |
      headers:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/errorResponse'
          example:
            status: '500'
            errorCode: SOME.ERROR.500
            errorDescription: The request could not be processed
            errorSubCode: ''
            furtherDetails: ''
            docLink: ''     

Use an extension to add them in automatically as part of your build script.

What you're looking to do is not part of the OpenAPI standard. Responses are not inherited from parent objects; they must be explicitly added to every endpoint. You can include a $ref for each response, but with a well-defined spec with lots of endpoints, you'll end up with a lot of duplication, and it's likely an endpoint might miss a response or two over time as additions and modifications are made. Plus, it's simply not a good authoring experience.

To prevent this problem, add an extension to your responses, like this:

responses:
  x-default-responses: true
  '418':
    description: Additional responses go here

The above spec will be the one that you work from. When done, have a script scan your completed yaml file, and replace that line with your list of default responses. Use this modified spec as the next stage of your development process (whatever it is you're using your OpenAPI spec for, be it documentation, testing, or SDK generation).

While you could have your script add the responses without the extension property (after all, it is just YAML you're writing), I recommend you don't do this. The extension makes your intent clear, so other editors will understand additional responses are present. Additionally, it is likely that (either now or one day in the future) you will have endpoints that do not conform to your default responses, so you wouldn't want to accidentally add those.

Related