Skip to content
mitchinsPublic

About

A fast and light Python web framework for cloudflare workers

Resources

Stars

8 stars

Watchers

0 watching

Forks

Repository files navigation

Kinglet Logo

Kinglet

Lightning-fast Python web framework for Cloudflare Workers

CI Quality Gate Status codecov PyPI version Python 3.12 License: MIT

Quick Start

Install: pip install kinglet or add dependencies = ["kinglet"] to pyproject.toml

import os

from kinglet import Kinglet, CorsMiddleware, Response, cache_aside_d1

app = Kinglet(root_path="/api")

# Flexible middleware (v1.4.2+)
app.add_middleware(CorsMiddleware(allow_origin="*"))

# Routes are default-deny (2.0): each is public=True or carries an auth decorator
@app.post("/auth/login", public=True)
async def login(request):
    data = await request.json()
    return {"token": "jwt-token", "user": data["email"]}

@app.get("/api/data", public=True)
@cache_aside_d1(cache_type="api_data", ttl=1800)  # D1 caching (v1.5.0+)
async def get_data(request):
    return {"data": "cached_in_prod_fresh_in_dev"}

On app.asgi, request bodies are read only when accessed and are limited to 1 MiB by default. Set Kinglet(max_body_size=8 * 1024 * 1024) for larger uploads. The limit counts actual received bytes, including requests without Content-Length; exceeding it returns HTTP 413. Authentication that only needs headers runs before body reads.

Why Kinglet?

Feature Kinglet FastAPI Flask
Bundle Size 272KB 7.8MB 1.9MB
Testing No server needed TestServer required Test client required
Workers Ready ✅ Built-in ❌ Complex setup ❌ Not compatible

Key Features

Core: Decorator routing, typed parameters, flexible middleware, auto error handling, serverless testing Cloudflare: D1/R2/KV helpers, D1-backed caching, environment-aware policies, CDN-aware URLs Database: Micro-ORM for D1 with migrations, field validation, bulk operations (v1.6.0+) Security: Default-deny routes, JWT validation, TOTP/2FA, fine-grained auth/ownership decorators Developer: Full type hints, debug mode, request validation, zero-dependency testing OpenAPI: Auto-generated Swagger/ReDoc docs from routes and models (v1.8.0+)

Examples

Typed Parameters & Auth:

from kinglet.authz import require_auth   # or wrap your own with @security_decorator

@app.get("/users/{user_id}")
@require_auth                            # JWT auth; route decorator stays outermost
async def get_user(request):
    user_id = request.path_param_int("user_id")  # Validates or returns 400
    limit = request.query_int("limit", 10)       # Query params with defaults
    return {"user": user_id, "limit": limit}

Flexible Middleware & Caching:

# Configure middleware with parameters
cors = CorsMiddleware(allow_origin="*", allow_methods="GET,POST")
app.add_middleware(cors)

# D1-backed caching (v1.5.0+) - faster and cheaper for <1MB responses
@app.get("/api/data", public=True)
@cache_aside_d1(cache_type="api_data", ttl=1800)  # D1 primary, R2 fallback
async def get_data(request):
    return {"data": "expensive_query_result"}

# R2-backed caching for larger responses
@app.get("/api/large", public=True)
@cache_aside(cache_type="large_data", ttl=3600)  # Environment-aware
async def get_large_data(request):
    return {"data": "large_expensive_query_result"}

D1 Micro-ORM (v1.6.0+):

from kinglet import Model, StringField, IntegerField

class Game(Model):
    title = StringField(max_length=200)
    score = IntegerField(default=0)

# Simple CRUD with field validation
game = await Game.objects.create(db, title="Pac-Man", score=100)
top_games = await Game.objects.filter(db, score__gte=90).order_by("-score").all()

Use explicit keywords for boolean-shaped IntegerField arguments: IntegerField(default=True) and IntegerField(index=True). Positional booleans such as IntegerField(True) are rejected as ambiguous.

Security & Access Control:

@app.get("/admin/debug")
@require_dev()                    # 404 in production (blackhole) - satisfies the route policy
@geo_restrict(allowed=["US"])     # 451 elsewhere - a filter, not auth (needs public/auth too)
async def debug_endpoint(request):
    return {"debug": "sensitive data"}

Testing (No Server):

def test_api():
    client = TestClient(app)
    status, headers, body = client.request("GET", "/users/123")
    assert status == 200

OpenAPI/Swagger Documentation (v1.8.0+):

from kinglet import SchemaGenerator, Response

@app.get("/openapi.json", public=True)
async def openapi_spec(request):
    generator = SchemaGenerator(app, title="My API", version="1.0.0")
    return Response(generator.generate_spec())

@app.get("/docs", public=True)
async def swagger_ui(request):
    generator = SchemaGenerator(app, title="My API")
    return Response(generator.serve_swagger_ui(), content_type="text/html")

# Auto-generates docs from validators and models
# Visit /docs for interactive Swagger UI

Documentation


Built for Cloudflare Workers Python community. Need help?

About

A fast and light Python web framework for cloudflare workers

Resources

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages