Files
2026-06-12 05:30:58 +00:00

1119 lines
116 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en" data-content_root="./">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Feature Tour &#8212; responder 4.0.0 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=5ecbeea2" />
<link rel="stylesheet" type="text/css" href="_static/basic.css?v=b08954a9" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css?v=27fed22d" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/design-elements.e5416f61bae5d36adc6d722a2b6f8cff.css?v=452a8e97" />
<script src="_static/documentation_options.js?v=3304f9e4"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=fd10adb8"></script>
<script>
</script>
<script src="_static/design-elements.bbdccc18c4abea9397628f9fea3d48c2.js?v=03c7770e"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="Deployment" href="deployment.html" />
<link rel="prev" title="Quick Start" href="quickstart.html" />
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="feature-tour">
<h1>Feature Tour<a class="headerlink" href="#feature-tour" title="Link to this heading"></a></h1>
<p>This section walks through Responders features in depth. Each section
explains the concept, shows working code, and explains the design choices
behind it. If youre new to web development, this is a good place to learn
how modern web frameworks work under the hood.</p>
<section id="method-filtering">
<h2>Method Filtering<a class="headerlink" href="#method-filtering" title="Link to this heading"></a></h2>
<p>HTTP defines several <em>methods</em> (also called verbs) that describe what a
client wants to do with a resource. The most common are:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">GET</span></code> — retrieve data</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">POST</span></code> — create something new</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">PUT</span></code> — replace something entirely</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">PATCH</span></code> — update part of something</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">DELETE</span></code> — remove something</p></li>
</ul>
<p>By default, a Responder route matches all methods. This is fine for simple
endpoints, but REST APIs typically map different methods to different
operations. Use the <code class="docutils literal notranslate"><span class="pre">methods</span></code> parameter to restrict a route:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/items&quot;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;GET&quot;</span><span class="p">])</span>
<span class="k">def</span><span class="w"> </span><span class="nf">list_items</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;items&quot;</span><span class="p">:</span> <span class="p">[]}</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/items&quot;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;POST&quot;</span><span class="p">],</span> <span class="n">check_existing</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_item</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">data</span> <span class="o">=</span> <span class="k">await</span> <span class="n">req</span><span class="o">.</span><span class="n">media</span><span class="p">()</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;created&quot;</span><span class="p">:</span> <span class="n">data</span><span class="p">}</span>
</pre></div>
</div>
<p>Note the <code class="docutils literal notranslate"><span class="pre">check_existing=False</span></code> — Responder normally prevents you from
registering two routes with the same path (to catch typos). When you
intentionally want multiple handlers for the same path with different
methods, you need to opt in.</p>
<p>Method-restricted routes get correct HTTP semantics for free:</p>
<ul class="simple">
<li><p>Requests with an unsupported method receive <code class="docutils literal notranslate"><span class="pre">405</span> <span class="pre">Method</span> <span class="pre">Not</span> <span class="pre">Allowed</span></code>
(not 404) with an <code class="docutils literal notranslate"><span class="pre">Allow</span></code> header listing what the path supports.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">OPTIONS</span></code> requests are answered automatically with the <code class="docutils literal notranslate"><span class="pre">Allow</span></code> header.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">HEAD</span></code> is accepted wherever <code class="docutils literal notranslate"><span class="pre">GET</span></code> is.</p></li>
</ul>
</section>
<section id="returning-values">
<h2>Returning Values<a class="headerlink" href="#returning-values" title="Link to this heading"></a></h2>
<p>Handlers normally communicate by mutating <code class="docutils literal notranslate"><span class="pre">resp</span></code>, but they can also just
return a value:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/users&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">list_users</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="k">return</span> <span class="p">[{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;alice&quot;</span><span class="p">}]</span> <span class="c1"># same as resp.media = [...]</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/hello&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="k">return</span> <span class="s2">&quot;hello, world!&quot;</span> <span class="c1"># same as resp.text = &quot;...&quot;</span>
</pre></div>
</div>
<p>A <code class="docutils literal notranslate"><span class="pre">dict</span></code> or <code class="docutils literal notranslate"><span class="pre">list</span></code> becomes <code class="docutils literal notranslate"><span class="pre">resp.media</span></code>, a <code class="docutils literal notranslate"><span class="pre">str</span></code> becomes
<code class="docutils literal notranslate"><span class="pre">resp.text</span></code>, and <code class="docutils literal notranslate"><span class="pre">bytes</span></code> become <code class="docutils literal notranslate"><span class="pre">resp.content</span></code>. Returning <code class="docutils literal notranslate"><span class="pre">None</span></code>
(the implicit default) leaves the response exactly as you set it, so
existing handlers are unaffected. Use whichever style reads better — for
quick JSON endpoints, returning the data directly is hard to beat.</p>
<p>Routes are also forgiving about trailing slashes: a request to <code class="docutils literal notranslate"><span class="pre">/users/</span></code>
when only <code class="docutils literal notranslate"><span class="pre">/users</span></code> is registered (or vice versa) receives a <code class="docutils literal notranslate"><span class="pre">307</span></code>
redirect to the canonical path, preserving the method and query string.
Pass <code class="docutils literal notranslate"><span class="pre">redirect_slashes=False</span></code> to <code class="docutils literal notranslate"><span class="pre">API()</span></code> for strict matching.</p>
</section>
<section id="class-based-views">
<h2>Class-Based Views<a class="headerlink" href="#class-based-views" title="Link to this heading"></a></h2>
<p>Function-based views are great for simple endpoints, but sometimes you want
to group related HTTP methods together into a single resource. This is
where class-based views come in — a pattern popularized by
<a class="reference external" href="https://falconframework.org/">Falcon</a>.</p>
<p>Responder dispatches to the appropriate method handler based on the HTTP
method:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/</span><span class="si">{greeting}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">class</span><span class="w"> </span><span class="nc">GreetingResource</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </span><span class="nf">on_get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">greeting</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">greeting</span><span class="si">}</span><span class="s2">, world!&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="nf">on_post</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">greeting</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;received&quot;</span><span class="p">:</span> <span class="n">greeting</span><span class="p">}</span>
<span class="k">def</span><span class="w"> </span><span class="nf">on_request</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">greeting</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Called on EVERY request, before the method-specific handler.&quot;&quot;&quot;</span>
<span class="n">resp</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;X-Greeting&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">greeting</span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">on_request</span></code> method is called for all HTTP methods, much like
middleware scoped to a single route. Method-specific handlers (<code class="docutils literal notranslate"><span class="pre">on_get</span></code>,
<code class="docutils literal notranslate"><span class="pre">on_post</span></code>, <code class="docutils literal notranslate"><span class="pre">on_put</span></code>, <code class="docutils literal notranslate"><span class="pre">on_delete</span></code>, etc.) are called after.</p>
<p>No inheritance required — just define a class with the right method names.
This is simpler than Djangos <code class="docutils literal notranslate"><span class="pre">View</span></code> classes and more Pythonic than
framework-specific base classes.</p>
</section>
<section id="lifespan-events">
<h2>Lifespan Events<a class="headerlink" href="#lifespan-events" title="Link to this heading"></a></h2>
<p>Real applications need to set up resources when they start (database
connection pools, ML models, caches) and tear them down when they stop.
This is called the application <em>lifespan</em>.</p>
<p>The modern approach is the <em>context manager</em> pattern, where startup and
shutdown are two halves of the same block:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">contextlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">asynccontextmanager</span>
<span class="nd">@asynccontextmanager</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">lifespan</span><span class="p">(</span><span class="n">app</span><span class="p">):</span>
<span class="c1"># Startup — runs before the first request</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;connecting to database...&quot;</span><span class="p">)</span>
<span class="k">yield</span>
<span class="c1"># Shutdown — runs after the server stops</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;closing connections...&quot;</span><span class="p">)</span>
<span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">lifespan</span><span class="o">=</span><span class="n">lifespan</span><span class="p">)</span>
</pre></div>
</div>
<p>Everything before <code class="docutils literal notranslate"><span class="pre">yield</span></code> runs at startup. Everything after runs at
shutdown. If startup fails, the server wont start. If shutdown raises,
its logged but the server still exits.</p>
<p>The traditional event decorator style also works:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">on_event</span><span class="p">(</span><span class="s2">&quot;startup&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">startup</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;starting up&quot;</span><span class="p">)</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">on_event</span><span class="p">(</span><span class="s2">&quot;shutdown&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">shutdown</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;shutting down&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>The context manager is preferred for new code — it keeps related startup
and shutdown logic together and makes resource cleanup more explicit.</p>
<p>For values that belong to the application as a whole, use <code class="docutils literal notranslate"><span class="pre">api.state</span></code>
a free-form namespace reachable from any handler via <code class="docutils literal notranslate"><span class="pre">req.api.state</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">on_event</span><span class="p">(</span><span class="s2">&quot;startup&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">connect</span><span class="p">():</span>
<span class="n">api</span><span class="o">.</span><span class="n">state</span><span class="o">.</span><span class="n">db</span> <span class="o">=</span> <span class="k">await</span> <span class="n">create_pool</span><span class="p">()</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/users&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">users</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="k">await</span> <span class="n">req</span><span class="o">.</span><span class="n">api</span><span class="o">.</span><span class="n">state</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">fetch_users</span><span class="p">()</span>
</pre></div>
</div>
<p>(For resources with teardown, app-scoped dependencies below are usually
the better fit.)</p>
</section>
<section id="dependency-injection">
<h2>Dependency Injection<a class="headerlink" href="#dependency-injection" title="Link to this heading"></a></h2>
<p>Views often need shared resources — a database session, a config object,
the current user. Rather than reaching for globals or recomputing them in
every handler, register them as <em>dependencies</em> and declare them as view
parameters:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">dependency</span><span class="p">()</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">db</span><span class="p">():</span>
<span class="n">session</span> <span class="o">=</span> <span class="k">await</span> <span class="n">create_session</span><span class="p">()</span>
<span class="k">yield</span> <span class="n">session</span>
<span class="k">await</span> <span class="n">session</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/users/{id:int}&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_user</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="nb">id</span><span class="p">,</span> <span class="n">db</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="k">await</span> <span class="n">db</span><span class="o">.</span><span class="n">fetch_user</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
</pre></div>
</div>
<p>Any view parameter (beyond <code class="docutils literal notranslate"><span class="pre">req</span></code> and <code class="docutils literal notranslate"><span class="pre">resp</span></code>) whose name matches a
registered dependency is injected automatically. Path parameters take
precedence over dependencies of the same name.</p>
<p>Providers can be:</p>
<ul class="simple">
<li><p><strong>Plain functions</strong> (sync or async) — the return value is injected.</p></li>
<li><p><strong>Generators</strong> (sync or async) — the yielded value is injected, and the
code after <code class="docutils literal notranslate"><span class="pre">yield</span></code> runs as teardown once the response is sent. This is
perfect for database sessions and other resources that need cleanup.</p></li>
</ul>
<p>Providers that accept a parameter receive the current <code class="docutils literal notranslate"><span class="pre">Request</span></code>, so
dependencies can be request-aware:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">dependency</span><span class="p">()</span>
<span class="k">def</span><span class="w"> </span><span class="nf">current_user</span><span class="p">(</span><span class="n">req</span><span class="p">):</span>
<span class="k">return</span> <span class="n">decode_token</span><span class="p">(</span><span class="n">req</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;Authorization&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">))</span>
</pre></div>
</div>
<p>Each dependency is resolved at most once per request, even when several
views (e.g. <code class="docutils literal notranslate"><span class="pre">on_request</span></code> and <code class="docutils literal notranslate"><span class="pre">on_get</span></code> in a class-based view) ask for
it. To register under a different name, pass it explicitly with
<code class="docutils literal notranslate"><span class="pre">&#64;api.dependency(name=&quot;db&quot;)</span></code> or call <code class="docutils literal notranslate"><span class="pre">api.add_dependency(&quot;db&quot;,</span> <span class="pre">provider)</span></code>.</p>
<p>For resources that should live as long as the application — connection
pools, ML models, expensive clients — use the <code class="docutils literal notranslate"><span class="pre">&quot;app&quot;</span></code> scope. The provider
runs once, on first use, and generator teardown is deferred until the
application shuts down:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">dependency</span><span class="p">(</span><span class="n">scope</span><span class="o">=</span><span class="s2">&quot;app&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">pool</span><span class="p">():</span>
<span class="n">pool</span> <span class="o">=</span> <span class="k">await</span> <span class="n">create_pool</span><span class="p">()</span>
<span class="k">yield</span> <span class="n">pool</span>
<span class="k">await</span> <span class="n">pool</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="c1"># runs at shutdown</span>
</pre></div>
</div>
<p>App-scoped providers cant take parameters — they outlive any single
request.</p>
<p>WebSocket handlers participate too: declare path parameters and
dependencies by name after the <code class="docutils literal notranslate"><span class="pre">ws</span></code> argument, and theyre injected the
same way (providers that take a parameter receive the WebSocket):</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/ws/</span><span class="si">{room}</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">websocket</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">chat</span><span class="p">(</span><span class="n">ws</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">room</span><span class="p">,</span> <span class="n">hub</span><span class="p">):</span>
<span class="k">await</span> <span class="n">ws</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span>
<span class="k">await</span> <span class="n">hub</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">room</span><span class="p">,</span> <span class="n">ws</span><span class="p">)</span>
</pre></div>
</div>
<p>Handlers that only take <code class="docutils literal notranslate"><span class="pre">ws</span></code> keep working unchanged — names are injected
only when declared.</p>
</section>
<section id="serving-files">
<h2>Serving Files<a class="headerlink" href="#serving-files" title="Link to this heading"></a></h2>
<p>Web applications often need to serve files — downloads, reports, images.
Responder makes this simple with <code class="docutils literal notranslate"><span class="pre">resp.file()</span></code>, which reads a file from
disk and sets the <code class="docutils literal notranslate"><span class="pre">Content-Type</span></code> header automatically using Pythons
<code class="docutils literal notranslate"><span class="pre">mimetypes</span></code> module:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/download&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">download</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">file</span><span class="p">(</span><span class="s2">&quot;reports/annual.pdf&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>You can override the content type if the automatic detection isnt right:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/image&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">image</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">file</span><span class="p">(</span><span class="s2">&quot;photos/cat.jpg&quot;</span><span class="p">,</span> <span class="n">content_type</span><span class="o">=</span><span class="s2">&quot;image/jpeg&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>For large files, use <code class="docutils literal notranslate"><span class="pre">resp.stream_file()</span></code> to avoid loading the entire
file into memory. This streams the file in chunks:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/export&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">export</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">stream_file</span><span class="p">(</span><span class="s2">&quot;data/export.csv&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>Both <code class="docutils literal notranslate"><span class="pre">resp.file()</span></code> and <code class="docutils literal notranslate"><span class="pre">resp.stream_file()</span></code> understand HTTP range
requests: clients sending <code class="docutils literal notranslate"><span class="pre">Range:</span> <span class="pre">bytes=...</span></code> receive <code class="docutils literal notranslate"><span class="pre">206</span> <span class="pre">Partial</span>
<span class="pre">Content</span></code> with the requested slice. This is what makes video seeking and
resumable downloads work — no extra code needed.</p>
<p>To prompt the browser to download rather than display, use
<code class="docutils literal notranslate"><span class="pre">resp.download()</span></code>, which streams the file (resumably) and sets
<code class="docutils literal notranslate"><span class="pre">Content-Disposition</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/export&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">export</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">download</span><span class="p">(</span><span class="s2">&quot;reports/annual.pdf&quot;</span><span class="p">,</span> <span class="n">filename</span><span class="o">=</span><span class="s2">&quot;Annual Report.pdf&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>Large <em>uploads</em> work the same way in reverse — iterate over the request
body in chunks instead of buffering it with <code class="docutils literal notranslate"><span class="pre">await</span> <span class="pre">req.content</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/upload&quot;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;POST&quot;</span><span class="p">])</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">upload</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="k">async</span> <span class="k">with</span> <span class="k">await</span> <span class="n">anyio</span><span class="o">.</span><span class="n">open_file</span><span class="p">(</span><span class="s2">&quot;incoming.bin&quot;</span><span class="p">,</span> <span class="s2">&quot;wb&quot;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="k">async</span> <span class="k">for</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="n">req</span><span class="o">.</span><span class="n">stream</span><span class="p">():</span>
<span class="k">await</span> <span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="conditional-requests">
<h2>Conditional Requests<a class="headerlink" href="#conditional-requests" title="Link to this heading"></a></h2>
<p>HTTP caching saves bandwidth and server time: clients remember a validator
for the responses theyve seen, and the server answers <code class="docutils literal notranslate"><span class="pre">304</span> <span class="pre">Not</span> <span class="pre">Modified</span></code>
— no body — when nothing changed. Set <code class="docutils literal notranslate"><span class="pre">resp.etag</span></code> or
<code class="docutils literal notranslate"><span class="pre">resp.last_modified</span></code> and Responder handles the comparison automatically:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/report&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">report</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">etag</span> <span class="o">=</span> <span class="n">compute_version_hash</span><span class="p">()</span>
<span class="n">resp</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="n">expensive_render</span><span class="p">()</span> <span class="c1"># skipped clients get a 304</span>
</pre></div>
</div>
<p>When the requests <code class="docutils literal notranslate"><span class="pre">If-None-Match</span></code> header matches the ETag (or
<code class="docutils literal notranslate"><span class="pre">If-Modified-Since</span></code> is at or after <code class="docutils literal notranslate"><span class="pre">last_modified</span></code>), the client
receives <code class="docutils literal notranslate"><span class="pre">304</span> <span class="pre">Not</span> <span class="pre">Modified</span></code> with empty body. <code class="docutils literal notranslate"><span class="pre">resp.last_modified</span></code>
accepts a <code class="docutils literal notranslate"><span class="pre">datetime</span></code> or a preformatted HTTP-date string:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">datetime</span><span class="w"> </span><span class="kn">import</span> <span class="n">datetime</span><span class="p">,</span> <span class="n">timezone</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/feed&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">feed</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">last_modified</span> <span class="o">=</span> <span class="n">datetime</span><span class="p">(</span><span class="mi">2026</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="n">tzinfo</span><span class="o">=</span><span class="n">timezone</span><span class="o">.</span><span class="n">utc</span><span class="p">)</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="n">load_feed</span><span class="p">()</span>
</pre></div>
</div>
<p>Per RFC 7232, <code class="docutils literal notranslate"><span class="pre">If-None-Match</span></code> takes precedence when both validators are
present, weak ETags (<code class="docutils literal notranslate"><span class="pre">W/&quot;...&quot;</span></code>) compare by their core value, and
conditional handling applies only to <code class="docutils literal notranslate"><span class="pre">GET</span></code> and <code class="docutils literal notranslate"><span class="pre">HEAD</span></code>.</p>
<p>Dont want to manage validators yourself? Turn on automatic ETags and
every <code class="docutils literal notranslate"><span class="pre">GET</span></code> response gets a content-hash tag, with <code class="docutils literal notranslate"><span class="pre">304</span></code> handling for
free (note: the body is still rendered server-side; you save bandwidth,
not compute):</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">auto_etag</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</pre></div>
</div>
<p>An explicitly set <code class="docutils literal notranslate"><span class="pre">resp.etag</span></code> always wins over the automatic one.</p>
<p>To control how long clients cache, set <code class="docutils literal notranslate"><span class="pre">Cache-Control</span></code> with the helper —
underscores become hyphens, <code class="docutils literal notranslate"><span class="pre">True</span></code> renders a bare directive:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">resp</span><span class="o">.</span><span class="n">cache_control</span><span class="p">(</span><span class="n">public</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">max_age</span><span class="o">=</span><span class="mi">3600</span><span class="p">)</span>
<span class="c1"># Cache-Control: public, max-age=3600</span>
</pre></div>
</div>
</section>
<section id="after-response-tasks">
<h2>After-Response Tasks<a class="headerlink" href="#after-response-tasks" title="Link to this heading"></a></h2>
<p>Work that shouldnt delay the response — sending emails, recording
analytics, cache warming — can be deferred until after the client has the
bytes:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/signup&quot;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;POST&quot;</span><span class="p">])</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">signup</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;ok&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">}</span>
<span class="n">resp</span><span class="o">.</span><span class="n">background</span><span class="p">(</span><span class="n">send_welcome_email</span><span class="p">,</span> <span class="s2">&quot;user@example.com&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>Sync functions run in a thread pool; async functions run on the event
loop. Multiple tasks run in the order scheduled. (For fire-and-forget work
from anywhere — not tied to a response — use <code class="docutils literal notranslate"><span class="pre">api.background</span></code> instead.)</p>
</section>
<section id="custom-error-handling">
<h2>Custom Error Handling<a class="headerlink" href="#custom-error-handling" title="Link to this heading"></a></h2>
<p>In production, you dont want your users to see raw Python tracebacks.
Responder lets you register custom handlers for specific exception types,
so you can return clean, structured error responses:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">exception_handler</span><span class="p">(</span><span class="ne">ValueError</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">handle_value_error</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">,</span> <span class="n">exc</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">status_code</span> <span class="o">=</span> <span class="mi">400</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;error&quot;</span><span class="p">:</span> <span class="nb">str</span><span class="p">(</span><span class="n">exc</span><span class="p">)}</span>
</pre></div>
</div>
<p>Now, any route that raises a <code class="docutils literal notranslate"><span class="pre">ValueError</span></code> will return a clean JSON
response with a 400 status code instead of a generic 500 error page.</p>
<p>This is a common pattern in API development — you define your own exception
classes for different error conditions, register handlers for each, and
your API always returns consistent, machine-readable error responses.</p>
<p>Built-in errors are content-negotiated automatically: clients that send
<code class="docutils literal notranslate"><span class="pre">Accept:</span> <span class="pre">application/json</span></code> get JSON error bodies for 404s and 405s
(<code class="docutils literal notranslate"><span class="pre">{&quot;error&quot;:</span> <span class="pre">&quot;Not</span> <span class="pre">Found&quot;}</span></code>), while browsers keep getting plain text.</p>
</section>
<section id="before-request-hooks">
<h2>Before-Request Hooks<a class="headerlink" href="#before-request-hooks" title="Link to this heading"></a></h2>
<p>Sometimes you need to run the same code before every request —
authentication checks, request logging, adding common headers, or setting
up per-request state. Before-request hooks let you do this without
duplicating code in every route:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="n">before_request</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">add_headers</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;X-API-Version&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;3.2&quot;</span>
</pre></div>
</div>
<p><strong>Short-circuiting</strong> is the really powerful part. If your hook sets
<code class="docutils literal notranslate"><span class="pre">resp.status_code</span></code>, the route handler is skipped entirely and the
response is sent immediately. This is the pattern for authentication:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="n">before_request</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">auth_check</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="k">if</span> <span class="s2">&quot;Authorization&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">req</span><span class="o">.</span><span class="n">headers</span><span class="p">:</span>
<span class="n">resp</span><span class="o">.</span><span class="n">status_code</span> <span class="o">=</span> <span class="mi">401</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;error&quot;</span><span class="p">:</span> <span class="s2">&quot;unauthorized&quot;</span><span class="p">}</span>
</pre></div>
</div>
<p>If the <code class="docutils literal notranslate"><span class="pre">Authorization</span></code> header is missing, the client gets a 401 response
and the actual route handler never runs. This is cleaner than adding
auth checks to every individual route.</p>
</section>
<section id="after-request-hooks">
<h2>After-Request Hooks<a class="headerlink" href="#after-request-hooks" title="Link to this heading"></a></h2>
<p>The complement to before-request hooks. After-request hooks run after the
route handler completes but before the response is sent. Theyre useful
for logging, adding response headers, or any post-processing:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">after_request</span><span class="p">()</span>
<span class="k">def</span><span class="w"> </span><span class="nf">log_response</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">req</span><span class="o">.</span><span class="n">method</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">req</span><span class="o">.</span><span class="n">full_url</span><span class="si">}</span><span class="s2"> -&gt; </span><span class="si">{</span><span class="n">resp</span><span class="o">.</span><span class="n">status_code</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">after_request</span><span class="p">()</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">add_timing</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s2">&quot;X-Served-By&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;responder&quot;</span>
</pre></div>
</div>
</section>
<section id="websocket-support">
<h2>WebSocket Support<a class="headerlink" href="#websocket-support" title="Link to this heading"></a></h2>
<p>HTTP is a request-response protocol — the client asks, the server answers.
But some applications need real-time, bidirectional communication: chat
apps, live dashboards, multiplayer games, collaborative editors.</p>
<p><a class="reference external" href="https://en.wikipedia.org/wiki/WebSocket">WebSockets</a> solve this by
upgrading an HTTP connection into a persistent, full-duplex channel where
both sides can send messages at any time:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/ws&quot;</span><span class="p">,</span> <span class="n">websocket</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">websocket</span><span class="p">(</span><span class="n">ws</span><span class="p">):</span>
<span class="k">await</span> <span class="n">ws</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">name</span> <span class="o">=</span> <span class="k">await</span> <span class="n">ws</span><span class="o">.</span><span class="n">receive_text</span><span class="p">()</span>
<span class="k">await</span> <span class="n">ws</span><span class="o">.</span><span class="n">send_text</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Hello </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">!&quot;</span><span class="p">)</span>
<span class="k">await</span> <span class="n">ws</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</pre></div>
</div>
<p>You can send and receive in multiple formats:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">send_text</span></code> / <code class="docutils literal notranslate"><span class="pre">receive_text</span></code> — plain text strings</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">send_json</span></code> / <code class="docutils literal notranslate"><span class="pre">receive_json</span></code> — JSON objects (auto-serialized)</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">send_bytes</span></code> / <code class="docutils literal notranslate"><span class="pre">receive_bytes</span></code> — raw binary data</p></li>
</ul>
<p>WebSocket routes are marked with <code class="docutils literal notranslate"><span class="pre">websocket=True</span></code> in the route decorator.
They receive a <code class="docutils literal notranslate"><span class="pre">ws</span></code> object instead of <code class="docutils literal notranslate"><span class="pre">req</span></code> and <code class="docutils literal notranslate"><span class="pre">resp</span></code>.</p>
</section>
<section id="server-sent-events-sse">
<h2>Server-Sent Events (SSE)<a class="headerlink" href="#server-sent-events-sse" title="Link to this heading"></a></h2>
<p>SSE is a simpler alternative to WebSockets for <em>one-way</em> real-time
communication — the server pushes events to the client, but the client
cant send messages back. This is perfect for live feeds, progress bars,
notification streams, and AI response streaming.</p>
<p>Unlike WebSockets, SSE works over plain HTTP, is automatically reconnected
by the browser, and doesnt require any special client-side libraries:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/events&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">events</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="nd">@resp</span><span class="o">.</span><span class="n">sse</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">stream</span><span class="p">():</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">):</span>
<span class="k">yield</span> <span class="p">{</span><span class="s2">&quot;data&quot;</span><span class="p">:</span> <span class="sa">f</span><span class="s2">&quot;message </span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">}</span>
</pre></div>
</div>
<p>On the client side, you consume SSE events with JavaScripts built-in
<code class="docutils literal notranslate"><span class="pre">EventSource</span></code> API:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">const</span> <span class="n">source</span> <span class="o">=</span> <span class="n">new</span> <span class="n">EventSource</span><span class="p">(</span><span class="s2">&quot;/events&quot;</span><span class="p">);</span>
<span class="n">source</span><span class="o">.</span><span class="n">onmessage</span> <span class="o">=</span> <span class="p">(</span><span class="n">event</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
<span class="n">console</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">event</span><span class="o">.</span><span class="n">data</span><span class="p">);</span>
<span class="p">};</span>
</pre></div>
</div>
<p>Each yielded value can be a string (treated as data) or a dict with the
standard SSE fields:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">yield</span> <span class="p">{</span><span class="s2">&quot;event&quot;</span><span class="p">:</span> <span class="s2">&quot;update&quot;</span><span class="p">,</span> <span class="s2">&quot;data&quot;</span><span class="p">:</span> <span class="s2">&quot;hello&quot;</span><span class="p">,</span> <span class="s2">&quot;id&quot;</span><span class="p">:</span> <span class="s2">&quot;1&quot;</span><span class="p">,</span> <span class="s2">&quot;retry&quot;</span><span class="p">:</span> <span class="s2">&quot;5000&quot;</span><span class="p">}</span>
<span class="k">yield</span> <span class="s2">&quot;simple string message&quot;</span>
</pre></div>
</div>
</section>
<section id="graphql">
<h2>GraphQL<a class="headerlink" href="#graphql" title="Link to this heading"></a></h2>
<p><a class="reference external" href="https://graphql.org/">GraphQL</a> is a query language for APIs that lets
clients request exactly the data they need — no more, no less. Instead of
multiple REST endpoints, you define a schema and let clients query it.</p>
<p>Responder includes built-in GraphQL support via
<a class="reference external" href="https://graphene-python.org/">Graphene</a>. Install it with the
<code class="docutils literal notranslate"><span class="pre">graphql</span></code> extra:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ uv pip install &#39;responder[graphql]&#39;
</pre></div>
</div>
<p>Then set up a full GraphQL endpoint with a single method call:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">graphene</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Query</span><span class="p">(</span><span class="n">graphene</span><span class="o">.</span><span class="n">ObjectType</span><span class="p">):</span>
<span class="n">hello</span> <span class="o">=</span> <span class="n">graphene</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="n">graphene</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="n">default_value</span><span class="o">=</span><span class="s2">&quot;stranger&quot;</span><span class="p">))</span>
<span class="k">def</span><span class="w"> </span><span class="nf">resolve_hello</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">info</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;Hello </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="n">api</span><span class="o">.</span><span class="n">graphql</span><span class="p">(</span><span class="s2">&quot;/graphql&quot;</span><span class="p">,</span> <span class="n">schema</span><span class="o">=</span><span class="n">graphene</span><span class="o">.</span><span class="n">Schema</span><span class="p">(</span><span class="n">query</span><span class="o">=</span><span class="n">Query</span><span class="p">))</span>
</pre></div>
</div>
<p>Visiting <code class="docutils literal notranslate"><span class="pre">/graphql</span></code> in a browser renders the
<a class="reference external" href="https://github.com/graphql/graphiql">GraphiQL</a> interactive IDE, where
you can explore your schema, write queries, and see results in real-time.
Programmatic clients can POST JSON queries to the same endpoint.</p>
<p>You can access the Responder request and response objects in your resolvers
through <code class="docutils literal notranslate"><span class="pre">info.context[&quot;request&quot;]</span></code> and <code class="docutils literal notranslate"><span class="pre">info.context[&quot;response&quot;]</span></code>.</p>
</section>
<section id="openapi-documentation">
<h2>OpenAPI Documentation<a class="headerlink" href="#openapi-documentation" title="Link to this heading"></a></h2>
<p><a class="reference external" href="https://www.openapis.org/">OpenAPI</a> (formerly Swagger) is the industry
standard for describing REST APIs. An OpenAPI specification lets you
auto-generate interactive documentation, client libraries, and validation
logic.</p>
<p>Responder generates OpenAPI specs from your code:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span>
<span class="n">title</span><span class="o">=</span><span class="s2">&quot;Pet Store&quot;</span><span class="p">,</span>
<span class="n">version</span><span class="o">=</span><span class="s2">&quot;1.0&quot;</span><span class="p">,</span>
<span class="n">openapi</span><span class="o">=</span><span class="s2">&quot;3.0.2&quot;</span><span class="p">,</span>
<span class="n">docs_route</span><span class="o">=</span><span class="s2">&quot;/docs&quot;</span><span class="p">,</span>
<span class="p">)</span>
</pre></div>
</div>
<p>This gives you:</p>
<ul class="simple">
<li><p>An OpenAPI schema at <code class="docutils literal notranslate"><span class="pre">/schema.yml</span></code></p></li>
<li><p>Interactive Swagger UI documentation at <code class="docutils literal notranslate"><span class="pre">/docs</span></code></p></li>
</ul>
<p>OpenAPI 3.1 is supported — pass <code class="docutils literal notranslate"><span class="pre">openapi=&quot;3.1.0&quot;</span></code>. The schema is served
as YAML by default; clients sending <code class="docutils literal notranslate"><span class="pre">Accept:</span> <span class="pre">application/json</span></code> get JSON,
and an <code class="docutils literal notranslate"><span class="pre">openapi_route</span></code> ending in <code class="docutils literal notranslate"><span class="pre">.json</span></code> (e.g. <code class="docutils literal notranslate"><span class="pre">&quot;/schema.json&quot;</span></code>)
serves JSON always.</p>
<p>Path parameters are documented automatically from your route patterns:
<code class="docutils literal notranslate"><span class="pre">/pets/{id:int}</span></code> produces a required integer path parameter in the spec,
with the OpenAPI-style template path (<code class="docutils literal notranslate"><span class="pre">/pets/{id}</span></code>).</p>
<p>There are three ways to document your endpoints.</p>
<p><strong>Pydantic models</strong> — the recommended approach. Use <code class="docutils literal notranslate"><span class="pre">request_model</span></code> and
<code class="docutils literal notranslate"><span class="pre">response_model</span></code> to annotate your routes, and Responder generates the
schema automatically. When <code class="docutils literal notranslate"><span class="pre">request_model</span></code> is set, request bodies are
also validated automatically — invalid inputs get a <code class="docutils literal notranslate"><span class="pre">422</span></code> response with
detailed error messages:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">pydantic</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseModel</span>
<span class="k">class</span><span class="w"> </span><span class="nc">PetIn</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
<span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">age</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">class</span><span class="w"> </span><span class="nc">PetOut</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
<span class="nb">id</span><span class="p">:</span> <span class="nb">int</span>
<span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">age</span><span class="p">:</span> <span class="nb">int</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/pets&quot;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;POST&quot;</span><span class="p">],</span>
<span class="n">request_model</span><span class="o">=</span><span class="n">PetIn</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">PetOut</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_pet</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">data</span> <span class="o">=</span> <span class="k">await</span> <span class="n">req</span><span class="o">.</span><span class="n">media</span><span class="p">()</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="o">**</span><span class="n">data</span><span class="p">}</span>
</pre></div>
</div>
<p>When <code class="docutils literal notranslate"><span class="pre">response_model</span></code> is set, the response is serialized through the
model — extra fields are stripped and types are enforced.</p>
<p><strong>YAML docstrings</strong> — for full control, embed OpenAPI YAML in the
docstring:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/pets&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">list_pets</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;A list of pets.</span>
<span class="sd"> ---</span>
<span class="sd"> get:</span>
<span class="sd"> description: Get all pets</span>
<span class="sd"> responses:</span>
<span class="sd"> 200:</span>
<span class="sd"> description: A list of pets</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">[{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Fido&quot;</span><span class="p">}]</span>
</pre></div>
</div>
<p><strong>Marshmallow schemas</strong> — if youre already using marshmallow:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">marshmallow</span><span class="w"> </span><span class="kn">import</span> <span class="n">Schema</span><span class="p">,</span> <span class="n">fields</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">schema</span><span class="p">(</span><span class="s2">&quot;Pet&quot;</span><span class="p">)</span>
<span class="k">class</span><span class="w"> </span><span class="nc">PetSchema</span><span class="p">(</span><span class="n">Schema</span><span class="p">):</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">fields</span><span class="o">.</span><span class="n">Str</span><span class="p">()</span>
</pre></div>
</div>
<p>All three approaches can be mixed in the same API. You can choose from
multiple documentation themes: <code class="docutils literal notranslate"><span class="pre">swagger_ui</span></code> (default), <code class="docutils literal notranslate"><span class="pre">redoc</span></code>,
<code class="docutils literal notranslate"><span class="pre">rapidoc</span></code>, or <code class="docutils literal notranslate"><span class="pre">elements</span></code>.</p>
</section>
<section id="route-groups">
<h2>Route Groups<a class="headerlink" href="#route-groups" title="Link to this heading"></a></h2>
<p>As your application grows, youll want to organize routes logically.
Route groups let you share a URL prefix across related endpoints — a
common pattern for API versioning:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">v1</span> <span class="o">=</span> <span class="n">api</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="s2">&quot;/v1&quot;</span><span class="p">)</span>
<span class="nd">@v1</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/users&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">list_users</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">[]</span>
<span class="nd">@v1</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/users/{user_id:int}&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">get_user</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">user_id</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="p">:</span> <span class="n">user_id</span><span class="p">}</span>
<span class="n">v2</span> <span class="o">=</span> <span class="n">api</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="s2">&quot;/v2&quot;</span><span class="p">)</span>
<span class="nd">@v2</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/users&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">list_users_v2</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;users&quot;</span><span class="p">:</span> <span class="p">[],</span> <span class="s2">&quot;total&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">}</span>
</pre></div>
</div>
<p>This keeps your code organized without affecting the routing logic.</p>
<p>Before-request hooks registered on a group only run for paths under the
groups prefix — handy for guarding a whole API version with one check:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@v1</span><span class="o">.</span><span class="n">before_request</span><span class="p">()</span>
<span class="k">def</span><span class="w"> </span><span class="nf">require_key</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="k">if</span> <span class="s2">&quot;X-Api-Key&quot;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">req</span><span class="o">.</span><span class="n">headers</span><span class="p">:</span>
<span class="n">resp</span><span class="o">.</span><span class="n">status_code</span> <span class="o">=</span> <span class="mi">401</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;error&quot;</span><span class="p">:</span> <span class="s2">&quot;missing API key&quot;</span><span class="p">}</span>
</pre></div>
</div>
</section>
<section id="mounting-other-apps">
<h2>Mounting Other Apps<a class="headerlink" href="#mounting-other-apps" title="Link to this heading"></a></h2>
<p>Responder can mount any WSGI or ASGI application at a subroute. This is
incredibly useful for gradual migrations — you can run Flask and Responder
side by side, moving routes over one at a time:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">flask</span><span class="w"> </span><span class="kn">import</span> <span class="n">Flask</span>
<span class="n">flask_app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
<span class="nd">@flask_app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">hello</span><span class="p">():</span>
<span class="k">return</span> <span class="s2">&quot;Hello from Flask!&quot;</span>
<span class="n">api</span><span class="o">.</span><span class="n">mount</span><span class="p">(</span><span class="s2">&quot;/flask&quot;</span><span class="p">,</span> <span class="n">flask_app</span><span class="p">)</span>
</pre></div>
</div>
<p>Requests to <code class="docutils literal notranslate"><span class="pre">/flask/</span></code> will be handled by Flask. Everything else goes
through Responder. Both WSGI and ASGI apps are supported — Responder
wraps WSGI apps in an ASGI adapter automatically.</p>
<p>You can also mount <a class="reference external" href="https://marimo.io/">marimo</a> notebooks as
interactive dashboards within your API:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">marimo</span>
<span class="n">server</span> <span class="o">=</span> <span class="p">(</span>
<span class="n">marimo</span><span class="o">.</span><span class="n">create_asgi_app</span><span class="p">()</span>
<span class="o">.</span><span class="n">with_app</span><span class="p">(</span><span class="n">path</span><span class="o">=</span><span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="n">root</span><span class="o">=</span><span class="s2">&quot;./notebooks/dashboard.py&quot;</span><span class="p">)</span>
<span class="o">.</span><span class="n">with_app</span><span class="p">(</span><span class="n">path</span><span class="o">=</span><span class="s2">&quot;/analysis&quot;</span><span class="p">,</span> <span class="n">root</span><span class="o">=</span><span class="s2">&quot;./notebooks/analysis.py&quot;</span><span class="p">)</span>
<span class="p">)</span>
<span class="n">api</span><span class="o">.</span><span class="n">mount</span><span class="p">(</span><span class="s2">&quot;/notebooks&quot;</span><span class="p">,</span> <span class="n">server</span><span class="o">.</span><span class="n">build</span><span class="p">())</span>
</pre></div>
</div>
<p>Notebooks are served at <code class="docutils literal notranslate"><span class="pre">/notebooks/</span></code> and <code class="docutils literal notranslate"><span class="pre">/notebooks/analysis</span></code>,
with full interactivity — reactive cells, widgets, plots, and all.</p>
</section>
<section id="cookies">
<h2>Cookies<a class="headerlink" href="#cookies" title="Link to this heading"></a></h2>
<p><a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies">Cookies</a> are
small pieces of data that the server asks the browser to store and send
back with every subsequent request. Theyre the foundation of sessions,
authentication tokens, and user preferences on the web.</p>
<p>Reading and writing cookies is straightforward:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Read cookies from the request</span>
<span class="n">session_id</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="n">cookies</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;session_id&quot;</span><span class="p">)</span>
<span class="c1"># Set a cookie on the response</span>
<span class="n">resp</span><span class="o">.</span><span class="n">cookies</span><span class="p">[</span><span class="s2">&quot;hello&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;world&quot;</span>
</pre></div>
</div>
<p>For production use, youll want to set security directives. The
<code class="docutils literal notranslate"><span class="pre">httponly</span></code> flag prevents JavaScript from reading the cookie (defending
against XSS attacks), and <code class="docutils literal notranslate"><span class="pre">secure</span></code> ensures its only sent over HTTPS:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">resp</span><span class="o">.</span><span class="n">set_cookie</span><span class="p">(</span>
<span class="s2">&quot;token&quot;</span><span class="p">,</span>
<span class="n">value</span><span class="o">=</span><span class="s2">&quot;abc123&quot;</span><span class="p">,</span>
<span class="n">max_age</span><span class="o">=</span><span class="mi">3600</span><span class="p">,</span> <span class="c1"># expires in 1 hour</span>
<span class="n">secure</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="c1"># HTTPS only</span>
<span class="n">httponly</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="c1"># no JavaScript access</span>
<span class="n">samesite</span><span class="o">=</span><span class="s2">&quot;strict&quot;</span><span class="p">,</span> <span class="c1"># never sent cross-site</span>
<span class="n">path</span><span class="o">=</span><span class="s2">&quot;/&quot;</span><span class="p">,</span>
<span class="p">)</span>
</pre></div>
</div>
<p>Cookies default to <code class="docutils literal notranslate"><span class="pre">SameSite=Lax</span></code>, matching modern browser behavior and
defending against CSRF. Pass <code class="docutils literal notranslate"><span class="pre">samesite=&quot;strict&quot;</span></code> for tighter isolation,
or <code class="docutils literal notranslate"><span class="pre">samesite=None</span></code> to omit the directive entirely.</p>
</section>
<section id="cookie-based-sessions">
<h2>Cookie-Based Sessions<a class="headerlink" href="#cookie-based-sessions" title="Link to this heading"></a></h2>
<p>Sessions let you store per-user data across multiple requests. Responders
built-in sessions are cookie-based — the session data is serialized, signed
with your secret key, and stored in a cookie. The signature prevents
tampering: if someone modifies the cookie, the signature wont match and
the data will be rejected:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/login&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">login</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">session</span><span class="p">[</span><span class="s2">&quot;username&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;alice&quot;</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/profile&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">profile</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;user&quot;</span><span class="p">:</span> <span class="n">req</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;username&quot;</span><span class="p">)}</span>
</pre></div>
</div>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>Always set a secret key in production. The default key is not secret:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">secret_key</span><span class="o">=</span><span class="s2">&quot;your-secret-key-here&quot;</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>By default, session data lives <em>in</em> the cookie (signed to prevent
tampering). Thats simple but capped around 4KB and impossible to revoke
server-side. For logout-everywhere, large sessions, or sensitive data,
switch to server-side storage — only an opaque ID travels in the cookie:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">responder.ext.sessions</span><span class="w"> </span><span class="kn">import</span> <span class="n">MemorySessionBackend</span>
<span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">session_backend</span><span class="o">=</span><span class="n">MemorySessionBackend</span><span class="p">())</span>
</pre></div>
</div>
<p>The handler code (<code class="docutils literal notranslate"><span class="pre">req.session[...]</span></code>) is identical either way. For
multi-process deployments, use <code class="docutils literal notranslate"><span class="pre">RedisSessionBackend(url=...)</span></code> so all
workers share the store. Custom backends need only three methods:
<code class="docutils literal notranslate"><span class="pre">get(id)</span></code>, <code class="docutils literal notranslate"><span class="pre">set(id,</span> <span class="pre">data,</span> <span class="pre">max_age)</span></code>, and <code class="docutils literal notranslate"><span class="pre">delete(id)</span></code>.</p>
</section>
<section id="static-files">
<h2>Static Files<a class="headerlink" href="#static-files" title="Link to this heading"></a></h2>
<p>Most web applications serve static assets — CSS stylesheets, JavaScript
files, images, fonts. Responder serves these from the <code class="docutils literal notranslate"><span class="pre">static/</span></code> directory
by default:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">static_dir</span><span class="o">=</span><span class="s2">&quot;static&quot;</span><span class="p">,</span> <span class="n">static_route</span><span class="o">=</span><span class="s2">&quot;/static&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>Place your assets in the <code class="docutils literal notranslate"><span class="pre">static/</span></code> directory and theyll be served
automatically at <code class="docutils literal notranslate"><span class="pre">/static/style.css</span></code>, <code class="docutils literal notranslate"><span class="pre">/static/app.js</span></code>, etc.</p>
<p>For single-page applications (React, Vue, Angular), you can serve
<code class="docutils literal notranslate"><span class="pre">index.html</span></code> as the default response for all unmatched routes:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">api</span><span class="o">.</span><span class="n">add_route</span><span class="p">(</span><span class="s2">&quot;/&quot;</span><span class="p">,</span> <span class="n">static</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="cors">
<h2>CORS<a class="headerlink" href="#cors" title="Link to this heading"></a></h2>
<p><a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">CORS</a> (Cross-
Origin Resource Sharing) is a security mechanism that controls which
websites can make requests to your API. Browsers enforce this — if your
API is at <code class="docutils literal notranslate"><span class="pre">api.example.com</span></code> and your frontend is at <code class="docutils literal notranslate"><span class="pre">app.example.com</span></code>,
the browser will block requests unless your API explicitly allows it.</p>
<p>Enable CORS and configure which origins are allowed:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">cors</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">cors_params</span><span class="o">=</span><span class="p">{</span>
<span class="s2">&quot;allow_origins&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;https://app.example.com&quot;</span><span class="p">],</span>
<span class="s2">&quot;allow_methods&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;GET&quot;</span><span class="p">,</span> <span class="s2">&quot;POST&quot;</span><span class="p">],</span>
<span class="s2">&quot;allow_headers&quot;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&quot;*&quot;</span><span class="p">],</span>
<span class="s2">&quot;allow_credentials&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
<span class="s2">&quot;max_age&quot;</span><span class="p">:</span> <span class="mi">600</span><span class="p">,</span>
<span class="p">})</span>
</pre></div>
</div>
<p>The default policy is restrictive — you must explicitly allow each origin.
Using <code class="docutils literal notranslate"><span class="pre">[&quot;*&quot;]</span></code> for allow_origins permits any website to call your API,
which is fine for public APIs but not for private ones.</p>
</section>
<section id="hsts">
<h2>HSTS<a class="headerlink" href="#hsts" title="Link to this heading"></a></h2>
<p><a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security">HSTS</a>
(HTTP Strict Transport Security) tells browsers to always use HTTPS when
communicating with your server. Once a browser sees the HSTS header, it
will refuse to connect over plain HTTP, even if the user types <code class="docutils literal notranslate"><span class="pre">http://</span></code>
in the address bar:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">enable_hsts</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="trusted-hosts">
<h2>Trusted Hosts<a class="headerlink" href="#trusted-hosts" title="Link to this heading"></a></h2>
<p>The <code class="docutils literal notranslate"><span class="pre">Host</span></code> header in an HTTP request tells the server which domain name
the client used. Attackers can forge this header to trick your application
into generating URLs to malicious domains (a class of attack called <em>Host
header injection</em>).</p>
<p>Restrict which hostnames your application accepts:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">allowed_hosts</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;example.com&quot;</span><span class="p">,</span> <span class="s2">&quot;*.example.com&quot;</span><span class="p">])</span>
</pre></div>
</div>
<p>Requests with unrecognized hosts get a <code class="docutils literal notranslate"><span class="pre">400</span> <span class="pre">Bad</span> <span class="pre">Request</span></code>. Wildcard
patterns are supported. By default, all hostnames are allowed.</p>
</section>
<section id="request-id">
<h2>Request ID<a class="headerlink" href="#request-id" title="Link to this heading"></a></h2>
<p>In distributed systems, tracing a single request across multiple services
is essential for debugging. Request IDs are unique identifiers attached to
each request — if something goes wrong, you can search your logs for that
ID and find every related event.</p>
<p>Responder can auto-generate request IDs. If the client sends an
<code class="docutils literal notranslate"><span class="pre">X-Request-ID</span></code> header (common in microservice architectures), its
forwarded. Otherwise, a new UUID is generated:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">request_id</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</pre></div>
</div>
<p>The ID appears in the <code class="docutils literal notranslate"><span class="pre">X-Request-ID</span></code> response header.</p>
</section>
<section id="request-size-limits">
<h2>Request Size Limits<a class="headerlink" href="#request-size-limits" title="Link to this heading"></a></h2>
<p>Unbounded request bodies are an easy denial-of-service vector. Cap them
application-wide and oversized uploads get <code class="docutils literal notranslate"><span class="pre">413</span></code> automatically —
whether the body is read with <code class="docutils literal notranslate"><span class="pre">await</span> <span class="pre">req.content</span></code>, <code class="docutils literal notranslate"><span class="pre">req.media()</span></code>, or
streamed:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">max_request_size</span><span class="o">=</span><span class="mi">10</span> <span class="o">*</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">)</span> <span class="c1"># 10 MB</span>
</pre></div>
</div>
<p>The check fails fast on the <code class="docutils literal notranslate"><span class="pre">Content-Length</span></code> header when present, and
enforces the limit cumulatively for chunked uploads.</p>
</section>
<section id="request-timeouts">
<h2>Request Timeouts<a class="headerlink" href="#request-timeouts" title="Link to this heading"></a></h2>
<p>A handler stuck on a slow database or unresponsive upstream shouldnt hold
the client forever. Set an application-wide budget and overruns are
answered with <code class="docutils literal notranslate"><span class="pre">504</span> <span class="pre">Gateway</span> <span class="pre">Timeout</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">request_timeout</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span> <span class="c1"># seconds</span>
</pre></div>
</div>
<p>Dependency teardowns still run when a request times out. One caveat: a
<em>synchronous</em> handler running in the thread pool cant be interrupted —
the client gets the 504 on time, but the thread runs to completion in the
background.</p>
</section>
<section id="rate-limiting">
<h2>Rate Limiting<a class="headerlink" href="#rate-limiting" title="Link to this heading"></a></h2>
<p>Rate limiting prevents individual clients from overwhelming your API with
too many requests. Its essential for public APIs, and good practice even
for internal ones.</p>
<p>Responder includes a built-in token bucket rate limiter:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">responder.ext.ratelimit</span><span class="w"> </span><span class="kn">import</span> <span class="n">RateLimiter</span>
<span class="n">limiter</span> <span class="o">=</span> <span class="n">RateLimiter</span><span class="p">(</span><span class="n">requests</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span> <span class="n">period</span><span class="o">=</span><span class="mi">60</span><span class="p">)</span> <span class="c1"># 100 req/min</span>
<span class="n">limiter</span><span class="o">.</span><span class="n">install</span><span class="p">(</span><span class="n">api</span><span class="p">)</span>
</pre></div>
</div>
<p>When the limit is exceeded, clients receive a <code class="docutils literal notranslate"><span class="pre">429</span> <span class="pre">Too</span> <span class="pre">Many</span> <span class="pre">Requests</span></code>
response with a <code class="docutils literal notranslate"><span class="pre">Retry-After</span></code> header. Every response includes
<code class="docutils literal notranslate"><span class="pre">X-RateLimit-Limit</span></code> and <code class="docutils literal notranslate"><span class="pre">X-RateLimit-Remaining</span></code> headers so clients
can pace themselves.</p>
<p>The rate limiter is per-client, keyed by IP address.</p>
<p>By default, counts live in process memory. For multi-process or multi-host
deployments, plug in the Redis backend so all workers share one budget:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">responder.ext.ratelimit</span><span class="w"> </span><span class="kn">import</span> <span class="n">RateLimiter</span><span class="p">,</span> <span class="n">RedisBackend</span>
<span class="n">limiter</span> <span class="o">=</span> <span class="n">RateLimiter</span><span class="p">(</span>
<span class="n">requests</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span> <span class="n">period</span><span class="o">=</span><span class="mi">60</span><span class="p">,</span>
<span class="n">backend</span><span class="o">=</span><span class="n">RedisBackend</span><span class="p">(</span><span class="n">url</span><span class="o">=</span><span class="s2">&quot;redis://localhost:6379/0&quot;</span><span class="p">),</span>
<span class="p">)</span>
</pre></div>
</div>
<p>Any object with a <code class="docutils literal notranslate"><span class="pre">hit(key,</span> <span class="pre">max_requests,</span> <span class="pre">period)</span> <span class="pre">-&gt;</span> <span class="pre">(allowed,</span> <span class="pre">remaining)</span></code>
method works as a backend, so custom stores are easy to write.</p>
<p>To rate-limit a single route instead of the whole API, apply
<a class="reference internal" href="api.html#responder.ext.ratelimit.RateLimiter.limit" title="responder.ext.ratelimit.RateLimiter.limit"><code class="xref py py-meth docutils literal notranslate"><span class="pre">limit()</span></code></a> beneath <code class="docutils literal notranslate"><span class="pre">&#64;api.route</span></code>.
Give each route its own <code class="docutils literal notranslate"><span class="pre">RateLimiter</span></code> so budgets stay independent:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">expensive_limiter</span> <span class="o">=</span> <span class="n">RateLimiter</span><span class="p">(</span><span class="n">requests</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span> <span class="n">period</span><span class="o">=</span><span class="mi">60</span><span class="p">)</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/reports&quot;</span><span class="p">)</span>
<span class="nd">@expensive_limiter</span><span class="o">.</span><span class="n">limit</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">generate_report</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="o">...</span>
</pre></div>
</div>
</section>
<section id="metrics">
<h2>Metrics<a class="headerlink" href="#metrics" title="Link to this heading"></a></h2>
<p>Production services need visibility into traffic and latency. Responder
ships a zero-dependency metrics endpoint in Prometheus text format:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">metrics_route</span><span class="o">=</span><span class="s2">&quot;/metrics&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>Every request is recorded as a counter
(<code class="docutils literal notranslate"><span class="pre">responder_requests_total{method,path,status}</span></code>) and a latency histogram
(<code class="docutils literal notranslate"><span class="pre">responder_request_duration_seconds</span></code>). Labels use the route <em>pattern</em>
(<code class="docutils literal notranslate"><span class="pre">/users/{id}</span></code>), not the raw path, so cardinality stays bounded; requests
matching no route are labelled <code class="docutils literal notranslate"><span class="pre">unmatched</span></code>. Point Prometheus, Grafana
Alloy, or any compatible scraper at the endpoint and you have dashboards.</p>
</section>
<section id="structured-logging">
<h2>Structured Logging<a class="headerlink" href="#structured-logging" title="Link to this heading"></a></h2>
<p>Production applications need structured, searchable logs. Responder
includes built-in logging that automatically attaches request context
— request ID, HTTP method, path, and client IP — to every log message
emitted during request handling:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">api</span> <span class="o">=</span> <span class="n">responder</span><span class="o">.</span><span class="n">API</span><span class="p">(</span><span class="n">enable_logging</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</pre></div>
</div>
<p>This gives you:</p>
<ul>
<li><p><strong>Access logging</strong> with timing for every request:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>2026-03-24 12:00:00 [INFO] responder.access — GET /users → 200 (1.2ms)
</pre></div>
</div>
</li>
<li><p><strong>A logger on the API instance</strong> — use <code class="docutils literal notranslate"><span class="pre">api.log</span></code> anywhere in
your routes. Request context (ID, method, path, client IP) is
attached automatically:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/users/{user_id:int}&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">get_user</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">user_id</span><span class="p">):</span>
<span class="n">api</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">&quot;fetching user </span><span class="si">%d</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">user_id</span><span class="p">)</span>
<span class="c1"># =&gt; [INFO] responder.app -- fetching user 42 [GET /users/42] [req:a1b2c3] [client:10.0.0.1]</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="p">:</span> <span class="n">user_id</span><span class="p">}</span>
</pre></div>
</div>
</li>
<li><p><strong>Request IDs</strong> generated automatically (or forwarded from the
<code class="docutils literal notranslate"><span class="pre">X-Request-ID</span></code> header) and included in responses.</p></li>
</ul>
<p>The logging uses Pythons standard <code class="docutils literal notranslate"><span class="pre">logging</span></code> module, so it works with
any handler — files, syslog, JSON formatters, Datadog, Sentry, whatever
you already use.</p>
<p>For additional loggers (e.g. in helper modules), use <code class="docutils literal notranslate"><span class="pre">get_logger</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">responder.ext.logging</span><span class="w"> </span><span class="kn">import</span> <span class="n">get_logger</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">get_logger</span><span class="p">(</span><span class="s2">&quot;myapp.db&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>You can also access the current request context directly:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">responder.ext.logging</span><span class="w"> </span><span class="kn">import</span> <span class="n">RequestContext</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/debug&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">debug</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">&quot;request_id&quot;</span><span class="p">:</span> <span class="n">RequestContext</span><span class="o">.</span><span class="n">get_request_id</span><span class="p">(),</span>
<span class="s2">&quot;client_ip&quot;</span><span class="p">:</span> <span class="n">RequestContext</span><span class="o">.</span><span class="n">get_client_ip</span><span class="p">(),</span>
<span class="p">}</span>
</pre></div>
</div>
<p>When <code class="docutils literal notranslate"><span class="pre">enable_logging=True</span></code> is set, it supersedes <code class="docutils literal notranslate"><span class="pre">request_id=True</span></code>
— the logging middleware handles request IDs itself, so you dont get
duplicate headers.</p>
</section>
<section id="pydantic-validation">
<h2>Pydantic Validation<a class="headerlink" href="#pydantic-validation" title="Link to this heading"></a></h2>
<p><a class="reference external" href="https://docs.pydantic.dev/">Pydantic</a> models integrate directly with
Responders routing. Set <code class="docutils literal notranslate"><span class="pre">request_model</span></code> to validate incoming data and
<code class="docutils literal notranslate"><span class="pre">response_model</span></code> to control the shape of outgoing data:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">pydantic</span><span class="w"> </span><span class="kn">import</span> <span class="n">BaseModel</span>
<span class="k">class</span><span class="w"> </span><span class="nc">ItemIn</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
<span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">price</span><span class="p">:</span> <span class="nb">float</span>
<span class="k">class</span><span class="w"> </span><span class="nc">ItemOut</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
<span class="nb">id</span><span class="p">:</span> <span class="nb">int</span>
<span class="n">name</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">price</span><span class="p">:</span> <span class="nb">float</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/items&quot;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;POST&quot;</span><span class="p">],</span>
<span class="n">request_model</span><span class="o">=</span><span class="n">ItemIn</span><span class="p">,</span> <span class="n">response_model</span><span class="o">=</span><span class="n">ItemOut</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_item</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">data</span> <span class="o">=</span> <span class="k">await</span> <span class="n">req</span><span class="o">.</span><span class="n">media</span><span class="p">()</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="o">**</span><span class="n">data</span><span class="p">}</span>
</pre></div>
</div>
<p>When <code class="docutils literal notranslate"><span class="pre">request_model</span></code> is set:</p>
<ul>
<li><p>Valid requests are parsed and the data is available via <code class="docutils literal notranslate"><span class="pre">await</span> <span class="pre">req.media()</span></code></p></li>
<li><p>The validated model instance is available as <code class="docutils literal notranslate"><span class="pre">req.state.validated</span></code>,
so you dont need to re-parse the body:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/items&quot;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;POST&quot;</span><span class="p">],</span> <span class="n">request_model</span><span class="o">=</span><span class="n">ItemIn</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">create_item</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">item</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="n">state</span><span class="o">.</span><span class="n">validated</span> <span class="c1"># an ItemIn instance</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="n">item</span><span class="o">.</span><span class="n">name</span><span class="p">}</span>
</pre></div>
</div>
</li>
<li><p>Invalid requests get an automatic <code class="docutils literal notranslate"><span class="pre">422</span> <span class="pre">Unprocessable</span> <span class="pre">Entity</span></code> response
with detailed error messages — you dont write any validation code</p></li>
</ul>
<p>When <code class="docutils literal notranslate"><span class="pre">response_model</span></code> is set:</p>
<ul class="simple">
<li><p>The response is serialized through the model before being sent</p></li>
<li><p>Extra fields are stripped automatically</p></li>
<li><p>Type coercion happens at the boundary</p></li>
</ul>
<p>Query strings validate the same way with <code class="docutils literal notranslate"><span class="pre">params_model</span></code> — values are
coerced (<code class="docutils literal notranslate"><span class="pre">&quot;5&quot;</span></code> becomes <code class="docutils literal notranslate"><span class="pre">5</span></code>), defaults apply, repeated keys map to
<code class="docutils literal notranslate"><span class="pre">list</span></code> fields, invalid queries get a <code class="docutils literal notranslate"><span class="pre">422</span></code>, and the parameters appear
in your OpenAPI spec:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">SearchParams</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
<span class="n">q</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/search&quot;</span><span class="p">,</span> <span class="n">params_model</span><span class="o">=</span><span class="n">SearchParams</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">search</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">params</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="n">state</span><span class="o">.</span><span class="n">validated_params</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="k">await</span> <span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">.</span><span class="n">q</span><span class="p">,</span> <span class="n">limit</span><span class="o">=</span><span class="n">params</span><span class="o">.</span><span class="n">limit</span><span class="p">)</span>
</pre></div>
</div>
<p>This is the recommended way to build validated REST APIs with Responder.
See the <a class="reference internal" href="tutorial-rest.html"><span class="doc">Building a REST API</span></a> for a complete walkthrough.</p>
</section>
<section id="content-negotiation">
<h2>Content Negotiation<a class="headerlink" href="#content-negotiation" title="Link to this heading"></a></h2>
<p>Responder automatically negotiates the response format based on the
clients <code class="docutils literal notranslate"><span class="pre">Accept</span></code> header. Set <code class="docutils literal notranslate"><span class="pre">resp.media</span></code> to a Python object and
the right thing happens:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">Accept:</span> <span class="pre">application/json</span></code> (default) → JSON</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">Accept:</span> <span class="pre">application/x-yaml</span></code> → YAML</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">Accept:</span> <span class="pre">application/x-msgpack</span></code> → MessagePack</p></li>
</ul>
<p>This means a single endpoint serves multiple formats without any
conditional logic in your code:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/data&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">data</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;key&quot;</span><span class="p">:</span> <span class="s2">&quot;value&quot;</span><span class="p">}</span>
</pre></div>
</div>
<p>Clients get the format they ask for:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ curl http://localhost:5042/data
{&quot;key&quot;: &quot;value&quot;}
$ curl -H &quot;Accept: application/x-yaml&quot; http://localhost:5042/data
key: value
</pre></div>
</div>
</section>
<section id="messagepack">
<h2>MessagePack<a class="headerlink" href="#messagepack" title="Link to this heading"></a></h2>
<p><a class="reference external" href="https://msgpack.org/">MessagePack</a> is a binary serialization format
thats more compact and faster to parse than JSON. Its useful for
high-throughput APIs, IoT devices, and anywhere bandwidth matters.</p>
<p>Responder supports MessagePack alongside JSON and YAML:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Decode a MessagePack request body</span>
<span class="n">data</span> <span class="o">=</span> <span class="k">await</span> <span class="n">req</span><span class="o">.</span><span class="n">media</span><span class="p">(</span><span class="s2">&quot;msgpack&quot;</span><span class="p">)</span>
<span class="c1"># Respond with MessagePack</span>
<span class="n">resp</span><span class="o">.</span><span class="n">media</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;result&quot;</span><span class="p">:</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]}</span>
</pre></div>
</div>
<p>Content negotiation works automatically — clients can send
<code class="docutils literal notranslate"><span class="pre">Accept:</span> <span class="pre">application/x-msgpack</span></code> to receive MessagePack responses
instead of JSON. You can also explicitly decode MessagePack request
bodies by passing <code class="docutils literal notranslate"><span class="pre">&quot;msgpack&quot;</span></code> to <code class="docutils literal notranslate"><span class="pre">req.media()</span></code>.</p>
</section>
</section>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="Main">
<div class="sphinxsidebarwrapper"><p class="logo">
<a href="index.html">
<img class="logo" src="_static/responder.png" />
</a>
</p>
<p>
<strong>Responder</strong> — a familiar HTTP service framework for Python.
<br />
<small>v4.0.0</small>
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="https://github.com/kennethreitz/responder">Responder @ GitHub</a></li>
<li><a href="https://pypi.org/project/responder/">Responder @ PyPI</a></li>
<li><a href="https://github.com/kennethreitz/responder/issues">Issue Tracker</a></li>
</ul>
<div>
<h3><a href="index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Feature Tour</a><ul>
<li><a class="reference internal" href="#method-filtering">Method Filtering</a></li>
<li><a class="reference internal" href="#returning-values">Returning Values</a></li>
<li><a class="reference internal" href="#class-based-views">Class-Based Views</a></li>
<li><a class="reference internal" href="#lifespan-events">Lifespan Events</a></li>
<li><a class="reference internal" href="#dependency-injection">Dependency Injection</a></li>
<li><a class="reference internal" href="#serving-files">Serving Files</a></li>
<li><a class="reference internal" href="#conditional-requests">Conditional Requests</a></li>
<li><a class="reference internal" href="#after-response-tasks">After-Response Tasks</a></li>
<li><a class="reference internal" href="#custom-error-handling">Custom Error Handling</a></li>
<li><a class="reference internal" href="#before-request-hooks">Before-Request Hooks</a></li>
<li><a class="reference internal" href="#after-request-hooks">After-Request Hooks</a></li>
<li><a class="reference internal" href="#websocket-support">WebSocket Support</a></li>
<li><a class="reference internal" href="#server-sent-events-sse">Server-Sent Events (SSE)</a></li>
<li><a class="reference internal" href="#graphql">GraphQL</a></li>
<li><a class="reference internal" href="#openapi-documentation">OpenAPI Documentation</a></li>
<li><a class="reference internal" href="#route-groups">Route Groups</a></li>
<li><a class="reference internal" href="#mounting-other-apps">Mounting Other Apps</a></li>
<li><a class="reference internal" href="#cookies">Cookies</a></li>
<li><a class="reference internal" href="#cookie-based-sessions">Cookie-Based Sessions</a></li>
<li><a class="reference internal" href="#static-files">Static Files</a></li>
<li><a class="reference internal" href="#cors">CORS</a></li>
<li><a class="reference internal" href="#hsts">HSTS</a></li>
<li><a class="reference internal" href="#trusted-hosts">Trusted Hosts</a></li>
<li><a class="reference internal" href="#request-id">Request ID</a></li>
<li><a class="reference internal" href="#request-size-limits">Request Size Limits</a></li>
<li><a class="reference internal" href="#request-timeouts">Request Timeouts</a></li>
<li><a class="reference internal" href="#rate-limiting">Rate Limiting</a></li>
<li><a class="reference internal" href="#metrics">Metrics</a></li>
<li><a class="reference internal" href="#structured-logging">Structured Logging</a></li>
<li><a class="reference internal" href="#pydantic-validation">Pydantic Validation</a></li>
<li><a class="reference internal" href="#content-negotiation">Content Negotiation</a></li>
<li><a class="reference internal" href="#messagepack">MessagePack</a></li>
</ul>
</li>
</ul>
</div>
<search id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</search>
<script>document.getElementById('searchbox').style.display = "block"</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
&#169;2018-2026, Kenneth Reitz.
|
<a href="_sources/tour.rst.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>