Files
kennethreitz.org/data/software/responder.md
T
kennethreitz 9e7d363648 Add NumPy as Synth Engine essay, update software pages
New essay on physical modeling synthesis in pure Python — Karplus-Strong
strings, tabla strokes, djembe cross-choking, Hammond organ drawbars.

Software pages:
- Responder: mention this site runs on it, link to colophon
- All pages: change pip install to uv add

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 05:04:56 -04:00

102 lines
3.5 KiB
Markdown

# Responder: A Familiar HTTP Service Framework
Responder is a web framework for Python that flips [Requests](/software/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
```python
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
```python
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](/colophon). 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
```bash
$ uv add responder
```
## Resources
- [Documentation](https://responder.kennethreitz.org/)
- [Source Code on GitHub](https://github.com/kennethreitz/responder)
- [Python Package Index](https://pypi.org/project/responder/)
## Related
- [**Requests**](/software/requests) — The client-side library whose philosophy Responder mirrors.
- [**From HTTP to Consciousness**](/essays/2025-08-27-from_http_to_consciousness) — The design thinking behind both libraries.
- [**Programming as Spiritual Practice**](/essays/2025-08-26-programming_as_spiritual_practice) — Building tools as a contemplative act.