Skip to main content

A familiar HTTP Service Framework for Python.

Project description

Responder

A familiar HTTP Service Framework for Python, powered by Starlette.

import responder

api = responder.API()

@api.route("/{greeting}")
async def greet_world(req, resp, *, greeting):
    resp.text = f"{greeting}, world!"

if __name__ == "__main__":
    api.run()
$ pip install responder

That's it. Supports Python 3.11+.

The Basics

  • resp.text sends back text. resp.html sends back HTML. resp.content sends back bytes.
  • resp.media sends back JSON (or YAML, with content negotiation).
  • resp.file("path.pdf") serves a file with automatic content-type detection.
  • File(...) uploads use streamed UploadFile objects; await file.save(path) writes them to disk.
  • req.headers is case-insensitive. req.params gives you query parameters.
  • Both sync and async views work — the async is optional.

Highlights

# Type-safe route parameters
@api.route("/users/{user_id:int}")
async def get_user(req, resp, *, user_id):
    resp.media = {"id": user_id}

# HTTP method filtering
@api.route("/items", methods=["POST"])
async def create_item(req, resp):
    data = await req.media()
    resp.media = {"created": data}

# Route-local hooks
def require_json(req, resp):
    if not req.is_json:
        resp.status_code = 415
        resp.media = {"error": "JSON required"}

@api.post("/events", before=require_json)
async def events(req, resp):
    resp.media = await req.media()

# Local dependencies
from responder import Depends

def current_user(req):
    return req.headers.get("X-User")

@api.get("/me")
def me(req, resp, *, user=Depends(current_user)):
    resp.media = {"user": user}

# Side-effect dependencies
@api.get("/ready", dependencies=[Depends(current_user)])
def ready(req, resp):
    resp.media = {"ready": True}

# Route-level auth with OpenAPI security
from responder.ext.auth import BearerAuth

auth = BearerAuth(tokens=["s3cret"])

@api.get("/private", auth=auth)
def private(req, resp, *, user):
    resp.media = {"user": user}

# App-level auth with public route opt-out
secured_api = responder.API(auth=auth)

@secured_api.get("/health", auth=None)
def health(req, resp):
    resp.media = {"ok": True}

# Optional auth accepts anonymous requests but still rejects bad credentials
optional_auth = auth.optional()

@api.get("/maybe", auth=optional_auth)
def maybe(req, resp, *, user):
    resp.media = {"user": user}

# Class-based views
@api.route("/things/{id}")
class ThingResource:
    def on_get(self, req, resp, *, id):
        resp.media = {"id": id}
    def on_post(self, req, resp, *, id):
        resp.text = "created"

# Before-request hooks (auth, rate limiting, etc.)
@api.route(before_request=True)
def check_auth(req, resp):
    if not req.headers.get("Authorization"):
        resp.status_code = 401
        resp.media = {"error": "unauthorized"}

# Custom error handling
@api.exception_handler(ValueError)
async def handle_error(req, resp, exc):
    resp.status_code = 400
    resp.media = {"error": str(exc)}

# Problem-details enrichment
def problem_handler(payload, request, exc):
    payload["type"] = f"https://example.com/problems/{payload['status']}"
    payload["instance"] = request.url.path

api = responder.API(problem_handler=problem_handler, request_id=True)

# Lifespan events
from contextlib import asynccontextmanager

@asynccontextmanager
async def lifespan(app):
    print("starting up")
    yield
    print("shutting down")

api = responder.API(lifespan=lifespan)

# GraphQL
import graphene
api.graphql("/graphql", schema=graphene.Schema(query=Query))

# WebSockets
@api.route("/ws", websocket=True)
async def websocket(ws):
    await ws.accept()
    while True:
        name = await ws.receive_text()
        await ws.send_text(f"Hello {name}!")

# Mount WSGI/ASGI apps
from flask import Flask
flask_app = Flask(__name__)
api.mount("/flask", flask_app)

# Background tasks
@api.route("/work")
def do_work(req, resp):
    @api.background.task
    def process():
        import time; time.sleep(10)
    process()
    resp.media = {"status": "processing"}

Built-in OpenAPI docs, cookie-based sessions, gzip compression, static file serving, Jinja2 templates, and a production uvicorn server. Install responder[server] to add Granian for production ASGI serving.

Route convertors: str, int, float, uuid, path.

Framework errors use RFC 9457-style application/problem+json responses by default; pass problem_details=False to keep the legacy error format. Pass problem_handler= to enrich those payloads; request IDs are included when request_id=True or structured logging is enabled. OpenAPI documents the shared ProblemDetails schema, and generated clients expose it as APIError.problem.

Documentation

https://responder.kennethreitz.org

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

responder-7.0.4.tar.gz (179.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

responder-7.0.4-py3-none-any.whl (124.7 kB view details)

Uploaded Python 3

File details

Details for the file responder-7.0.4.tar.gz.

File metadata

  • Download URL: responder-7.0.4.tar.gz
  • Upload date:
  • Size: 179.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for responder-7.0.4.tar.gz
Algorithm Hash digest
SHA256 1dd49907d4372e17fe11db7dc469288d22692e137468e61f2eb6d7902aed50bc
MD5 1573647b6237a574715c8376f9becf56
BLAKE2b-256 adaa0ff829bd58b4cddab96b81690a4fe2f2d44959081358bfc749b44710b2f2

See more details on using hashes here.

File details

Details for the file responder-7.0.4-py3-none-any.whl.

File metadata

  • Download URL: responder-7.0.4-py3-none-any.whl
  • Upload date:
  • Size: 124.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for responder-7.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 2e0bb887a4c80adf56ec36d783486175ba0d40b1713a418ab1dd5c6ebea9b383
MD5 f19cd5b2e29b7aa0e907be379c0cafeb
BLAKE2b-256 3ba3d1520762eaa9eddd98a47edcc42ee1cfd178dbee632f175380694d9a48c1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page