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=1
x: float
: accepts only a number, e.g. ?x=1
x: List[float]
accepts a list of numbers, e.g. ?x=1&x=2
. You can import List
from typing
x: 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