v1.72.0 OpenAPIHandler automatically generates OpenAPI specifications for your APIs.
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.
?indent=2 indents with 2 spaces?indent=0 shows compressed output (same as not specifying an indent)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
The kwargs: can take the following optional keys:
urls: A list of url: keys to expose as OpenAPI. For example:urls: ['compare', 'max'] exposes the URLs compare: and max:urls: ['server-*'] exposes all URLs beginning with the key server-urls: ['*'] exposes all URLsinfo: Sets the info: object in the OpenAPI spec. Under info:title: sets the titledescription: sets the overall API descriptioninfo: attributesserver: Sets the server: object in the OpenAPI spec. Under server:url: sets the root location of the API. Typically, this is /, or the URL where Gramex is hosteddescription: sets the server descriptionserver: attributesIn addition, the handlers it exposes (e.g. FunctionHandler) can use these kwargs:
methods: list of HTTP methods supported. For example:methods: GET only exposes the GET method on the handlermethods: [GET, PUT, DELETE] exposes GET, PUT and DELETEopenapi: adds to the Path Item Object
in the OpenAPI spec. Under openapi:summary: sets the path summarydescription: sets the path descriptionget: sets the GET request parameters. Specifically, under get:, you can add responses: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.
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:
x: accepts any input, e.g. ?x=a or ?x=1x: float: accepts only a number, e.g. ?x=1x: List[float] accepts a list of numbers, e.g. ?x=1&x=2. You can import List from typingx: Annotated[float, 'first value'] accepts a number, and adds the description “first value”x: Annotated[List[float], 'first list'] accepts a list of number, and adds the description “first list”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