Files
woggioni 9a68d10868 Add kaya-openapi package for automatic OpenAPI spec generation
- New packages/kaya-openapi with OpenAPIMixin, @operation decorator,
  and generate_spec() that walks the routing tree
- Enables kaya-core's Tree.register to expose the original handler
  callback as an instance attribute for metadata introspection
- Registers GET /openapi.json and GET /docs (Swagger UI) routes
- Supports  and  path parameters, docstring
  descriptions, @operation metadata, and excludes wildcard/WS routes
- Adds example/openapi.py, updates CI, README, and requirements
2026-07-25 09:20:19 +00:00

3.3 KiB

kaya-openapi

Automatic OpenAPI specification generation for the Kaya lightweight ASGI web framework.

The package provides an OpenAPIMixin that inspects a KayaApp's routing tree and serves:

  • an OpenAPI 3.1 JSON document (default: GET /openapi.json)
  • a Swagger UI page to browse it interactively (default: GET /docs)

Usage

from kaya.core import HttpContext, KayaApp
from kaya.openapi import OpenAPIMixin, operation

app = KayaApp(mixins=[OpenAPIMixin(title='My API', version='1.0.0')])


@app.GET('/users/${user_id:int}')
@operation(summary='Get a user',
           tags=['users'],
           responses={
               200: {'description': 'The user'},
               404: {'description': 'User not found'},
           })
async def get_user(ctx: HttpContext, user_id: int) -> None:
    ...

Run the app with any ASGI/RSGI server and open http://localhost:8000/docs.

How routes are mapped

  • Static segments and parameters are converted to OpenAPI path templating:
    • /users/${user_id}/users/{user_id} (string path parameter)
    • /users/${user_id:int}/users/{user_id} (integer path parameter)
  • Wildcard routes (*) are skipped: they cannot be expressed in OpenAPI path syntax.
  • Websocket routes are skipped: OpenAPI does not model websockets.
  • Method-agnostic routes (registered with app.route(path) without methods) are documented under all standard HTTP methods, since they respond to all of them.
  • The mixin's own endpoints are excluded from the document unless include_self=True.

The document is generated on every request to the spec endpoint, so routes registered after the mixin is applied are always included.

Operation metadata

The @operation decorator attaches OpenAPI metadata to a route handler. All fragments are plain dicts merged verbatim into the generated operation object, so any valid OpenAPI 3.1 construct can be used:

@operation(summary='...',            # operation summary
           description='...',        # defaults to the handler docstring
           tags=['users'],
           operation_id='getUser',
           request_body={...},       # OpenAPI requestBody object
           responses={200: {...}},   # per-status-code response objects
           parameters=[...],         # extra/overriding parameter objects
           deprecated=False,
           hidden=False)             # exclude from the document

parameters entries whose name and in match an auto-generated path parameter override it; all others are appended.

Configuration

OpenAPIMixin(
    title='My API',                # info.title (required)
    version='1.0.0',               # info.version (required)
    description='',                # info.description
    spec_path='/openapi.json',     # where the JSON document is served
    docs_path='/docs',             # where Swagger UI is served
    servers=[{'url': 'https://api.example.com'}],
    openapi_version='3.1.0',
    include_self=False,            # include spec/docs endpoints in the document
)

The document can also be generated programmatically without serving it:

from kaya.openapi import generate_spec

spec = generate_spec(app, title='My API', version='1.0.0')