OpenAPI 3.1 generation for hayate —
built from what your app already knows: routes from app.routes, request
schemas from your validators, response schemas from one decorator. No magic
inference, no schema-library lock-in.
Status: alpha (0.2.x). The emitted document passes the official
openapi-spec-validatorand feedsopenapi-typescriptfor end-to-end TypeScript types. The internal design memo (Japanese, per project convention) lives in DESIGN.md. Security schemes, multipart uploads, and strict inline typing are included. Release history is in CHANGELOG.md.
from hayate import Hayate
from hayate_openapi import OpenApi, describe, validated
import msgspec
class BookIn(msgspec.Struct):
title: str
app = Hayate()
@app.post("/books", validated("json", BookIn)) # validator + schema tag in one
@describe(status=201, summary="Create a book")
async def create(c):
book = c.req.valid("json") # BookIn instance — validation still runs
return c.json({"title": book.title}, status=201)
OpenApi(app, title="Bookstore", version="1.0.0").register(app)
# GET /openapi.json is live; or emit statically:
# python -m hayate_openapi main:app --title Bookstore --version 1.0.0| Source | What it provides |
|---|---|
app.routes (hayate ≥ 0.8) |
every method + path, converted to OpenAPI templating (:id → {id}) |
validated(target, T) |
request body / query / form schemas — a tagging wrapper around the core validator, behavior-identical |
@describe(...) |
summary, tags, response schemas, operationId — all optional, all additive |
hayate-auth middleware can supply operation security automatically:
@app.get("/documents", auth.require_oauth_token("documents:read"))
async def documents(c):
return c.json([])
OpenApi(
app,
title="API",
version="1",
security_schemes=auth.openapi_security_schemes(),
).register(app)Use @describe(security=[]) for an explicitly public operation. For uploads,
combine validated("form", schema, media_type="multipart/form-data") with
binary_file() in a raw schema.
Schema conversion goes through a SchemaProvider protocol. msgspec and
pydantic are auto-detected (guarded imports); a plain dict is taken as
literal JSON Schema. The package itself depends only on hayate.
TypeScript types, the recommended recipe:
python -m hayate_openapi main:app --title API --version 1.0.0 -o openapi.json
npx openapi-typescript openapi.json -o src/api-types.tsDocs UI: serve the JSON and point any renderer at it — e.g. one line of Scalar or Redoc HTML. Nothing is bundled.
- Routes with real HTTP verbs; WebSocket routes and wildcard mounts
(
/api/auth/*) are skipped. - Responses you declare. Undeclared operations get a bare 200 — the generator never invents schemas.
- Operations with a validator automatically document the 400
application/problem+jsonfailure the framework actually returns. - Cookie, Bearer, and OAuth 2.0 security requirements, including combined route middleware requirements.
- JSON, URL-encoded, and multipart request bodies, including binary file parts.
MIT