OpenAPI & Swagger UI
The openviper.openapi package auto-generates an OpenAPI 3.1.0 schema from
registered routes and their Python type hints, and serves interactive
Swagger UI and ReDoc documentation pages.
Overview
The framework inspects every registered route’s handler signature and, where
present, its serializer_class attribute to build a complete OpenAPI schema.
The schema is served as JSON at /open-api/schema.json and two interactive
UI pages are mounted at /open-api/docs (Swagger UI) and
/open-api/redoc (ReDoc) by default.
No extra code or decorator is required - the schema is generated automatically from route registration and type annotations.
Key Functions
openviper.openapi.schema
- generate_openapi_schema(routes, title='OpenViper API', description='', version='1.0.0') dict
Generate an OpenAPI 3.1.0 schema dict from a list of
Routeobjects.Path parameters (e.g.
{id:int}) are included asparameters.Return type annotations that are Pydantic models are reflected into
responses["200"]["content"].When a
serializer_classis set on a class-based view, it is used to produce therequestBodyschema.Python built-in types are mapped to JSON Schema types automatically.
Results are cached by a SHA-256 fingerprint of the routes, title, description, and version; call
reset_openapi_cache()to invalidate.
- reset_openapi_cache() None
Clear the generated schema cache. Useful after dynamic route registration in tests.
- filter_openapi_routes(routes: list[Route]) list[Route]
Filter a list of
Routeobjects according toOPENAPI["exclude"]."__ALL__"→ returns[].A
list[str]of prefixes → drops routes whose path starts with any of the given prefixes (case-insensitive, leading/normalised).Empty list or missing setting → returns all routes unchanged.
Any other value triggers a warning and returns all routes unchanged.
- request_schema(serializer_cls: type) Callable[[RouteHandler], RouteHandler]
Decorator that attaches a serializer class to a route handler. The OpenAPI schema generator uses it to produce a
requestBodyentry so that Swagger UI displays the input form correctly.Works with both function-based and class-based views:
@router.post("/blogs") @request_schema(BlogSerializer) async def create_blog(request): ...
For class-based views, prefer setting
serializer_classon theViewsubclass instead - it is picked up automatically.
- tag_from_path(path: str) str
Derive a meaningful OpenAPI tag from a route path. Skips path parameters, generic prefixes (
api,v1,v2,v3), and version segments. Returns the first remaining segment capitalised, or"Root"when none is found.
- python_type_to_schema(annotation) dict
Convert a Python type annotation to a JSON Schema dict. Handles built-in types,
list[X],dict,Optional[X]/Uniontypes, and Pydantic models that exposemodel_json_schema().
- extract_path_params(path: str) list[dict]
Extract
{name:type}segments from a path template. Parameter names exceeding 128 characters are rejected to prevent resource-exhaustion vectors.
- openapi_path(path: str) str
Convert an OpenViper path (e.g.
/users/{id:int}) to the OpenAPI form (e.g./users/{id}).
- resolve_request_schema(handler: RouteHandler) type | None
Determine the request body schema class for a handler. Resolution order:
handler.openapi_request_schema- set byrequest_schema()handler.view_class.serializer_class- class-based view attributeDocstring tags (
Request: <Name>/Body: <Name>)Source-code auto-detection (
<Name>.validate()calls)
- format_operation_description(docstring: str) str
Render structured docstrings as safe HTML for documentation UIs. Recognises
Request:,Body:,Example Request:, andExample Response:sections and formats them with appropriate HTML tags.
- build_operation(route: Route, method: str) dict
Build an OpenAPI operation object for a given route and HTTP method. Combines path parameters, request body, response schema, tags, and per-route security into a single operation dict.
- build_request_body(handler, method, hints, doc_handler=None) dict | None
Resolve the request body schema for a handler at a given HTTP method. Checks the serializer, docstring examples, inline schemas, and parameter annotations in order.
- build_per_route_security(handler: RouteHandler) list[dict] | None
Return an explicit
securitylist for a handler when it restricts authentication. ReturnsNonewhen no per-routeauthentication_classesare set, letting the global security requirement remain in effect.
- build_responses(method, docstring, response_schema) dict
Build the
responsesdict for an OpenAPI operation. Includes the200success response with its schema, example responses from docstrings, and a422validation error entry for mutating methods.
- OPENAPI_REQUEST_SCHEMA_ATTR
The attribute name (
"openapi_request_schema") used to carry the schema class on a handler function, set byrequest_schema().
openviper.openapi.router
openviper.openapi.utils
Re-exports the public helpers so callers can import from a single, stable location without depending on internal module structure.
- filter_openapi_routes(routes) list[Route]
Filter a list of
Routeobjects according toOPENAPI["exclude"]. Re-exported fromschema.
- should_register_openapi() bool
Return
Truewhen the OpenAPI router should be registered. Re-exported fromrouter.
openviper.openapi.ui
openviper.openapi.schema - Types
- class RouteHandler
A
Protocoldescribing the structural interface of a route handler callable. Handlers must be callable and may carry optional metadata attributes set by decorators or view machinery:__name__(str) - function name__module__(str) - module nameview_class(type | None) - class-based view classview_action(str | None) - view action nameopenapi_request_schema(type | None) - set byrequest_schema()authentication_classes(list[type]) - auth classes__globals__(dict[str, Any]) - function globals
Example Usage
Automatic Schema (Zero Configuration)
from openviper import OpenViper
app = OpenViper(
title="Blog API",
version="1.0.0",
)
# Schema available at /open-api/schema.json
# Swagger UI at /open-api/docs
# ReDoc at /open-api/redoc
Richer Schema via Pydantic Serializers
Attach a serializer_class to a class-based view to give Swagger UI an
input form for the request body:
from openviper.http.views import View
from openviper.serializers import Serializer
from openviper.http.response import JSONResponse
class CreatePostSerializer(Serializer):
title: str
body: str
tags: list[str] = []
class PostCreateView(View):
serializer_class = CreatePostSerializer
async def post(self, request) -> JSONResponse:
data = CreatePostSerializer.validate(await request.json())
post = await Post.objects.create(**data.model_dump())
return JSONResponse(post._to_dict(), status_code=201)
PostCreateView.register(router, "/posts")
Method Documentation
OpenAPI uses the first line of each handler docstring as the operation summary and the remaining lines as the description. For class-based views, each HTTP method contributes its own documentation.
For small request bodies, an inline Body or Request block can build a
request schema without a serializer:
class PostCreateView(View):
async def post(self, request) -> JSONResponse:
"""Create a post.
Body: {
"title": str,
"state": str, # "DRAFT", "PUBLISHED"
"author_id": str (UUID)
}
"""
...
Supported inline field types are str, int, float, bool,
bytes, list, dict, and UUID. Quoted values in a trailing
comment are exposed as enum choices. Use a serializer for nested structures,
validation rules, or schemas reused across endpoints.
Return Type Annotation for Response Schema
from pydantic import BaseModel
class PostOut(BaseModel):
id: int
title: str
body: str
@router.get("/posts/{post_id:int}")
async def get_post(request, post_id: int) -> PostOut:
post = await Post.objects.get(id=post_id)
return JSONResponse(post._to_dict())
The PostOut model is reflected into the OpenAPI responses section for
this endpoint.
Accessing the Raw Schema
from openviper.openapi.schema import generate_openapi_schema
from openviper.routing.router import Router
import json
router = Router()
# ... register routes ...
schema = generate_openapi_schema(
routes=router.routes,
title="My API",
version="2.0.0",
)
print(json.dumps(schema, indent=2))
OpenAPI Exclusion
The OPENAPI dict’s exclude key lets you disable the OpenAPI router
entirely or remove specific base routes from the generated schema without
changing your routing registration.
Value |
Effect |
|---|---|
|
Non-admin routes appear in the schema; docs endpoints are active. |
|
The OpenAPI router is not registered. |
|
Routes whose path starts with |
|
Routes under |
Disable OpenAPI
Set OPENAPI["exclude"] = "__ALL__" to prevent the docs and schema endpoints
from being registered. This is recommended for production deployments where
you do not want to expose API documentation.
# settings.py
OPENAPI = {
"exclude": "__ALL__",
}
After applying this setting, any request to /open-api/openapi.json,
/open-api/docs, or /open-api/redoc will receive a 404 response.
Exclude Routes by Prefix
Pass a list of route-path prefixes (without the leading /) to remove
matching paths from the generated schema. The docs endpoint itself remains
accessible; only the schema content is filtered.
# Remove all /admin/* routes from the schema
OPENAPI = {"exclude": ["admin"]}
# Remove /admin/* and /blogs/* routes
OPENAPI = {"exclude": ["admin", "blogs"]}
# Remove /admin/*, /blogs/*, and /internal/* routes
OPENAPI = {"exclude": ["admin", "blogs", "internal"]}
Admin Routes
Routes under /admin are hidden from generated schemas by default. To
expose them intentionally, set the admin URL explicitly:
OPENAPI = {"admin_url": "/admin"}
Prefix Matching Rules
Matching is case-insensitive:
"Admin"and"admin"produce the same result.Leading slashes in a prefix are normalised:
"/admin"is treated identically to"admin".Only complete path segments are matched. A prefix of
"blogs"will exclude/blogsand/blogs/postsbut not/blogsearchor/blog.
Security Benefits
Using OPENAPI["exclude"] reduces the attack surface of your API:
Hide internal admin endpoints from the public schema.
Prevent automated scanners from discovering route structures.
Disable the schema entirely in production to avoid information leakage.
See also
OpenAPI & Swagger UI - main OpenAPI reference page.
API Reference - Exclusion Helpers
openviper.openapi.schema
- filter_openapi_routes(routes: list[Route]) list[Route]
Filter a list of
Routeobjects according toOPENAPI["exclude"]."__ALL__"→ returns[].A
list[str]of prefixes → drops routes whose path starts with any of the given prefixes (case-insensitive, leading/normalised).Empty list or missing setting → returns all routes unchanged.
Any other value triggers a warning and returns all routes unchanged.
openviper.openapi.router
Configuration Reference
OPENAPI
Type: dict
Default: see key table below
A dict consolidating all OpenAPI configuration. Each key corresponds to
a flat OPENAPI_* setting; the flat names are still accepted for
backward compatibility via read_openapi_settings().
Key |
Default |
Description |
|---|---|---|
|
|
Master switch. |
|
|
Title shown in Swagger UI and ReDoc. |
|
|
API version string in the schema |
|
|
API description in the schema |
|
|
URL at which the raw JSON schema is served. |
|
|
URL for the Swagger UI page. |
|
|
URL for the ReDoc page. |
|
|
When set, admin routes are included in the schema. |
|
|
Route exclusion list (see above). |
Configuration Examples
Disable OpenAPI entirely in production:
# settings.py
OPENAPI = {"exclude": "__ALL__"}
Remove admin routes from the public schema:
OPENAPI = {"exclude": ["admin"]}
Remove multiple path prefixes:
OPENAPI = {"exclude": ["admin", "blogs", "internal", "health"]}
Customise title and version:
OPENAPI = {
"title": "My Service API",
"version": "2.1.0",
"description": "Internal microservice for order processing.",
}
Security Considerations
Disable the schema entirely (
OPENAPI["exclude"] = "__ALL__") in production to prevent automated scanners from discovering your API surface.Use prefix exclusion to hide
/adminand other sensitive sub-trees from publicly served documentation.Schema exposure is listed as a risk in OWASP API Security Top 10 (API7: Security Misconfiguration).
OPENAPI["exclude"]directly mitigates this risk.