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.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 |
| Requests & responses | Read query, header, cookie, path, and body parameters; shape JSON, streaming, file, and custom responses |
| Dependencies | Sub-dependencies, generator yield teardown, async context managers, overrides |
| Forms | URL-encoded and multipart form field injection |
| Uploads | UploadFile with multipart streaming |
| Static files | StaticFiles with app.mount() |
| Templating | Optional Jinja2 Templates / TemplateResponse |
| Middleware | CORS, GZip, HTTPS redirect, trusted host, security headers, timing, request ID |
| Background tasks | Post-response work |
| WebSockets | Text/bytes/json frames, path params, and subscriptions |
| Sessions | Signed session cookies via SessionMiddleware (stdlib-only signing) |
| Security | API-key header / query / cookie, OAuth2 password bearer, HTTP Basic |
| Rate limiting | Dependency-based token / fixed / sliding window limits with memory + Redis storage |
| Caching | Server-side route response cache with shared memory + Redis storage |
| OpenAPI | Auto-generated 3.1 docs with reusable schemas, route metadata, and Swagger UI |
| API versioning | Path, header, Accept, and deprecation versioning |
| GraphQL | Strawberry schema over HTTP and WebSocket with subscriptions and file uploads |
| Config | Immutable typed Config container with @Config.register / Config.get |
| Testing | Built-in TestClient and AsyncTestClient for ASGI and RSGI |
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 |
caching.py |
Server-side route response caching |
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 |
query_method.py |
HTTP QUERY method with body-aware caching |
Advanced and integrations¶
| Example | What it shows |
|---|---|
api_versioning.py |
Path, header, Accept, and deprecation versioning |
config.py |
Typed Config classes |
exception_handlers.py |
Custom exception handlers |
graphql.py |
GraphQL queries, mutations, subscriptions, and file uploads |
json_encoder.py |
Explicit JSON encoder selection |
max_body_size.py |
Global max_content_length |
model_backends.py |
msgspec / pydantic / custom model backends |
route_extras.py |
Route decorator metadata |
static_files.py |
Static file serving |
templates.py |
Optional Jinja2 templating |
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.
- Dependencies - share logic with dependency injection.
- Uploads - multipart file uploads with
UploadFile. - Testing - test apps in-process with
TestClientandAsyncTestClient. - API Versioning - version by path, header, or Accept header.
- GraphQL - expose a Strawberry schema over HTTP and WebSocket.
- Deployment - run in production on RSGI or ASGI.