Icon for Responder: A Familiar HTTP Service Framework

Responder: A Familiar HTTP Service Framework

Responder is a web framework for Python that flips Requests inside out. If Requests is how you consume HTTP, Responder is how you serve it — using the same mental model.

$ uv add responder

What It Looks Like

import responder

api = responder.API()

@api.route("/")
def home(req, resp):
    resp.html = "<h1>Hello, world.</h1>"

@api.route("/api/data")
def data(req, resp):
    resp.media = {"message": "Hello from Responder", "status": "ok"}

@api.route("/greet/{name}")
async def greet(req, resp, *, name):
    resp.text = f"Hello, {name}!"

if __name__ == "__main__":
    api.run()

resp.text sends text. resp.html sends HTML. resp.media sends JSON. The async keyword is optional — use it when you need it, skip it when you don't.

Built-In Batteries

import responder

api = responder.API()

# Built-in background tasks.
@api.route("/upload")
async def upload(req, resp):
    data = await req.media()

    @api.background.task
    def process(data):
        # This runs after the response is sent.
        expensive_operation(data)

    process(data)
    resp.media = {"status": "processing"}

# GraphQL support out of the box.
import graphene

class Query(graphene.ObjectType):
    hello = graphene.String(name=graphene.String(default_value="world"))
    def resolve_hello(self, info, name):
        return f"Hello, {name}!"

schema = graphene.Schema(query=Query)
api.add_route("/graph", schema)

# OpenAPI schema generation.
# Visit /docs for interactive API documentation.

# WebSocket support.
@api.route("/ws", websocket=True)
async def websocket(ws):
    await ws.accept()
    while True:
        data = await ws.receive_text()
        await ws.send_text(f"Echo: {data}")

Background tasks, GraphQL, WebSockets, OpenAPI docs, and Jinja2 templates — all built in. No extensions to install. No configuration to fumble with.

The Idea

I wanted to take the API primitives from Requests and put them into a web framework. The niceties of Flask and the performance philosophy of Falcon, unified with a Requests-like interface for responses. Setting resp.content sends bytes. Setting resp.media sends JSON. Case-insensitive headers. Familiar status codes. If you know Requests, you already know half of Responder.

It was a bit ahead of its time. Some of these ideas — automatic async handling, type-aware serialization, built-in OpenAPI — showed up later in FastAPI, which I'd recommend for production use today. But Responder is back in active development, and this site runs on it. The experiment became the production framework after all.

The deeper question Responder tried to answer: why do we accept that consuming an API and serving an API should feel like completely different activities? They're the same protocol. The mental model should be the same.

Install

$ uv add responder

Resources