OpenAPIHandler documents APIs

v1.72.0 OpenAPIHandler automatically generates OpenAPI specifications for your APIs.

OpenAPIHandler

Let’s start with a FunctionHandler:

url:
  compare:
    pattern: /compare
    handler: FunctionHandler
    kwargs:
      function: openapicalc.compare
      methods: GET # Only allow GET, not PUT/POST/...
      openapi: # Optional OpenAPI info
        summary: List Comparison FunctionHandler

… which exposes the function compare(x, y) from openapicalc.py. It checks if sum(x) > sum(y), where x and y are arrays of numbers.

from gramex.transforms import handler
from typing import List
from typing_extensions import Annotated


@handler
def compare(
    x: Annotated[List[float], 'First list'] = [],
    y: Annotated[List[float], 'Second list'] = [],
) -> bool:
    '''
    Return True if the first list (x) is larger than the second list (y)
    '''
    return True if sum(x) > sum(y) else False

Compare sum(2, 3) > sum(4)

You can now add an OpenAPIHandler that generates an OpenAPI JSON for this FunctionHandler.

url:
  compare:
    pattern: /$YAMLURL/my-compare-url
    handler: FunctionHandler
    kwargs:
      function: str('Hello world')
  openapi:
    pattern: /$YAMLURL/docs
    handler: OpenAPIHandler
    kwargs:
      info:
        title: Gramex Microservices
        description: >
          [Gramex](https://gramener.com/gramex/) OpenAPIHandler
        version: 0.1.2
      servers:
        - url: ..
          description: Gramex OpenAPIHandler demo
      urls: # List of url: keys (not pattern) to expose.
        - compare # "compare" is the key under url:, not the pattern
        - "*" # Use '*' to match any string

See the OpenAPI output

You can control the indentation by passing ?indent= to the OpenAPIHandler.

To allow users to interact with the API with a UI, use Swagger UI.

<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4.15.5/swagger-ui.css"
/>

<div id="swagger-ui"></div>
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4.15.5/swagger-ui-bundle.js"></script>
<script>
  const ui = SwaggerUIBundle({
    url: "docs", // OpenAPIHandler URL
    dom_id: "#swagger-ui",
    presets: [
      SwaggerUIBundle.presets.apis,
      SwaggerUIBundle.SwaggerUIStandalonePreset,
    ],
  });
</script>

See the API UI for compare

OpenAPIHandler Configuration

The kwargs: can take the following optional keys:

In addition, the handlers it exposes (e.g. FunctionHandler) can use these kwargs:

openapi:
  get:
    responses:
      "200":
        description: Successful Response
        content:
          application/json: {}
      "400":
        description: Bad request
        content:
          text/html:
            example: Bad request

Note that this overrides any configurations OpenAPIHandler generates. So if you have a FunctionHandler with @handler, any parameters it generates will be over-written by your kwargs.openapi: configuration.

FunctionHandler OpenAPI

If you expose a FunctionHandler via OpenAPIHandler, it’s best to use it with gramex.transforms.handler, like this:

from gramex.transforms import handler

@handler
def compare(x, y):
    '''Describe the function'''
    return sum(x) > sum(y)

The function docstring (e.g. Describe the function above) will describe the API endpoint.

You can add function annotations that will define types in the OpenAPI. For example, the argument x can be defined as:

FormHandler OpenAPI

If you expose a FormHandler via OpenAPIHandler, you must explicitly specify the columns, like this:

url:
  flagdata:
    pattern: flagdata
    handler: FormHandler
    kwargs:
      url: flags.csv
      columns: [Name, Continent, Symbols, Shapes, Stripes]

See the API UI for flagdata