# kaya-openapi Automatic [OpenAPI](https://www.openapis.org/) specification generation for the [Kaya](https://github.com/woggioni/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 ```python 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: ```python @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 ```python 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: ```python from kaya.openapi import generate_spec spec = generate_spec(app, title='My API', version='1.0.0') ```