Home
The speed of a velociraptor, the flexibility of Python.
A micro framework built to hunt.
VelociPy doesn't lumber through the jungle with bulked-up gear. It stays light, feathered, and alert - reading the terrain, then striking with a sickle claw when the moment is right. When the hunt calls for sharper talons, tougher hide, or a keener nose, it grows them - then sheds them when the job is done.
The result is a micro framework with a tiny core and a set of swappable
adapters and optional modules. Start with nothing but the core. Add
validation, OpenAPI, security, rate limiting, multipart uploads, Redis storage,
and more by importing what you need. Only httpx2 is required at runtime.
Why hunt with VelociPy?¶
- Light bones, fast muscles - a radix-tree router, lightweight request/response objects, and precomputed handler plans keep the core tiny.
- Hunt on any terrain - the same app runs unchanged on ASGI (Uvicorn, Hypercorn) or RSGI (Granian).
- Swap your claws - pick your JSON encoder, model validator, and protocol independently.
- Keen senses - typed parameters for query, header, cookie, path, and body injection.
- Armor and discipline - optional security helpers, rate limiting, and request-size limits.
Quick start¶
Create main.py:
from velocipy import VelociPy
app = VelociPy()
@app.get("/")
async def hello():
return {"message": "Hello, VelociPy!"}
Run it under the fastest terrain:
# RSGI - the open plain
granian main:app --interface rsgi --reload
# ASGI - the dense, well-trodden forest
uvicorn main:app --reload
Visit http://localhost:8000/docs for the auto-generated Swagger UI.
RSGI is the fastest strike
For raw throughput, run VelociPy under Granian with RSGI. For compatibility with the broader async ecosystem, use ASGI.
A hunt in one file¶
This single file shows the essentials: validation, dependencies, rate limiting, headers, and OpenAPI tags.
from typing import Annotated
from pydantic import BaseModel
from velocipy import Depends, Header, Query, VelociPy
from velocipy.limiter import (
Limiter,
RateLimitAlgorithm,
RateLimitRule,
rate_limit,
)
from velocipy.limiter.storage import MemoryStorage
app = VelociPy()
limiter = Limiter(storage=MemoryStorage())
class Item(BaseModel):
name: str
price: float
async def page_params(
page: Annotated[int, Query(ge=1)] = 1,
size: Annotated[int, Query(le=100)] = 20,
):
return {"page": page, "size": size}
rule = RateLimitRule(
name="items",
limit=60,
window=60,
algorithm=RateLimitAlgorithm.TOKEN_BUCKET,
)
@app.get("/items", response_model=list[Item], tags=["items"])
async def list_items(
params: Annotated[dict, Depends(page_params)],
_: None = Depends(rate_limit(limiter, rules=[rule])),
x_request_id: Annotated[str | None, Header()] = None,
):
return [{"name": "hoodie", "price": 49.99}]
Test the catch in-process:
from velocipy.testing import TestClient
client = TestClient(app)
assert client.get("/items?page=1").status_code == 200
See examples/basic.py for a runnable version with lifespan events, custom OpenAPI metadata, and authentication dependencies.
The raptor at a glance¶
| Gear | What's included |
|---|---|
| Routing | Radix-tree router, static / parametric / typed / catch-all routes, sub-routers, named routes |
| Validation | msgspec.Struct, pydantic.BaseModel, or custom adapters |
| Parameters | Query, header, cookie, path, and body injection via type hints |
| Dependencies | Sub-dependencies, generator yield teardown, async context managers, overrides |
| Protocols | Native ASGI and RSGI support, WebSocket endpoints |
| Responses | JSON, streaming, files, redirects, custom response classes |
| Middleware | CORS, GZip, HTTPS redirect, trusted host, security headers, timing, request ID |
| Security | API-key header / query / cookie, OAuth2 password bearer |
| Rate limiting | Dependency-based, async-only, token / fixed / sliding window, memory + Redis |
| Request safety | Global max_content_length with automatic 413 Payload Too Large |
| Sessions | Signed session cookies via SessionMiddleware (stdlib-only signing) |
| Form parameters | Form(...) field injection and adapter-neutral form models |
| Typed config | Immutable Config container with @Config.register / Config.get |
| Templating | Optional Jinja2 Templates / TemplateResponse |
| Static files | StaticFiles with app.mount() |
| Uploads | UploadFile with multipart streaming |
| Background tasks | Post-response work |
| Testing | Built-in TestClient and AsyncTestClient for ASGI and RSGI |
| OpenAPI | Auto-generated 3.1 docs with reusable schemas and Swagger UI |
Choose your terrain¶
| Terrain | Best for |
|---|---|
| RSGI (Granian) | Maximum speed, lowest overhead. The open plain. |
| ASGI (Uvicorn / Hypercorn) | Compatibility with the broad async ecosystem. The dense forest. |
Example hunts¶
The examples/ directory has small, runnable demos. Start with the essentials, then move through common patterns and advanced integrations.
Start here¶
| Example | What it shows |
|---|---|
basic.py |
Validation, OpenAPI, dependencies, lifespan |
routing.py |
Routes, path params, sub-routers, catch-all routes |
query_params.py |
Query parameter parsing and validation |
dependencies.py |
Dependency injection patterns |
responses.py |
Custom and streaming responses |
Common patterns¶
| Example | What it shows |
|---|---|
security.py |
API-key and OAuth2 authentication |
rate_limit.py |
Dependency-based rate limiting |
middleware.py |
Function, class, and built-in middleware |
testing.py |
Testing with TestClient and AsyncTestClient |
uploads.py |
UploadFile and multipart streaming |
background_tasks.py |
Post-response background work |
sessions.py |
Signed session cookies |
form_parameters.py |
Form(...) field injection |
websocket.py |
WebSocket echo and path params |
headers_cookies.py |
Header and cookie injection |
Advanced and integrations¶
| Example | What it shows |
|---|---|
json_encoder.py |
Explicit JSON encoder selection |
model_backends.py |
msgspec / pydantic / custom model backends |
templates.py |
Optional Jinja2 templating |
static_files.py |
Static file serving |
config.py |
Typed Config classes |
max_body_size.py |
Global max_content_length |
exception_handlers.py |
Custom exception handlers |
route_extras.py |
Route decorator metadata |
uvicorn_server.py |
Running a VelociPy app with Uvicorn via ASGI |
Next steps¶
- Getting started - install VelociPy and build your first app.
- Routing - routes, parameters, and handlers.
- Requests & responses - read the request and shape the response.
- Deployment - run in production on RSGI or ASGI.