mirror of
https://github.com/kennethreitz/kennethreitz.org.git
synced 2026-06-17 21:20:57 +00:00
6b6ce3f9c8
- Every page under /software rewritten or tightened: 165 em-dashes removed, download stats corrected against live PyPI numbers, broken kjvstudy/httpbin links fixed (homepage too) - Full rewrites: sample pack, clint, autoenv, osx-gcc-installer, games and legacy indexes; Responder promoted to Major Projects - New essay documenting today's changes, the Obsidian sync pipeline (kr-vault plugin + sync-repo.py), and uv; added to essays index Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
104 lines
3.7 KiB
Markdown
104 lines
3.7 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
|
|
|
|
- [**A Framework of One's Own**](/essays/2026-06-11-a_framework_of_ones_own): The full story: the 2018 web, the five-year freeze, and the AI-assisted revival.
|
|
- [**This Site Now Runs on Responder**](/essays/2026-03-22-this_site_now_runs_on_responder): The afternoon this site ported from Flask.
|
|
- [**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.
|