Files
2026-07-17 21:00:53 +00:00

1981 lines
237 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 9.2.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=eb4102c5" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css?v=76b2166b" />
<link rel="stylesheet" type="text/css" href="_static/sphinx-design.min.css?v=95c83b7e" />
<link rel="stylesheet" type="text/css" href="_static/design-elements.e5416f61bae5d36adc6d722a2b6f8cff.css?v=452a8e97" />
<script src="_static/documentation_options.js?v=e8b940be"></script>
<script src="_static/doctools.js?v=fd6eb6e6"></script>
<script src="_static/sphinx_highlight.js?v=6ffebe34"></script>
<script src="_static/clipboard.min.js?v=a7894cd8"></script>
<script src="_static/copybutton.js?v=fd10adb8"></script>
<script>
</script>
<script src="_static/design-tabs.js?v=f930bc37"></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="Examples" href="examples.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 Responder’s features in depth. Each section
explains the concept, shows working code, and explains the design choices
behind it. If you’re 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>, <code class="docutils literal notranslate"><span class="pre">list</span></code>, number, or boolean 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>. A Pydantic model
or a dataclass instance also becomes <code class="docutils literal notranslate"><span class="pre">resp.media</span></code> — serialized natively
across JSON, YAML, and MessagePack, so a trailing <code class="docutils literal notranslate"><span class="pre">.model_dump()</span></code> is optional.
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>To set a status code, or status and headers, return a Flask-style tuple —
<code class="docutils literal notranslate"><span class="pre">body,</span> <span class="pre">status</span></code> or <code class="docutils literal notranslate"><span class="pre">body,</span> <span class="pre">status,</span> <span class="pre">headers</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;/items/{id:int}&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">get_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="o">*</span><span class="p">,</span> <span class="nb">id</span><span class="p">):</span>
<span class="n">item</span> <span class="o">=</span> <span class="n">lookup</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
<span class="k">if</span> <span class="n">item</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">&quot;error&quot;</span><span class="p">:</span> <span class="s2">&quot;not found&quot;</span><span class="p">},</span> <span class="mi">404</span>
<span class="k">return</span> <span class="n">item</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="p">{</span><span class="s2">&quot;X-Source&quot;</span><span class="p">:</span> <span class="s2">&quot;cache&quot;</span><span class="p">}</span>
</pre></div>
</div>
<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 Django’s <code class="docutils literal notranslate"><span class="pre">View</span></code> classes and more Pythonic than
framework-specific base classes.</p>
<p>Everything the type-driven request pipeline offers function views works on
class-based view methods too: a required Pydantic-typed body parameter on a
write-method handler receives the parsed, validated body (an invalid body
returns <code class="docutils literal notranslate"><span class="pre">422</span></code> before the handler runs), and <code class="docutils literal notranslate"><span class="pre">Query()</span></code>/<code class="docutils literal notranslate"><span class="pre">Header()</span></code>/
<code class="docutils literal notranslate"><span class="pre">Cookie()</span></code>/<code class="docutils literal notranslate"><span class="pre">Path()</span></code>/<code class="docutils literal notranslate"><span class="pre">Form()</span></code> markers resolve exactly as they do on
functions:</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="kn">from</span><span class="w"> </span><span class="nn">responder</span><span class="w"> </span><span class="kn">import</span> <span class="n">Query</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="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="k">class</span><span class="w"> </span><span class="nc">ItemResource</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">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="mi">10</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">fetch_items</span><span class="p">(</span><span class="n">limit</span><span class="p">)</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">item</span><span class="p">:</span> <span class="n">ItemIn</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">create_item</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
</pre></div>
</div>
<p>When <code class="docutils literal notranslate"><span class="pre">on_request</span></code> and a method handler both declare the same body model,
the body is parsed and validated once and the same instance is injected into
both.</p>
<p>Class-based views also answer <code class="docutils literal notranslate"><span class="pre">OPTIONS</span></code> automatically with <code class="docutils literal notranslate"><span class="pre">200</span></code> and an
<code class="docutils literal notranslate"><span class="pre">Allow</span></code> header listing the implemented methods — define <code class="docutils literal notranslate"><span class="pre">on_options</span></code>
(or a catch-all <code class="docutils literal notranslate"><span class="pre">on_request</span></code>) to take over. A request for an unimplemented
method still gets a <code class="docutils literal notranslate"><span class="pre">405</span></code> whose <code class="docutils literal notranslate"><span class="pre">Allow</span></code> header advertises exactly the
methods that are actually served.</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 won’t start. If shutdown raises,
it’s 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>The two styles compose: with <code class="docutils literal notranslate"><span class="pre">lifespan=</span></code> set, <code class="docutils literal notranslate"><span class="pre">on_event(&quot;startup&quot;)</span></code>
handlers run after the context manager is entered, and
<code class="docutils literal notranslate"><span class="pre">on_event(&quot;shutdown&quot;)</span></code> handlers run before it exits.</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>For one-off dependencies that belong to a single route, use <code class="docutils literal notranslate"><span class="pre">Depends</span></code>
instead of registering a global name:</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</span><span class="w"> </span><span class="kn">import</span> <span class="n">Depends</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_user</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="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/me&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">me</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</span><span class="o">=</span><span class="n">Depends</span><span class="p">(</span><span class="n">current_user</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">user</span><span class="p">}</span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">Depends</span></code> providers follow the same lifecycle rules as registered
dependencies, including generator teardown.</p>
<p><code class="docutils literal notranslate"><span class="pre">Depends</span></code> also <strong>nests</strong>: a provider’s own parameters can use <code class="docutils literal notranslate"><span class="pre">Depends</span></code>, so
dependencies compose into chains without registering each one by name:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">get_db</span><span class="p">():</span>
<span class="o">...</span>
<span class="k">def</span><span class="w"> </span><span class="nf">current_user</span><span class="p">(</span><span class="n">db</span><span class="o">=</span><span class="n">Depends</span><span class="p">(</span><span class="n">get_db</span><span class="p">)):</span> <span class="c1"># a provider depending on a provider</span>
<span class="k">return</span> <span class="n">load_user</span><span class="p">(</span><span class="n">db</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;/me&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">me</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</span><span class="o">=</span><span class="n">Depends</span><span class="p">(</span><span class="n">current_user</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">user</span><span class="p">}</span>
</pre></div>
</div>
<p>Each provider resolves once per request and shares the same cache, and cycles
are detected and reported.</p>
<p>When a dependency is only a guard or setup step, attach it to the route instead
of adding an unused handler parameter:</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;/private&quot;</span><span class="p">,</span> <span class="n">dependencies</span><span class="o">=</span><span class="p">[</span><span class="n">Depends</span><span class="p">(</span><span class="n">current_user</span><span class="p">)])</span>
<span class="k">def</span><span class="w"> </span><span class="nf">private</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>
</pre></div>
</div>
<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 (for
WebSocket routes, when the connection closes). This is perfect for
database sessions and other resources that need cleanup. Teardown runs
even if the handler raised.</p></li>
</ul>
<p>To make a dependency request-aware, give the provider a parameter named
<code class="docutils literal notranslate"><span class="pre">req</span></code> (or <code class="docutils literal notranslate"><span class="pre">request</span></code>, or one annotated <code class="docutils literal notranslate"><span class="pre">responder.Request</span></code>) — it
receives the current request:</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><strong>Dependencies compose.</strong> A provider can depend on other providers simply
by naming them as parameters; Responder resolves the whole graph for you,
memoizes each provider so it runs at most once per request, and tears
everything down in reverse order:</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">config</span><span class="p">():</span>
<span class="k">return</span> <span class="n">load_config</span><span class="p">()</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">config</span><span class="p">):</span> <span class="c1"># depends on `config`</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="n">config</span><span class="o">.</span><span class="n">db_url</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>Because resolution is memoized across the whole request, a shared provider
(or one asked for by both <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) runs a single time. Cyclic dependencies are detected and raise
<code class="docutils literal notranslate"><span class="pre">DependencyCycleError</span></code>.</p>
<p>The names <code class="docutils literal notranslate"><span class="pre">req</span></code>, <code class="docutils literal notranslate"><span class="pre">request</span></code>, <code class="docutils literal notranslate"><span class="pre">resp</span></code>, <code class="docutils literal notranslate"><span class="pre">response</span></code>, <code class="docutils literal notranslate"><span class="pre">ws</span></code>, and
<code class="docutils literal notranslate"><span class="pre">websocket</span></code> are reserved and can’t be used as dependency names. When
resolution goes wrong, Responder raises a catchable <code class="docutils literal notranslate"><span class="pre">DependencyError</span></code> —
specifically <code class="docutils literal notranslate"><span class="pre">DependencyCycleError</span></code>, <code class="docutils literal notranslate"><span class="pre">DependencyScopeError</span></code>, or
<code class="docutils literal notranslate"><span class="pre">DependencyResolutionError</span></code>. 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 may compose with <em>other</em> app-scoped providers, but
they can’t depend on the request or on request-scoped providers — they
outlive any single request. A per-request database session layered on an
app-scoped connection pool is the canonical pattern; see
<a class="reference internal" href="tutorial-sqlalchemy.html"><span class="doc">Using SQLAlchemy</span></a> for a complete example.</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 they’re injected the
same way (a provider receives the socket via a <code class="docutils literal notranslate"><span class="pre">ws</span></code>/<code class="docutils literal notranslate"><span class="pre">websocket</span></code> (or
<code class="docutils literal notranslate"><span class="pre">req</span></code>/<code class="docutils literal notranslate"><span class="pre">request</span></code>) parameter or a <code class="docutils literal notranslate"><span class="pre">WebSocket</span></code> annotation):</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 Python’s
<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 isn’t 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. Multiple ranges are answered as
<code class="docutils literal notranslate"><span class="pre">multipart/byteranges</span></code>. 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>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>When the path comes from user input — a URL segment, a query parameter —
pass <code class="docutils literal notranslate"><span class="pre">root=</span></code> to jail file access to a directory. <code class="docutils literal notranslate"><span class="pre">resp.file</span></code>,
<code class="docutils literal notranslate"><span class="pre">resp.stream_file</span></code>, and <code class="docutils literal notranslate"><span class="pre">resp.download</span></code> resolve the path under
<code class="docutils literal notranslate"><span class="pre">root</span></code> and return <code class="docutils literal notranslate"><span class="pre">404</span></code> on any <code class="docutils literal notranslate"><span class="pre">../</span></code> or symlink that escapes it:</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;/files/</span><span class="si">{name}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">serve</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">name</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="n">name</span><span class="p">,</span> <span class="n">root</span><span class="o">=</span><span class="s2">&quot;/srv/public&quot;</span><span class="p">)</span> <span class="c1"># cannot escape /srv/public</span>
</pre></div>
</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>
<p>For ordinary multipart uploads, a typed <code class="docutils literal notranslate"><span class="pre">File</span></code> marker gives you Starlette’s
<code class="docutils literal notranslate"><span class="pre">UploadFile</span></code> plus a Responder convenience method for saving it:</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</span><span class="w"> </span><span class="kn">import</span> <span class="n">File</span><span class="p">,</span> <span class="n">UploadFile</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/avatar&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">avatar</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">image</span><span class="p">:</span> <span class="n">UploadFile</span> <span class="o">=</span> <span class="n">File</span><span class="p">(</span><span class="o">...</span><span class="p">)):</span>
<span class="n">path</span> <span class="o">=</span> <span class="k">await</span> <span class="n">image</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="s2">&quot;/srv/uploads/avatar.bin&quot;</span><span class="p">,</span> <span class="n">create_parents</span><span class="o">=</span><span class="kc">True</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;saved&quot;</span><span class="p">:</span> <span class="nb">str</span><span class="p">(</span><span class="n">path</span><span class="p">)}</span>
</pre></div>
</div>
<p>Since 9.0, multipart bodies parse <em>incrementally</em> off the wire: file parts
spool to temporary files (rolling to disk past ~1 MB) instead of being held
in memory, so a gigabyte upload does not mean a gigabyte of RAM — raise
<code class="docutils literal notranslate"><span class="pre">max_request_size</span></code> to taste and <code class="docutils literal notranslate"><span class="pre">UploadFile.save()</span></code> moves the bytes
disk-to-disk in chunks.</p>
</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 they’ve 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 request’s <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 <code class="docutils literal notranslate"><span class="pre">304</span></code>
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>The same validators also guard writes against lost updates. On
state-changing methods (anything but <code class="docutils literal notranslate"><span class="pre">GET</span></code>/<code class="docutils literal notranslate"><span class="pre">HEAD</span></code>), when <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> is set, Responder evaluates the request’s
<code class="docutils literal notranslate"><span class="pre">If-Match</span></code>, <code class="docutils literal notranslate"><span class="pre">If-Unmodified-Since</span></code>, and <code class="docutils literal notranslate"><span class="pre">If-None-Match</span></code> preconditions
(RFC 9110 §13) and answers <code class="docutils literal notranslate"><span class="pre">412</span> <span class="pre">Precondition</span> <span class="pre">Failed</span></code> — carrying the
current validator in the response headers — when they don’t hold:</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/</span><span class="si">{id}</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;PUT&quot;</span><span class="p">])</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">update_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="o">*</span><span class="p">,</span> <span class="nb">id</span><span class="p">):</span>
<span class="n">item</span> <span class="o">=</span> <span class="n">load</span><span class="p">(</span><span class="nb">id</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">item</span><span class="o">.</span><span class="n">version_hash</span> <span class="c1"># the resource&#39;s current validator</span>
<span class="n">apply_changes</span><span class="p">(</span><span class="n">item</span><span class="p">,</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;updated&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">}</span>
</pre></div>
</div>
<p>A client that sends <code class="docutils literal notranslate"><span class="pre">If-Match:</span> <span class="pre">&quot;&lt;stale-tag&gt;&quot;</span></code> receives a <code class="docutils literal notranslate"><span class="pre">412</span></code> instead
of the success response. Per RFC 9110, <code class="docutils literal notranslate"><span class="pre">If-Match</span></code> uses <em>strong</em>
comparison (a weak <code class="docutils literal notranslate"><span class="pre">W/&quot;...&quot;</span></code> ETag never matches it), <code class="docutils literal notranslate"><span class="pre">If-Match:</span> <span class="pre">*</span></code>
succeeds whenever a validator is set, and <code class="docutils literal notranslate"><span class="pre">If-Unmodified-Since</span></code> is
consulted only when <code class="docutils literal notranslate"><span class="pre">If-Match</span></code> is absent. As with <code class="docutils literal notranslate"><span class="pre">304</span></code> handling, each
precondition is evaluated only when its validator is set (<code class="docutils literal notranslate"><span class="pre">resp.etag</span></code>
for the ETag preconditions, <code class="docutils literal notranslate"><span class="pre">resp.last_modified</span></code> for the date one), and
only for responses that would otherwise succeed (2xx) — a handler that
already set a <code class="docutils literal notranslate"><span class="pre">404</span></code> keeps its <code class="docutils literal notranslate"><span class="pre">404</span></code>.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>Preconditions are checked when the response is sent, <em>after</em> the
handler has run. To skip the state change itself (not just the success
response), compare the precondition in the handler before mutating —
e.g. check <code class="docutils literal notranslate"><span class="pre">req.headers.get(&quot;If-Match&quot;)</span></code> against the current tag and
return early with <code class="docutils literal notranslate"><span class="pre">resp.status_code</span> <span class="pre">=</span> <span class="pre">412</span></code>.</p>
</div>
<p>Don’t 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>
<p>For common REST responses, the response object has small helpers that set the
status, headers, body, and media type together:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/items&quot;</span><span class="p">)</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">save_item</span><span class="p">()</span>
<span class="n">resp</span><span class="o">.</span><span class="n">created</span><span class="p">({</span><span class="s2">&quot;id&quot;</span><span class="p">:</span> <span class="n">item</span><span class="o">.</span><span class="n">id</span><span class="p">},</span> <span class="n">location</span><span class="o">=</span><span class="sa">f</span><span class="s2">&quot;/items/</span><span class="si">{</span><span class="n">item</span><span class="o">.</span><span class="n">id</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">delete</span><span class="p">(</span><span class="s2">&quot;/items/{id:int}&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">delete_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="o">*</span><span class="p">,</span> <span class="nb">id</span><span class="p">):</span>
<span class="n">delete_item_by_id</span><span class="p">(</span><span class="nb">id</span><span class="p">)</span>
<span class="n">resp</span><span class="o">.</span><span class="n">no_content</span><span class="p">()</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;/items&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">reject_duplicate</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">problem</span><span class="p">(</span><span class="mi">409</span><span class="p">,</span> <span class="s2">&quot;Item already exists&quot;</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="s2">&quot;https://example.com/conflict&quot;</span><span class="p">)</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 shouldn’t 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 don’t 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>To raise an HTTP error from anywhere — a handler, a hook, a dependency —
call <code class="docutils literal notranslate"><span class="pre">abort()</span></code> instead of importing Starlette’s exceptions:</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</span><span class="w"> </span><span class="kn">import</span> <span class="n">abort</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/admin&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">admin</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="ow">not</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;is_admin&quot;</span><span class="p">):</span>
<span class="n">abort</span><span class="p">(</span><span class="mi">403</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s2">&quot;Forbidden&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">abort()</span></code> halts the handler immediately and renders a content-negotiated
error — unlike setting <code class="docutils literal notranslate"><span class="pre">resp.status_code</span> <span class="pre">=</span> <span class="pre">403</span></code>, which doesn’t stop
execution.</p>
<p>For richer errors, raise <code class="docutils literal notranslate"><span class="pre">responder.Problem</span></code> — a raisable RFC 9457
problem-details exception carrying <code class="docutils literal notranslate"><span class="pre">type</span></code>, <code class="docutils literal notranslate"><span class="pre">title</span></code>, <code class="docutils literal notranslate"><span class="pre">instance</span></code>, and
arbitrary extension members into the rendered <code class="docutils literal notranslate"><span class="pre">application/problem+json</span></code>
payload. Because it works from anywhere an exception propagates (views,
hooks, dependencies), it makes typed problem catalogs possible without
hand-building responses in every handler:</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</span><span class="w"> </span><span class="kn">import</span> <span class="n">Problem</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/quota&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">quota</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">raise</span> <span class="n">Problem</span><span class="p">(</span>
<span class="mi">409</span><span class="p">,</span>
<span class="s2">&quot;You have used all 100 requests for today.&quot;</span><span class="p">,</span>
<span class="n">title</span><span class="o">=</span><span class="s2">&quot;Quota Exceeded&quot;</span><span class="p">,</span>
<span class="nb">type</span><span class="o">=</span><span class="s2">&quot;https://api.example.com/errors/quota-exceeded&quot;</span><span class="p">,</span>
<span class="n">balance</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span>
<span class="p">)</span>
</pre></div>
</div>
<p>The payload flows through the same machinery as framework errors —
<code class="docutils literal notranslate"><span class="pre">problem_handler</span></code> enrichment, request IDs, and the app’s JSON encoder —
and <code class="docutils literal notranslate"><span class="pre">instance</span></code> defaults to the request path per the RFC recommendation.
Passing any of these members to <code class="docutils literal notranslate"><span class="pre">abort()</span></code> raises a <code class="docutils literal notranslate"><span class="pre">Problem</span></code> for you:
<code class="docutils literal notranslate"><span class="pre">abort(409,</span> <span class="pre">type=&quot;...&quot;,</span> <span class="pre">balance=0)</span></code>.</p>
<p>Handlers can also be registered programmatically with
<code class="docutils literal notranslate"><span class="pre">api.add_exception_handler(exc_or_status,</span> <span class="pre">handler)</span></code> (the
<code class="docutils literal notranslate"><span class="pre">&#64;api.exception_handler</span></code> decorator delegates to it). It accepts an
exception class <em>or</em> an integer status code, so you can catch a <code class="docutils literal notranslate"><span class="pre">404</span></code>
directly. Registering against <code class="docutils literal notranslate"><span class="pre">Exception</span></code> (or <code class="docutils literal notranslate"><span class="pre">500</span></code>) installs a
catch-all for unhandled server errors — though under <code class="docutils literal notranslate"><span class="pre">debug=True</span></code> the
traceback page is shown instead:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">not_found</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">404</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;nothing here&quot;</span><span class="p">}</span>
<span class="n">api</span><span class="o">.</span><span class="n">add_exception_handler</span><span class="p">(</span><span class="mi">404</span><span class="p">,</span> <span class="n">not_found</span><span class="p">)</span>
</pre></div>
</div>
<p>Framework-generated errors use an RFC 9457-style envelope by default. Errors
such as 404, 405, validation failures, response-model validation failures, and
request timeouts use <code class="docutils literal notranslate"><span class="pre">application/problem+json</span></code> with <code class="docutils literal notranslate"><span class="pre">type</span></code>, <code class="docutils literal notranslate"><span class="pre">title</span></code>,
<code class="docutils literal notranslate"><span class="pre">status</span></code>, and <code class="docutils literal notranslate"><span class="pre">detail</span></code> fields. Validation errors also include <code class="docutils literal notranslate"><span class="pre">errors</span></code>.</p>
<p>Pass <code class="docutils literal notranslate"><span class="pre">problem_details=False</span></code> when creating the app to keep the legacy
content-negotiated error format, where JSON clients receive bodies like
<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> and browsers receive 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>
<p>When the hook belongs to just one endpoint, attach it directly to that route:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">require_admin</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="ow">not</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;is_admin&quot;</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">403</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;forbidden&quot;</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;/admin&quot;</span><span class="p">,</span> <span class="n">before</span><span class="o">=</span><span class="n">require_admin</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">admin</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>
</pre></div>
</div>
</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. They’re 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>
<p>The parentheses are optional — the bare <code class="docutils literal notranslate"><span class="pre">&#64;api.after_request</span></code> works too,
as does the bare <code class="docutils literal notranslate"><span class="pre">&#64;api.before_request</span></code>.</p>
<p>Route-local after hooks use <code class="docutils literal notranslate"><span class="pre">after=</span></code> and run before global after hooks:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">audit</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-Audited&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;1&quot;</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="n">after</span><span class="o">=</span><span class="n">audit</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">reports</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>
</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
can’t 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 doesn’t require any special client-side libraries. A
typed SSE route validates and serializes every event, publishes its event
schema in OpenAPI, and generates a streaming client method:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">collections.abc</span><span class="w"> </span><span class="kn">import</span> <span class="n">AsyncIterator</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">Progress</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
<span class="n">job_id</span><span class="p">:</span> <span class="nb">int</span>
<span class="n">percent</span><span class="p">:</span> <span class="nb">int</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">sse</span><span class="p">(</span><span class="s2">&quot;/events&quot;</span><span class="p">,</span> <span class="n">heartbeat</span><span class="o">=</span><span class="mi">15</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="o">-&gt;</span> <span class="n">AsyncIterator</span><span class="p">[</span><span class="n">responder</span><span class="o">.</span><span class="n">SSE</span><span class="p">[</span><span class="n">Progress</span><span class="p">]]:</span>
<span class="k">async</span> <span class="k">for</span> <span class="n">update</span> <span class="ow">in</span> <span class="n">build_updates</span><span class="p">():</span>
<span class="k">yield</span> <span class="n">responder</span><span class="o">.</span><span class="n">SSE</span><span class="p">(</span>
<span class="n">Progress</span><span class="p">(</span><span class="n">job_id</span><span class="o">=</span><span class="n">update</span><span class="o">.</span><span class="n">job_id</span><span class="p">,</span> <span class="n">percent</span><span class="o">=</span><span class="n">update</span><span class="o">.</span><span class="n">percent</span><span class="p">),</span>
<span class="n">event</span><span class="o">=</span><span class="s2">&quot;progress&quot;</span><span class="p">,</span>
<span class="nb">id</span><span class="o">=</span><span class="n">update</span><span class="o">.</span><span class="n">sequence</span><span class="p">,</span>
<span class="p">)</span>
</pre></div>
</div>
<p>On the client side, you consume SSE events with JavaScript’s 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>Yield the annotated model directly when no SSE metadata is needed. Wrap it in
<a class="reference internal" href="api.html#responder.SSE" title="responder.SSE"><code class="xref py py-class docutils literal notranslate"><span class="pre">SSE</span></code></a> to set <code class="docutils literal notranslate"><span class="pre">event</span></code>, <code class="docutils literal notranslate"><span class="pre">id</span></code>, or <code class="docutils literal notranslate"><span class="pre">retry</span></code>; comment-only
events are also supported:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">yield</span> <span class="n">Progress</span><span class="p">(</span><span class="n">job_id</span><span class="o">=</span><span class="mi">7</span><span class="p">,</span> <span class="n">percent</span><span class="o">=</span><span class="mi">42</span><span class="p">)</span>
<span class="k">yield</span> <span class="n">responder</span><span class="o">.</span><span class="n">SSE</span><span class="p">(</span>
<span class="n">Progress</span><span class="p">(</span><span class="n">job_id</span><span class="o">=</span><span class="mi">7</span><span class="p">,</span> <span class="n">percent</span><span class="o">=</span><span class="mi">42</span><span class="p">),</span> <span class="n">event</span><span class="o">=</span><span class="s2">&quot;update&quot;</span><span class="p">,</span> <span class="nb">id</span><span class="o">=</span><span class="s2">&quot;1&quot;</span><span class="p">,</span> <span class="n">retry</span><span class="o">=</span><span class="mi">1500</span>
<span class="p">)</span>
<span class="k">yield</span> <span class="n">responder</span><span class="o">.</span><span class="n">SSE</span><span class="p">(</span><span class="n">comment</span><span class="o">=</span><span class="s2">&quot;still working&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>For long-lived streams behind proxies, pass <code class="docutils literal notranslate"><span class="pre">heartbeat=</span></code> (seconds) to emit a
keepalive comment during idle periods so the connection isn’t dropped. The
response also sets <code class="docutils literal notranslate"><span class="pre">X-Accel-Buffering:</span> <span class="pre">no</span></code> so events flush immediately:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">sse</span><span class="p">(</span><span class="s2">&quot;/events&quot;</span><span class="p">,</span> <span class="n">heartbeat</span><span class="o">=</span><span class="mi">15</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="o">-&gt;</span> <span class="n">AsyncIterator</span><span class="p">[</span><span class="n">Progress</span><span class="p">]:</span>
<span class="o">...</span>
</pre></div>
</div>
<p>When the browser reconnects it sends the id of the last event it saw; read it
with <a class="reference internal" href="api.html#responder.Request.last_event_id" title="responder.Request.last_event_id"><code class="xref py py-attr docutils literal notranslate"><span class="pre">req.last_event_id</span></code></a> to resume:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nd">@api</span><span class="o">.</span><span class="n">sse</span><span class="p">(</span><span class="s2">&quot;/events&quot;</span><span class="p">,</span> <span class="n">heartbeat</span><span class="o">=</span><span class="mi">15</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="o">-&gt;</span> <span class="n">AsyncIterator</span><span class="p">[</span><span class="n">Progress</span><span class="p">]:</span>
<span class="n">resume_from</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="n">last_event_id</span>
<span class="k">async</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">feed</span><span class="p">(</span><span class="n">after</span><span class="o">=</span><span class="n">resume_from</span><span class="p">):</span>
<span class="k">yield</span> <span class="n">responder</span><span class="o">.</span><span class="n">SSE</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">payload</span><span class="p">,</span> <span class="nb">id</span><span class="o">=</span><span class="n">item</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
</pre></div>
</div>
<p>Use <a class="reference internal" href="api.html#responder.API.ndjson" title="responder.API.ndjson"><code class="xref py py-meth docutils literal notranslate"><span class="pre">API.ndjson</span></code></a> for the same typed, incremental
contract over newline-delimited JSON. Each yielded item becomes one compact
JSON line with media type <code class="docutils literal notranslate"><span class="pre">application/x-ndjson</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">ndjson</span><span class="p">(</span><span class="s2">&quot;/exports&quot;</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">exports</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">-&gt;</span> <span class="n">AsyncIterator</span><span class="p">[</span><span class="n">Progress</span><span class="p">]:</span>
<span class="k">async</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">export_rows</span><span class="p">():</span>
<span class="k">yield</span> <span class="n">item</span>
</pre></div>
</div>
<p>The first immediately available item is validated before response headers are
sent, so a broken contract can still return <code class="docutils literal notranslate"><span class="pre">500</span></code>. If a later item fails,
Responder logs the contract error and terminates the stream without writing
the invalid item. Producer cancellation closes the source iterator and keeps
request-scoped dependency teardown tied to the connection lifetime.</p>
<p>For untyped protocol frames and raw streaming escape hatches, the existing
<code class="docutils literal notranslate"><span class="pre">resp.sse()</span></code> and <code class="docutils literal notranslate"><span class="pre">resp.stream()</span></code> APIs remain available.</p>
</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,orjson]&#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>
<p>For production, lock the endpoint down: turn off the in-browser IDE, reject
introspection queries, and cap query nesting depth to blunt
denial-of-service queries:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></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">schema</span><span class="p">,</span>
<span class="n">graphiql</span><span class="o">=</span><span class="n">api</span><span class="o">.</span><span class="n">debug</span><span class="p">,</span> <span class="n">introspection</span><span class="o">=</span><span class="n">api</span><span class="o">.</span><span class="n">debug</span><span class="p">,</span> <span class="n">max_depth</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span>
<span class="p">)</span>
</pre></div>
</div>
</section>
<section id="openapi-documentation">
<span id="tour-openapi"></span><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><strong>Every route appears automatically.</strong> Responder builds the spec from each
route’s methods, path parameters, body and response models, and any
<code class="docutils literal notranslate"><span class="pre">Query</span></code>/<code class="docutils literal notranslate"><span class="pre">Header</span></code>/<code class="docutils literal notranslate"><span class="pre">Cookie</span></code> markers (see <a class="reference internal" href="#pydantic-validation">Pydantic Validation</a>) — so a
route shows up with its parameters and schemas even without a line of
annotation:</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;/health&quot;</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">health</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;status&quot;</span><span class="p">:</span> <span class="s2">&quot;ok&quot;</span><span class="p">}</span> <span class="c1"># documented as GET /health -&gt; 200</span>
</pre></div>
</div>
<p>Validating routes (anything with a request body or typed parameters) also
get an automatic <code class="docutils literal notranslate"><span class="pre">422</span></code> in the spec. To hide a route, pass
<code class="docutils literal notranslate"><span class="pre">include_in_schema=False</span></code>; the internal schema, docs, static, and metrics
endpoints are excluded for you:</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;/internal&quot;</span><span class="p">,</span> <span class="n">include_in_schema</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">internal</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">text</span> <span class="o">=</span> <span class="s2">&quot;private&quot;</span>
</pre></div>
</div>
<p>Operational responses are documented too. CSRF-protected unsafe routes show
<code class="docutils literal notranslate"><span class="pre">403</span></code>, capped request bodies show <code class="docutils literal notranslate"><span class="pre">413</span></code>, rate-limited routes show
<code class="docutils literal notranslate"><span class="pre">429</span></code> (and fail-closed backend <code class="docutils literal notranslate"><span class="pre">503</span></code>), and configured request timeouts
show <code class="docutils literal notranslate"><span class="pre">504</span></code>. With the default Problem Details contract, those responses
reference the reusable <code class="docutils literal notranslate"><span class="pre">ProblemDetails</span></code> schema.</p>
<p>Beyond that baseline, three tools let you enrich and override the generated
operations.</p>
<p><strong>Typed contracts</strong> — the recommended approach. Add a required Pydantic-typed
handler parameter for the request body and a supported return annotation (or
an explicit <code class="docutils literal notranslate"><span class="pre">response_model=</span></code>) for the response. Responder generates the
schema and validates at runtime: invalid bodies get a <code class="docutils literal notranslate"><span class="pre">422</span></code> with detailed
errors, and responses are serialized through the contract (extra fields
stripped, types enforced):</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="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="o">*</span><span class="p">,</span> <span class="n">pet</span><span class="p">:</span> <span class="n">PetIn</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">PetOut</span><span class="p">:</span>
<span class="k">return</span> <span class="n">PetOut</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="n">pet</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="o">=</span><span class="n">pet</span><span class="o">.</span><span class="n">age</span><span class="p">)</span>
</pre></div>
</div>
<p>See <a class="reference internal" href="#pydantic-validation">Pydantic Validation</a> for how these typed signatures behave at runtime.</p>
<p><strong>Decorator metadata</strong> — for operation-level OpenAPI details that should stay
near the route, pass metadata directly to the route decorator. <code class="docutils literal notranslate"><span class="pre">responses</span></code> and
examples are deep-merged with generated response schemas and framework 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">get</span><span class="p">(</span>
<span class="s2">&quot;/pets/{id:int}&quot;</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="n">tags</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;pets&quot;</span><span class="p">],</span>
<span class="n">summary</span><span class="o">=</span><span class="s2">&quot;Fetch a pet&quot;</span><span class="p">,</span>
<span class="n">responses</span><span class="o">=</span><span class="p">{</span><span class="mi">404</span><span class="p">:</span> <span class="s2">&quot;Pet not found&quot;</span><span class="p">},</span>
<span class="n">examples</span><span class="o">=</span><span class="p">{</span>
<span class="s2">&quot;found&quot;</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">&quot;summary&quot;</span><span class="p">:</span> <span class="s2">&quot;Existing pet&quot;</span><span class="p">,</span>
<span class="s2">&quot;value&quot;</span><span class="p">:</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="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Fido&quot;</span><span class="p">,</span> <span class="s2">&quot;age&quot;</span><span class="p">:</span> <span class="mi">4</span><span class="p">},</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="n">openapi_extra</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;x-codeSamples&quot;</span><span class="p">:</span> <span class="p">[]},</span>
<span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">get_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="o">*</span><span class="p">,</span> <span class="nb">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="nb">id</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> <span class="s2">&quot;age&quot;</span><span class="p">:</span> <span class="mi">4</span><span class="p">}</span>
</pre></div>
</div>
<p>Routes whose success status isn’t <code class="docutils literal notranslate"><span class="pre">200</span></code> can declare it with
<code class="docutils literal notranslate"><span class="pre">status_code=</span></code>. It does double duty: <code class="docutils literal notranslate"><span class="pre">resp.status_code</span></code> is pre-seeded
with it before the handler runs (so the handler only sets the body), and the
generated OpenAPI operation documents the success response under that status
instead of <code class="docutils literal notranslate"><span class="pre">200</span></code>. Declaring <code class="docutils literal notranslate"><span class="pre">status_code=204</span></code> documents no response
body. An explicit <code class="docutils literal notranslate"><span class="pre">resp.status_code</span> <span class="pre">=</span> <span class="pre">...</span></code> in the handler still wins:</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="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">status_code</span><span class="o">=</span><span class="mi">201</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="o">*</span><span class="p">,</span> <span class="n">pet</span><span class="p">:</span> <span class="n">PetIn</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="s2">&quot;name&quot;</span><span class="p">:</span> <span class="n">pet</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="s2">&quot;age&quot;</span><span class="p">:</span> <span class="n">pet</span><span class="o">.</span><span class="n">age</span><span class="p">}</span> <span class="c1"># -&gt; 201</span>
</pre></div>
</div>
<p><strong>YAML docstrings</strong> — for fine-grained control, embed OpenAPI YAML in the
docstring; it is deep-merged <em>on top of</em> the auto-generated operation, so
you override only what you mention:</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 you’re 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, you’ll 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
group’s 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>
<p>Groups register on a live <code class="docutils literal notranslate"><span class="pre">api</span></code> instance. To declare routes in separate
modules — without an <code class="docutils literal notranslate"><span class="pre">API</span></code> at all — use the standalone
<a class="reference internal" href="api.html#responder.Router" title="responder.Router"><code class="xref py py-class docutils literal notranslate"><span class="pre">responder.Router</span></code></a> and <code class="docutils literal notranslate"><span class="pre">api.include_router()</span></code>; see <a class="reference internal" href="routers.html"><span class="doc">Composing Apps with Routers</span></a>.</p>
</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;/notebooks&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;/notebooks/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;&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. They’re 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, you’ll 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 it’s 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. Responder’s
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 won’t 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>
<p>Sessions are on by default (<code class="docutils literal notranslate"><span class="pre">sessions=&quot;auto&quot;</span></code>). You read and write
<code class="docutils literal notranslate"><span class="pre">req.session</span></code> like a dict; <code class="docutils literal notranslate"><span class="pre">resp.session</span></code> is a write-through view of the
same data, so <code class="docutils literal notranslate"><span class="pre">resp.session</span> <span class="pre">=</span> <span class="pre">{&quot;username&quot;:</span> <span class="pre">&quot;alice&quot;}</span></code> replaces it wholesale.
Pass <code class="docutils literal notranslate"><span class="pre">sessions=False</span></code> to turn the middleware off entirely (then touching
<code class="docutils literal notranslate"><span class="pre">req.session</span></code> raises a guiding <code class="docutils literal notranslate"><span class="pre">RuntimeError</span></code>).</p>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p><strong>Set a stable secret key in production.</strong> Responder signs cookie
sessions with <code class="docutils literal notranslate"><span class="pre">secret_key</span></code> (or the <code class="docutils literal notranslate"><span class="pre">RESPONDER_SECRET_KEY</span></code> environment
variable). If neither is set, it mints a <em>random per-process</em> key and
logs a warning — fine for a quick demo, but it means every worker and
every restart gets a different key, logging users out. Generate one once:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">python</span> <span class="o">-</span><span class="n">c</span> <span class="s2">&quot;import secrets; print(secrets.token_urlsafe(32))&quot;</span>
</pre></div>
</div>
<p>then pass it explicitly (or via the env var):</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;&lt;your-32+-char-random-key&gt;&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>The old public placeholder <code class="docutils literal notranslate"><span class="pre">secret_key=&quot;NOTASECRET&quot;</span></code> now raises
<code class="docutils literal notranslate"><span class="pre">SessionConfigError</span></code>, and <code class="docutils literal notranslate"><span class="pre">sessions=True</span></code> (strict mode) refuses to
start without a real key.</p>
</div>
<p>Session cookies are <code class="docutils literal notranslate"><span class="pre">HttpOnly</span></code> always, and <code class="docutils literal notranslate"><span class="pre">Secure</span></code> in production
(<code class="docutils literal notranslate"><span class="pre">debug=False</span></code>) by default. Behind a TLS proxy this needs no action; pass
<code class="docutils literal notranslate"><span class="pre">session_https_only=False</span></code> only when you genuinely serve plain HTTP, such
as local dev or tests over <code class="docutils literal notranslate"><span class="pre">http://</span></code>. <code class="docutils literal notranslate"><span class="pre">SameSite</span></code> defaults to <code class="docutils literal notranslate"><span class="pre">&quot;lax&quot;</span></code>;
<code class="docutils literal notranslate"><span class="pre">session_same_site=&quot;none&quot;</span></code> requires a Secure cookie. Adjust the cookie
lifetime with <code class="docutils literal notranslate"><span class="pre">session_max_age</span></code> (default 14 days).</p>
<p>By default, session data lives <em>in</em> the cookie (signed to prevent
tampering). That’s 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 (so
<code class="docutils literal notranslate"><span class="pre">secret_key</span></code> no longer matters):</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> (or
<code class="docutils literal notranslate"><span class="pre">AsyncRedisSessionBackend</span></code> in async apps) so all workers share the store.
Custom backends are duck-typed — any object with <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> works. After a login or
privilege change, call <code class="docutils literal notranslate"><span class="pre">regenerate_session(req)</span></code> (from
<code class="docutils literal notranslate"><span class="pre">responder.ext.sessions</span></code>) to rotate the session ID and defeat session
fixation. For a full login flow, see <a class="reference internal" href="tutorial-auth.html"><span class="doc">Authentication</span></a>.</p>
<p>Treat session values as JSON-shaped data (dicts, lists, strings, numbers,
booleans, <code class="docutils literal notranslate"><span class="pre">None</span></code>) so the same code works on every backend. The memory
backend stores Python objects as-is; the Redis backends serialize, and
their default codec also round-trips <code class="docutils literal notranslate"><span class="pre">datetime</span></code>, <code class="docutils literal notranslate"><span class="pre">date</span></code>, <code class="docutils literal notranslate"><span class="pre">time</span></code>,
<code class="docutils literal notranslate"><span class="pre">Decimal</span></code>, <code class="docutils literal notranslate"><span class="pre">UUID</span></code>, <code class="docutils literal notranslate"><span class="pre">set</span></code>, <code class="docutils literal notranslate"><span class="pre">frozenset</span></code>, and <code class="docutils literal notranslate"><span class="pre">bytes</span></code> values —
so <code class="docutils literal notranslate"><span class="pre">req.session[&quot;expires&quot;]</span> <span class="pre">=</span> <span class="pre">datetime.now()</span></code> keeps working when you move
from the memory backend in development to Redis in production. Tuples come
back as lists on the Redis path (a JSON limitation). For anything richer,
pass <code class="docutils literal notranslate"><span class="pre">serializer=</span></code> to the Redis backends — any object with
<code class="docutils literal notranslate"><span class="pre">dumps(data)</span></code> and <code class="docutils literal notranslate"><span class="pre">loads(raw)</span></code> methods works.</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. When a <code class="docutils literal notranslate"><span class="pre">static/</span></code> directory exists next to your app,
Responder serves it automatically — no configuration needed:</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>
</pre></div>
</div>
<p>Place your assets in the <code class="docutils literal notranslate"><span class="pre">static/</span></code> directory and they’ll be served 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. Use <code class="docutils literal notranslate"><span class="pre">static_dir</span></code> and
<code class="docutils literal notranslate"><span class="pre">static_route</span></code> to serve a different directory or URL prefix.</p>
<p>The directory is never created for you: the implicit default <code class="docutils literal notranslate"><span class="pre">static/</span></code>
is simply skipped if it doesn’t exist, while a <code class="docutils literal notranslate"><span class="pre">static_dir</span></code> you pass
explicitly must exist — a missing one raises <code class="docutils literal notranslate"><span class="pre">FileNotFoundError</span></code> at
construction, so create the directory before pointing Responder at it.
Pass <code class="docutils literal notranslate"><span class="pre">static_dir=None</span></code> to disable static file serving entirely.</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="kn">import</span><span class="w"> </span><span class="nn">pathlib</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">spa</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">html</span> <span class="o">=</span> <span class="n">pathlib</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="s2">&quot;static/index.html&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">read_text</span><span class="p">()</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">spa</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="csrf-protection">
<h2>CSRF Protection<a class="headerlink" href="#csrf-protection" title="Link to this heading"></a></h2>
<p>Cookie sessions make browsers <em>ambient</em> authenticators: any site can make a
visitor’s browser POST to your app, cookies included. <a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF">CSRF</a>
protection defeats that by requiring a token an attacker’s page can’t read.
It’s off by default; switch it on and every unsafe request
(<code class="docutils literal notranslate"><span class="pre">POST</span></code>/<code class="docutils literal notranslate"><span class="pre">PUT</span></code>/<code class="docutils literal notranslate"><span class="pre">PATCH</span></code>/<code class="docutils literal notranslate"><span class="pre">DELETE</span></code>) must present the session’s token or
it gets a <code class="docutils literal notranslate"><span class="pre">403</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">csrf</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</pre></div>
</div>
<p>For HTML forms, pass the request into your template context and drop the
ready-made hidden input inside each <code class="docutils literal notranslate"><span class="pre">&lt;form</span> <span class="pre">method=&quot;post&quot;&gt;</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">{{</span> <span class="n">req</span><span class="o">.</span><span class="n">csrf_input</span> <span class="p">}}</span>
</pre></div>
</div>
<p>For JavaScript clients, read <code class="docutils literal notranslate"><span class="pre">req.csrf_token</span></code> (expose it in a page or a
small endpoint) and send it back in an <code class="docutils literal notranslate"><span class="pre">X-CSRF-Token</span></code> header. The token is
minted on first access, lives in the signed session, and stays stable for
the session’s lifetime.</p>
<p>Endpoints that are <em>supposed</em> to receive cross-site POSTs — webhook
receivers, token-authenticated APIs — opt out per route; conversely a single
route can opt in without the app-wide switch:</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;/webhooks/stripe&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">csrf</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">stripe_webhook</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="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/account/delete&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">csrf</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">delete_account</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>
<p>The token check shares the request’s single (streaming) form parse, so a
protected multipart upload is still parsed exactly once.</p>
</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>
<p>This redirects HTTP requests to HTTPS and sends a
<code class="docutils literal notranslate"><span class="pre">Strict-Transport-Security</span></code> header on responses. For a custom <code class="docutils literal notranslate"><span class="pre">max-age</span></code>
or to enable preloading, install the middleware 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.middleware</span><span class="w"> </span><span class="kn">import</span> <span class="n">HSTSMiddleware</span>
<span class="n">api</span><span class="o">.</span><span class="n">add_middleware</span><span class="p">(</span><span class="n">HSTSMiddleware</span><span class="p">,</span> <span class="n">max_age</span><span class="o">=</span><span class="mi">63072000</span><span class="p">,</span> <span class="n">preload</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="security-headers">
<h2>Security Headers<a class="headerlink" href="#security-headers" title="Link to this heading"></a></h2>
<p>Opt in to common security headers on every response —
<code class="docutils literal notranslate"><span class="pre">X-Content-Type-Options:</span> <span class="pre">nosniff</span></code>, <code class="docutils literal notranslate"><span class="pre">X-Frame-Options:</span> <span class="pre">DENY</span></code>, and
<code class="docutils literal notranslate"><span class="pre">Referrer-Policy:</span> <span class="pre">strict-origin-when-cross-origin</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">security_headers</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</pre></div>
</div>
<p>Add a Content-Security-Policy or Permissions-Policy, or override any header,
by passing options through:</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">security_headers</span><span class="o">=</span><span class="p">{</span>
<span class="s2">&quot;content_security_policy&quot;</span><span class="p">:</span> <span class="s2">&quot;default-src &#39;self&#39;&quot;</span><span class="p">,</span>
<span class="s2">&quot;headers&quot;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&quot;X-Frame-Options&quot;</span><span class="p">:</span> <span class="s2">&quot;SAMEORIGIN&quot;</span><span class="p">},</span>
<span class="p">})</span>
</pre></div>
</div>
<p>A header a handler sets itself is always left untouched.</p>
</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="authentication">
<h2>Authentication<a class="headerlink" href="#authentication" title="Link to this heading"></a></h2>
<p><code class="docutils literal notranslate"><span class="pre">responder.ext.auth</span></code> provides Bearer, Basic, and API-key schemes. Each one
is a callable that pulls the credential from the request, verifies it, and
returns the principal your <code class="docutils literal notranslate"><span class="pre">verify</span></code> callback produced — or raises <code class="docutils literal notranslate"><span class="pre">401</span></code>
with the right <code class="docutils literal notranslate"><span class="pre">WWW-Authenticate</span></code> challenge. The most direct form is
<code class="docutils literal notranslate"><span class="pre">auth=</span></code> on a route:</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.auth</span><span class="w"> </span><span class="kn">import</span> <span class="n">BearerAuth</span>
<span class="n">auth</span> <span class="o">=</span> <span class="n">BearerAuth</span><span class="p">(</span><span class="n">verify</span><span class="o">=</span><span class="k">lambda</span> <span class="n">token</span><span class="p">:</span> <span class="n">users</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">token</span><span class="p">))</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/me&quot;</span><span class="p">,</span> <span class="n">auth</span><span class="o">=</span><span class="n">auth</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">me</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</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">user</span><span class="p">}</span>
</pre></div>
</div>
<p>Responder enforces the scheme, registers the OpenAPI security scheme when
OpenAPI is enabled, stores the principal on <code class="docutils literal notranslate"><span class="pre">req.state.user</span></code> /
<code class="docutils literal notranslate"><span class="pre">req.state.auth</span></code>, and injects it into <code class="docutils literal notranslate"><span class="pre">user</span></code>, <code class="docutils literal notranslate"><span class="pre">principal</span></code>, or <code class="docutils literal notranslate"><span class="pre">auth</span></code>
parameters. The older explicit form still works when you want to separate
runtime dependency wiring from documentation:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">auth</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">api</span><span class="p">)</span>
<span class="n">api</span><span class="o">.</span><span class="n">add_dependency</span><span class="p">(</span><span class="s2">&quot;user&quot;</span><span class="p">,</span> <span class="n">auth</span><span class="p">)</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/me&quot;</span><span class="p">,</span> <span class="n">security</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;bearerAuth&quot;</span><span class="p">])</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">me</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</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">user</span><span class="p">}</span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">verify</span></code> may be sync or async; return a truthy principal to accept or a falsy
value to reject. For static secrets, pass them directly and the scheme compares
them in constant time:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">BearerAuth</span><span class="p">(</span><span class="n">tokens</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;s3cret&quot;</span><span class="p">])</span>
<span class="n">APIKeyAuth</span><span class="p">(</span><span class="n">keys</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;abc123&quot;</span><span class="p">],</span> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;X-API-Key&quot;</span><span class="p">)</span> <span class="c1"># or location=&quot;query&quot;</span>
<span class="n">BasicAuth</span><span class="p">(</span><span class="n">credentials</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;alice&quot;</span><span class="p">:</span> <span class="s2">&quot;password&quot;</span><span class="p">})</span>
</pre></div>
</div>
<p>Calling <code class="docutils literal notranslate"><span class="pre">register()</span></code> (or <code class="xref py py-meth docutils literal notranslate"><span class="pre">api.add_security_scheme()</span></code>) makes the scheme
appear in the OpenAPI document, so the interactive docs grow an <strong>Authorize</strong>
button. <code class="docutils literal notranslate"><span class="pre">security=</span></code> on a route marks which operations require it; pass
<code class="docutils literal notranslate"><span class="pre">default=True</span></code> to <code class="docutils literal notranslate"><span class="pre">add_security_scheme</span></code> to require a scheme everywhere.</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), it’s
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>
<p>An inbound ID is honored only when it looks like a request ID — at most 128
characters of letters, digits, <code class="docutils literal notranslate"><span class="pre">.</span></code>, <code class="docutils literal notranslate"><span class="pre">_</span></code>, and <code class="docutils literal notranslate"><span class="pre">-</span></code>. Anything else (an
oversized header, control characters) is discarded and a fresh UUID minted
instead, so a client can’t bloat your responses or log lines.
<code class="docutils literal notranslate"><span class="pre">enable_logging=True</span></code> applies exactly the same policy and mints the same
UUID format, so switching access logging on or off never changes the IDs
your log pipeline parses.</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, so since 9.0
bodies are capped at <strong>100 MiB by default</strong>. Oversized requests 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. Tune the cap (or disable it) per application:</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>
<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="kc">None</span><span class="p">)</span> <span class="c1"># unlimited (pre-9.0)</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. Multipart uploads
stream to disk as they arrive, so a generous cap does not translate into
memory pressure.</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 shouldn’t 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 can’t be interrupted —
the client gets the 504 on time, but the thread runs to completion in the
background.</p>
<p>WebSocket handlers have a matching guard. Pass <code class="docutils literal notranslate"><span class="pre">ws_idle_timeout</span></code> to close a
connection that waits too long for the next inbound message (close code
<code class="docutils literal notranslate"><span class="pre">1001</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">ws_idle_timeout</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span> <span class="c1"># seconds</span>
</pre></div>
</div>
<p>The deadline resets on every message received, so it bounds <em>idle</em> time between
messages rather than the total connection lifetime — a continuously active
client is never closed. It defaults to <code class="docutils literal notranslate"><span class="pre">None</span></code> (no timeout).</p>
</section>
<section id="rate-limiting">
<span id="tour-rate-limiting"></span><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. It’s essential for public APIs, and good practice even
for internal ones.</p>
<p>Responder includes a built-in rate limiter (a sliding window in memory; a
fixed window on the Redis backends):</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>, <code class="docutils literal notranslate"><span class="pre">X-RateLimit-Remaining</span></code>, and <code class="docutils literal notranslate"><span class="pre">X-RateLimit-Reset</span></code>
(seconds until the window resets) headers so clients can pace themselves.
With the default <code class="docutils literal notranslate"><span class="pre">problem_details=True</span></code> setting, over-limit and fail-closed
backend responses use the same <code class="docutils literal notranslate"><span class="pre">application/problem+json</span></code> envelope as
framework-generated errors; pass <code class="docutils literal notranslate"><span class="pre">problem_details=False</span></code> on the API to keep
the legacy <code class="docutils literal notranslate"><span class="pre">{&quot;error&quot;:</span> <span class="pre">...}</span></code> JSON body.</p>
<p>The rate limiter is per-client, keyed by IP address by default. To key by
something else — an API key, an authenticated user id — pass <code class="docutils literal notranslate"><span class="pre">key=</span></code>, a
callable receiving the request:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></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">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">req</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;x-api-key&quot;</span><span class="p">,</span> <span class="s2">&quot;anonymous&quot;</span><span class="p">),</span>
<span class="p">)</span>
</pre></div>
</div>
<p>If the key function returns <code class="docutils literal notranslate"><span class="pre">None</span></code> (or an empty string), the request
falls back to the IP-based key.</p>
<p>By default, counts live in process memory. The in-memory store tracks at most
<code class="docutils literal notranslate"><span class="pre">max_keys</span></code> distinct clients (100k by default), evicting the least-recently
seen beyond that, so a client rotating source IPs can’t grow memory without
bound. 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>In async apps, reach for <code class="docutils literal notranslate"><span class="pre">AsyncRedisBackend</span></code> instead — it talks to Redis
without a thread-pool hop. It’s async-only, so drive it with
<code class="docutils literal notranslate"><span class="pre">limiter.install(api)</span></code> (or <code class="docutils literal notranslate"><span class="pre">await</span> <span class="pre">limiter.acheck(req,</span> <span class="pre">resp)</span></code>) rather than
a sync route:</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">AsyncRedisBackend</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">AsyncRedisBackend</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>
<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>A word on outages: when the backend errors out (say, Redis is unreachable),
the limiter answers <code class="docutils literal notranslate"><span class="pre">503</span> <span class="pre">Service</span> <span class="pre">Unavailable</span></code> by default — nothing slips
past the limit, but rate-limited routes go down with the backend. Pass
<code class="docutils literal notranslate"><span class="pre">fail_open=True</span></code> to make the opposite trade-off: requests are let through
unmetered, with a warning logged, until the backend recovers. Choose
<code class="docutils literal notranslate"><span class="pre">fail_open=True</span></code> when availability matters more than strict enforcement
(most public APIs); keep the default when the limiter is a security control
(login attempts, expensive mutations).</p>
<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></code> method returning
<code class="docutils literal notranslate"><span class="pre">(allowed,</span> <span class="pre">remaining)</span></code> — or <code class="docutils literal notranslate"><span class="pre">(allowed,</span> <span class="pre">remaining,</span> <span class="pre">reset_after)</span></code> to
populate <code class="docutils literal notranslate"><span class="pre">X-RateLimit-Reset</span></code> — works as a backend (<code class="docutils literal notranslate"><span class="pre">ahit</span></code> for the async
variant), 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>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>Prefer <code class="docutils literal notranslate"><span class="pre">limiter.install(api)</span></code> or <code class="docutils literal notranslate"><span class="pre">&#64;limiter.limit</span></code> over calling
<code class="docutils literal notranslate"><span class="pre">limiter.check(req,</span> <span class="pre">resp)</span></code> from a hand-written before-request hook.
All three enforce identically, but only the first two are visible to the
OpenAPI generator — a manual <code class="docutils literal notranslate"><span class="pre">check()</span></code> call is arbitrary code, so the
generated schema won’t list the <code class="docutils literal notranslate"><span class="pre">429</span></code>/<code class="docutils literal notranslate"><span class="pre">503</span></code> responses for it.</p>
</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>), a latency histogram
(<code class="docutils literal notranslate"><span class="pre">responder_request_duration_seconds</span></code>), and an in-flight gauge
(<code class="docutils literal notranslate"><span class="pre">responder_requests_in_flight</span></code> — requests currently being handled, the
series capacity dashboards want). 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>
<p>The histogram’s bucket bounds default to 5ms–10s
(<code class="xref py py-data docutils literal notranslate"><span class="pre">responder.ext.metrics.BUCKETS</span></code>). If your latency profile is
different — 30-second report endpoints, sub-millisecond cache hits — pass
your own ascending bounds via <code class="docutils literal notranslate"><span class="pre">metrics_buckets=</span></code> (or construct a
<code class="xref py py-class docutils literal notranslate"><span class="pre">MetricsCollector</span></code> with <code class="docutils literal notranslate"><span class="pre">buckets=</span></code>
directly):</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>
<span class="n">metrics_buckets</span><span class="o">=</span><span class="p">(</span><span class="mf">0.1</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">5.0</span><span class="p">,</span> <span class="mf">30.0</span><span class="p">),</span>
<span class="p">)</span>
</pre></div>
</div>
</section>
<section id="health-checks">
<h2>Health Checks<a class="headerlink" href="#health-checks" title="Link to this heading"></a></h2>
<p>Orchestrators (Kubernetes, load balancers) want a readiness endpoint that
reflects whether the app’s dependencies are actually reachable. Register
checks and Responder aggregates them:</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">health_route</span><span class="o">=</span><span class="s2">&quot;/health&quot;</span><span class="p">)</span>
<span class="n">api</span><span class="o">.</span><span class="n">add_health_check</span><span class="p">(</span><span class="s2">&quot;db&quot;</span><span class="p">,</span> <span class="k">lambda</span><span class="p">:</span> <span class="n">database</span><span class="o">.</span><span class="n">ping</span><span class="p">())</span>
<span class="n">api</span><span class="o">.</span><span class="n">add_health_check</span><span class="p">(</span><span class="s2">&quot;cache&quot;</span><span class="p">,</span> <span class="n">check_redis</span><span class="p">)</span>
</pre></div>
</div>
<p>A check passes unless it returns <code class="docutils literal notranslate"><span class="pre">False</span></code> or raises. The endpoint returns
<code class="docutils literal notranslate"><span class="pre">200</span></code> with <code class="docutils literal notranslate"><span class="pre">{&quot;status&quot;:</span> <span class="pre">&quot;ok&quot;,</span> <span class="pre">&quot;checks&quot;:</span> <span class="pre">{...}}</span></code> when all pass, and <code class="docutils literal notranslate"><span class="pre">503</span></code>
otherwise — with each check’s status (and the error detail for any that
raised). Checks may be sync or async, and the route is excluded from the
OpenAPI schema. (<code class="docutils literal notranslate"><span class="pre">add_health_check</span></code> adds the route at <code class="docutils literal notranslate"><span class="pre">/health</span></code> on first
use if you didn’t set <code class="docutils literal notranslate"><span class="pre">health_route</span></code>.)</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 Python’s 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 don’t get
duplicate headers.</p>
<p>For debugging route control flow, pass <code class="docutils literal notranslate"><span class="pre">trace_dispatch=True</span></code>. Responder emits
debug logs for the documented dispatch stages: before hooks, auth,
dependencies, handler execution, and after hooks:</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> <span class="n">trace_dispatch</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</pre></div>
</div>
</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
Responder’s routing. Add a required Pydantic-typed body parameter to validate
incoming data and annotate the return value to control the outgoing shape:</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="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="o">*</span><span class="p">,</span> <span class="n">item</span><span class="p">:</span> <span class="n">ItemIn</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">ItemOut</span><span class="p">:</span>
<span class="k">return</span> <span class="n">ItemOut</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="o">**</span><span class="n">item</span><span class="o">.</span><span class="n">model_dump</span><span class="p">())</span>
</pre></div>
</div>
<p>When a write-method handler has a required Pydantic-typed body parameter:</p>
<ul class="simple">
<li><p>Valid requests are parsed, validated, and injected as a model instance.</p></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 don’t write any validation code.</p></li>
</ul>
<p>When a response contract is inferred or explicitly 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>For individual query parameters, headers, and cookies, declare them as
keyword-only arguments with a <em>marker</em> default. Responder reads the value,
coerces it to the annotated type, and injects it — returning <code class="docutils literal notranslate"><span class="pre">422</span></code> if a
required value is missing or won’t coerce:</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</span><span class="w"> </span><span class="kn">import</span> <span class="n">Query</span><span class="p">,</span> <span class="n">Header</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="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="o">*</span><span class="p">,</span>
<span class="n">q</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="o">...</span><span class="p">),</span>
<span class="n">limit</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span>
<span class="n">tags</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">Query</span><span class="p">([]),</span>
<span class="n">user_agent</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Header</span><span class="p">(</span><span class="s2">&quot;unknown&quot;</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;q&quot;</span><span class="p">:</span> <span class="n">q</span><span class="p">,</span> <span class="s2">&quot;limit&quot;</span><span class="p">:</span> <span class="n">limit</span><span class="p">,</span> <span class="s2">&quot;tags&quot;</span><span class="p">:</span> <span class="n">tags</span><span class="p">}</span>
</pre></div>
</div>
<ul>
<li><p><code class="docutils literal notranslate"><span class="pre">Query(...)</span></code> (an Ellipsis) marks the value <strong>required</strong>; <code class="docutils literal notranslate"><span class="pre">Query(10)</span></code>
makes it optional with that default.</p></li>
<li><p>The annotation drives coercion: <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>, and a sequence
type like <code class="docutils literal notranslate"><span class="pre">list[str]</span></code> collects repeated keys (<code class="docutils literal notranslate"><span class="pre">?tags=a&amp;tags=b</span></code>).</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">Header</span></code> looks up the header named after the parameter, converting
underscores to dashes (<code class="docutils literal notranslate"><span class="pre">user_agent</span></code> → <code class="docutils literal notranslate"><span class="pre">user-agent</span></code>); pass <code class="docutils literal notranslate"><span class="pre">alias=</span></code>
for an explicit name. <code class="docutils literal notranslate"><span class="pre">Cookie</span></code> reads cookies, and <code class="docutils literal notranslate"><span class="pre">Path</span></code> re-validates
or renames a path segment:</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</span><span class="w"> </span><span class="kn">import</span> <span class="n">Path</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">&quot;/users/{uid: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="nb">int</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="o">...</span><span class="p">,</span> <span class="n">alias</span><span class="o">=</span><span class="s2">&quot;uid&quot;</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>
</pre></div>
</div>
</li>
</ul>
<p>All four markers are exported from the top level
(<code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">responder</span> <span class="pre">import</span> <span class="pre">Query,</span> <span class="pre">Header,</span> <span class="pre">Cookie,</span> <span class="pre">Path</span></code>) and feed the
generated OpenAPI <code class="docutils literal notranslate"><span class="pre">parameters</span></code>.</p>
<p>To validate the whole query string as a single model instead, use
<code class="docutils literal notranslate"><span class="pre">params_model</span></code> — values are coerced, 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>The body and the response validate from type hints too. On
<code class="docutils literal notranslate"><span class="pre">POST</span></code>/<code class="docutils literal notranslate"><span class="pre">PUT</span></code>/<code class="docutils literal notranslate"><span class="pre">PATCH</span></code>/<code class="docutils literal notranslate"><span class="pre">DELETE</span></code>, a keyword-only parameter annotated
with a Pydantic model (and no default) receives the parsed, validated body —
and a supported return annotation becomes the response contract:</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="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="o">*</span><span class="p">,</span> <span class="n">item</span><span class="p">:</span> <span class="n">ItemIn</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">ItemOut</span><span class="p">:</span>
<span class="k">return</span> <span class="n">ItemOut</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="n">item</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">price</span><span class="o">=</span><span class="n">item</span><span class="o">.</span><span class="n">price</span><span class="p">)</span>
</pre></div>
</div>
<p>An invalid or non-object body returns <code class="docutils literal notranslate"><span class="pre">422</span></code> before your handler runs. The
<code class="docutils literal notranslate"><span class="pre">-&gt;</span> <span class="pre">ItemOut</span></code> return annotation validates and coerces the outgoing value and
strips undeclared fields; if the payload violates the contract it fails closed
(a <code class="docutils literal notranslate"><span class="pre">500</span></code> in production, or re-raises under <code class="docutils literal notranslate"><span class="pre">debug=True</span></code>) rather than
leaking a malformed response. Validation runs after after-request hooks, so the
contract describes what is actually sent. Opt out with
<code class="docutils literal notranslate"><span class="pre">&#64;api.route(...,</span> <span class="pre">response_model=False)</span></code>.</p>
<p>Inference uses Pydantic’s <code class="docutils literal notranslate"><span class="pre">TypeAdapter</span></code> support boundary. Models,
dataclasses, typed dictionaries, <code class="docutils literal notranslate"><span class="pre">list[ItemOut]</span></code>, <code class="docutils literal notranslate"><span class="pre">Page[ItemOut]</span></code>, unions,
and JSON scalar types all drive runtime validation, OpenAPI, and generated
clients. A union like <code class="docutils literal notranslate"><span class="pre">ItemOut</span> <span class="pre">|</span> <span class="pre">ErrorOut</span></code> emits <code class="docutils literal notranslate"><span class="pre">anyOf</span></code>/<code class="docutils literal notranslate"><span class="pre">oneOf</span></code> and
validates the selected branch. String and bytes annotations default to
<code class="docutils literal notranslate"><span class="pre">text/plain</span></code> and <code class="docutils literal notranslate"><span class="pre">application/octet-stream</span></code>; other inferred contracts use
<code class="docutils literal notranslate"><span class="pre">application/json</span></code>. Explicit <code class="docutils literal notranslate"><span class="pre">response_model=</span></code> remains available when the
signature cannot or should not carry the contract.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>A raw ORM object needs a model configured with <code class="docutils literal notranslate"><span class="pre">from_attributes=True</span></code>, or
an explicit <code class="docutils literal notranslate"><span class="pre">ItemOut.model_validate(obj)</span></code> before it is returned.</p>
</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="pagination">
<h2>Pagination<a class="headerlink" href="#pagination" title="Link to this heading"></a></h2>
<p><code class="docutils literal notranslate"><span class="pre">responder.ext.pagination</span></code> provides a generic <code class="docutils literal notranslate"><span class="pre">Page</span></code> envelope and a
<code class="docutils literal notranslate"><span class="pre">paginate</span></code> helper for list endpoints. Pair them with the typed <code class="docutils literal notranslate"><span class="pre">Query</span></code>
markers and a <code class="docutils literal notranslate"><span class="pre">Page[Model]</span></code> response model:</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</span><span class="w"> </span><span class="kn">import</span> <span class="n">Query</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">responder.ext.pagination</span><span class="w"> </span><span class="kn">import</span> <span class="n">Page</span><span class="p">,</span> <span class="n">paginate</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/items&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="o">*</span><span class="p">,</span>
<span class="n">page</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">ge</span><span class="o">=</span><span class="mi">1</span><span class="p">),</span>
<span class="n">size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="n">ge</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">le</span><span class="o">=</span><span class="mi">100</span><span class="p">))</span> <span class="o">-&gt;</span> <span class="n">Page</span><span class="p">[</span><span class="n">Item</span><span class="p">]:</span>
<span class="k">return</span> <span class="n">paginate</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">all</span><span class="p">(),</span> <span class="n">page</span><span class="o">=</span><span class="n">page</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="n">size</span><span class="p">)</span>
</pre></div>
</div>
<p>The response is an envelope with <code class="docutils literal notranslate"><span class="pre">items</span></code>, <code class="docutils literal notranslate"><span class="pre">total</span></code>, <code class="docutils literal notranslate"><span class="pre">page</span></code>, <code class="docutils literal notranslate"><span class="pre">size</span></code>, and
<code class="docutils literal notranslate"><span class="pre">pages</span></code>, and OpenAPI documents it as an inline object referencing your element
model. <code class="docutils literal notranslate"><span class="pre">paginate</span></code> slices an in-memory collection by default; when you page in
the database yourself, pass the already-sliced rows plus the overall
<code class="docutils literal notranslate"><span class="pre">total=</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">rows</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">limit</span><span class="o">=</span><span class="n">size</span><span class="p">,</span> <span class="n">offset</span><span class="o">=</span><span class="p">(</span><span class="n">page</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="n">size</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">paginate</span><span class="p">(</span><span class="n">rows</span><span class="p">,</span> <span class="n">page</span><span class="o">=</span><span class="n">page</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="n">size</span><span class="p">,</span> <span class="n">total</span><span class="o">=</span><span class="n">db</span><span class="o">.</span><span class="n">count</span><span class="p">())</span>
</pre></div>
</div>
<p>Many REST consumers navigate by response <em>headers</em> instead of the body
envelope — the GitHub-style <span class="target" id="index-0"></span><a class="rfc reference external" href="https://datatracker.ietf.org/doc/html/rfc8288.html"><strong>RFC 8288</strong></a> <code class="docutils literal notranslate"><span class="pre">Link</span></code> header plus
<code class="docutils literal notranslate"><span class="pre">X-Total-Count</span></code>. Call <code class="docutils literal notranslate"><span class="pre">set_pagination_headers</span></code> to emit both:</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.pagination</span><span class="w"> </span><span class="kn">import</span> <span class="n">paginate</span><span class="p">,</span> <span class="n">set_pagination_headers</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/items&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="o">*</span><span class="p">,</span>
<span class="n">page</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">ge</span><span class="o">=</span><span class="mi">1</span><span class="p">),</span>
<span class="n">size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="n">ge</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">le</span><span class="o">=</span><span class="mi">100</span><span class="p">))</span> <span class="o">-&gt;</span> <span class="n">Page</span><span class="p">[</span><span class="n">Item</span><span class="p">]:</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">paginate</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">all</span><span class="p">(),</span> <span class="n">page</span><span class="o">=</span><span class="n">page</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="n">size</span><span class="p">)</span>
<span class="n">set_pagination_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">result</span><span class="p">)</span>
<span class="k">return</span> <span class="n">result</span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">Link</span></code> header carries <code class="docutils literal notranslate"><span class="pre">rel=&quot;first&quot;</span></code>/<code class="docutils literal notranslate"><span class="pre">&quot;prev&quot;</span></code>/<code class="docutils literal notranslate"><span class="pre">&quot;next&quot;</span></code>/<code class="docutils literal notranslate"><span class="pre">&quot;last&quot;</span></code>
URLs built from the request’s own URL — every other query parameter
(filters, sort specs) is preserved; only <code class="docutils literal notranslate"><span class="pre">page</span></code> and <code class="docutils literal notranslate"><span class="pre">size</span></code> are
rewritten. <code class="docutils literal notranslate"><span class="pre">prev</span></code> is omitted on the first page and <code class="docutils literal notranslate"><span class="pre">next</span></code> on the last.</p>
<section id="sorting-and-filtering">
<h3>Sorting and Filtering<a class="headerlink" href="#sorting-and-filtering" title="Link to this heading"></a></h3>
<p><code class="docutils literal notranslate"><span class="pre">responder.ext.query</span></code> rounds out list endpoints with <code class="docutils literal notranslate"><span class="pre">sort_items</span></code> and
<code class="docutils literal notranslate"><span class="pre">filter_items</span></code> — in-memory helpers (dicts or objects, no ORM coupling) that
pair with the typed markers and <code class="docutils literal notranslate"><span class="pre">paginate</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.query</span><span class="w"> </span><span class="kn">import</span> <span class="n">filter_items</span><span class="p">,</span> <span class="n">sort_items</span>
<span class="nd">@api</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;/items&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="o">*</span><span class="p">,</span>
<span class="n">status</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="kc">None</span><span class="p">),</span>
<span class="n">sort</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="s2">&quot;name&quot;</span><span class="p">),</span>
<span class="n">page</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">ge</span><span class="o">=</span><span class="mi">1</span><span class="p">),</span>
<span class="n">size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">Query</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="n">ge</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">le</span><span class="o">=</span><span class="mi">100</span><span class="p">))</span> <span class="o">-&gt;</span> <span class="n">Page</span><span class="p">[</span><span class="n">Item</span><span class="p">]:</span>
<span class="n">rows</span> <span class="o">=</span> <span class="n">filter_items</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">all</span><span class="p">(),</span> <span class="p">{</span><span class="s2">&quot;status&quot;</span><span class="p">:</span> <span class="n">status</span><span class="p">})</span>
<span class="n">rows</span> <span class="o">=</span> <span class="n">sort_items</span><span class="p">(</span><span class="n">rows</span><span class="p">,</span> <span class="n">sort</span><span class="p">,</span> <span class="n">allowed</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;name&quot;</span><span class="p">,</span> <span class="s2">&quot;created_at&quot;</span><span class="p">})</span>
<span class="k">return</span> <span class="n">paginate</span><span class="p">(</span><span class="n">rows</span><span class="p">,</span> <span class="n">page</span><span class="o">=</span><span class="n">page</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="n">size</span><span class="p">)</span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">filter_items</span></code> applies <code class="docutils literal notranslate"><span class="pre">field</span> <span class="pre">==</span> <span class="pre">value</span></code> equality and skips entries whose
value is <code class="docutils literal notranslate"><span class="pre">None</span></code> (so optional markers pass straight through). <code class="docutils literal notranslate"><span class="pre">sort_items</span></code>
reads a <code class="docutils literal notranslate"><span class="pre">name,-created</span></code> spec (<code class="docutils literal notranslate"><span class="pre">-</span></code> = descending, multiple keys allowed);
always pass <code class="docutils literal notranslate"><span class="pre">allowed=</span></code> for a client-supplied <code class="docutils literal notranslate"><span class="pre">sort</span></code> so users can’t order by
arbitrary attributes — an out-of-list field returns <code class="docutils literal notranslate"><span class="pre">400</span></code>.</p>
</section>
</section>
<section id="content-negotiation">
<span id="tour-content-negotiation"></span><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
client’s <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/yaml</span></code> → YAML (the legacy <code class="docutils literal notranslate"><span class="pre">application/x-yaml</span></code>
is accepted too)</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/yaml&quot; http://localhost:5042/data
key: value
</pre></div>
</div>
<p>When the <code class="docutils literal notranslate"><span class="pre">Accept</span></code> header ranks several supported formats, Responder
serves the one the client prefers — the highest q-value wins, per
RFC 9110 §12.5.1 — rather than the first format registered:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ curl -H &quot;Accept: application/yaml;q=0.9, application/json;q=0.1&quot; \
http://localhost:5042/data
key: value
</pre></div>
</div>
<p>Without an <code class="docutils literal notranslate"><span class="pre">Accept</span></code> header, or when everything ties (e.g. <code class="docutils literal notranslate"><span class="pre">*/*</span></code>),
JSON remains the default. To apply the same ranking yourself — say, to
choose between representations you render by hand — use
<code class="docutils literal notranslate"><span class="pre">req.preferred_media_type()</span></code>, which returns the candidate the client
prefers (ties keep your order) or <code class="docutils literal notranslate"><span class="pre">None</span></code> when it accepts none of them:</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">preferred</span> <span class="o">=</span> <span class="n">req</span><span class="o">.</span><span class="n">preferred_media_type</span><span class="p">([</span><span class="s2">&quot;application/json&quot;</span><span class="p">,</span> <span class="s2">&quot;text/csv&quot;</span><span class="p">])</span>
<span class="k">if</span> <span class="n">preferred</span> <span class="o">==</span> <span class="s2">&quot;text/csv&quot;</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">render_csv</span><span class="p">()</span>
<span class="n">resp</span><span class="o">.</span><span class="n">mimetype</span> <span class="o">=</span> <span class="s2">&quot;text/csv&quot;</span>
<span class="k">else</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">render_dict</span><span class="p">()</span>
</pre></div>
</div>
</section>
<section id="faster-json-with-orjson">
<h2>Faster JSON with orjson<a class="headerlink" href="#faster-json-with-orjson" title="Link to this heading"></a></h2>
<p>The recommended Responder installation includes
<a class="reference external" href="https://github.com/ijl/orjson">orjson</a>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ pip install &quot;responder[orjson]&quot;
</pre></div>
</div>
<p>Responder detects it automatically and uses it to encode JSON responses —
typically 3-10x faster than the standard library, which matters for large
payloads that would otherwise serialize on the event loop. No code changes
are needed: <code class="docutils literal notranslate"><span class="pre">resp.media</span></code> and custom <code class="docutils literal notranslate"><span class="pre">API(encoder=...)</span></code> hooks keep working
exactly as before. Request decoding remains on the standard library to
preserve arbitrary-precision integers and compatibility with <code class="docutils literal notranslate"><span class="pre">NaN</span></code> and
<code class="docutils literal notranslate"><span class="pre">Infinity</span></code>. The legacy <code class="docutils literal notranslate"><span class="pre">API(json_ensure_ascii=True)</span></code> mode also stays on
the standard library, since orjson emits UTF-8 only.</p>
<p>Free-threaded CPython builds (<code class="docutils literal notranslate"><span class="pre">3.14t</span></code>/<code class="docutils literal notranslate"><span class="pre">3.15t</span></code>) can install the base
<code class="docutils literal notranslate"><span class="pre">responder</span></code> package. If orjson is unavailable, Responder transparently
falls back to the standard-library encoder.</p>
<p>Output is byte-identical to the stdlib encoder except for whitespace
(orjson emits compact <code class="docutils literal notranslate"><span class="pre">,</span></code>/<code class="docutils literal notranslate"><span class="pre">:</span></code> separators) and one corner: float
<code class="docutils literal notranslate"><span class="pre">nan</span></code>/<code class="docutils literal notranslate"><span class="pre">inf</span></code> values serialize as <code class="docutils literal notranslate"><span class="pre">null</span></code> instead of the non-standard
<code class="docutils literal notranslate"><span class="pre">NaN</span></code>/<code class="docutils literal notranslate"><span class="pre">Infinity</span></code> literals. Payloads orjson cannot handle (such as
integers beyond 64 bits) automatically fall back to the standard library,
so nothing that serialized before stops working.</p>
</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
that’s more compact and faster to parse than JSON. It’s 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>v9.2.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><h3>Navigation</h3>
<p class="caption" role="heading"><span class="caption-text">User Guide</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quick Start</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Feature Tour</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#method-filtering">Method Filtering</a></li>
<li class="toctree-l2"><a class="reference internal" href="#returning-values">Returning Values</a></li>
<li class="toctree-l2"><a class="reference internal" href="#class-based-views">Class-Based Views</a></li>
<li class="toctree-l2"><a class="reference internal" href="#lifespan-events">Lifespan Events</a></li>
<li class="toctree-l2"><a class="reference internal" href="#dependency-injection">Dependency Injection</a></li>
<li class="toctree-l2"><a class="reference internal" href="#serving-files">Serving Files</a></li>
<li class="toctree-l2"><a class="reference internal" href="#conditional-requests">Conditional Requests</a></li>
<li class="toctree-l2"><a class="reference internal" href="#after-response-tasks">After-Response Tasks</a></li>
<li class="toctree-l2"><a class="reference internal" href="#custom-error-handling">Custom Error Handling</a></li>
<li class="toctree-l2"><a class="reference internal" href="#before-request-hooks">Before-Request Hooks</a></li>
<li class="toctree-l2"><a class="reference internal" href="#after-request-hooks">After-Request Hooks</a></li>
<li class="toctree-l2"><a class="reference internal" href="#websocket-support">WebSocket Support</a></li>
<li class="toctree-l2"><a class="reference internal" href="#server-sent-events-sse">Server-Sent Events (SSE)</a></li>
<li class="toctree-l2"><a class="reference internal" href="#graphql">GraphQL</a></li>
<li class="toctree-l2"><a class="reference internal" href="#openapi-documentation">OpenAPI Documentation</a></li>
<li class="toctree-l2"><a class="reference internal" href="#route-groups">Route Groups</a></li>
<li class="toctree-l2"><a class="reference internal" href="#mounting-other-apps">Mounting Other Apps</a></li>
<li class="toctree-l2"><a class="reference internal" href="#cookies">Cookies</a></li>
<li class="toctree-l2"><a class="reference internal" href="#cookie-based-sessions">Cookie-Based Sessions</a></li>
<li class="toctree-l2"><a class="reference internal" href="#static-files">Static Files</a></li>
<li class="toctree-l2"><a class="reference internal" href="#csrf-protection">CSRF Protection</a></li>
<li class="toctree-l2"><a class="reference internal" href="#cors">CORS</a></li>
<li class="toctree-l2"><a class="reference internal" href="#hsts">HSTS</a></li>
<li class="toctree-l2"><a class="reference internal" href="#security-headers">Security Headers</a></li>
<li class="toctree-l2"><a class="reference internal" href="#trusted-hosts">Trusted Hosts</a></li>
<li class="toctree-l2"><a class="reference internal" href="#authentication">Authentication</a></li>
<li class="toctree-l2"><a class="reference internal" href="#request-id">Request ID</a></li>
<li class="toctree-l2"><a class="reference internal" href="#request-size-limits">Request Size Limits</a></li>
<li class="toctree-l2"><a class="reference internal" href="#request-timeouts">Request Timeouts</a></li>
<li class="toctree-l2"><a class="reference internal" href="#rate-limiting">Rate Limiting</a></li>
<li class="toctree-l2"><a class="reference internal" href="#metrics">Metrics</a></li>
<li class="toctree-l2"><a class="reference internal" href="#health-checks">Health Checks</a></li>
<li class="toctree-l2"><a class="reference internal" href="#structured-logging">Structured Logging</a></li>
<li class="toctree-l2"><a class="reference internal" href="#pydantic-validation">Pydantic Validation</a></li>
<li class="toctree-l2"><a class="reference internal" href="#pagination">Pagination</a></li>
<li class="toctree-l2"><a class="reference internal" href="#content-negotiation">Content Negotiation</a></li>
<li class="toctree-l2"><a class="reference internal" href="#faster-json-with-orjson">Faster JSON with orjson</a></li>
<li class="toctree-l2"><a class="reference internal" href="#messagepack">MessagePack</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="examples.html">Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="routers.html">Composing Apps with Routers</a></li>
<li class="toctree-l1"><a class="reference internal" href="guide-config.html">Configuration</a></li>
<li class="toctree-l1"><a class="reference internal" href="clientgen.html">Generated Clients</a></li>
<li class="toctree-l1"><a class="reference internal" href="deployment.html">Deployment</a></li>
<li class="toctree-l1"><a class="reference internal" href="testing.html">Testing</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">Tutorials</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="tutorial-rest.html">Building a REST API</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-sqlalchemy.html">Using SQLAlchemy</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-auth.html">Authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-websockets.html">WebSocket Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-middleware.html">Writing Middleware</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorial-flask.html">Migrating from Flask</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">Reference</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="api.html">API Reference</a></li>
<li class="toctree-l1"><a class="reference internal" href="cli.html">Command Line Interface</a></li>
<li class="toctree-l1"><a class="reference internal" href="runtime-contracts.html">Runtime Contracts</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">Project</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="changes.html">Changelog</a></li>
<li class="toctree-l1"><a class="reference internal" href="migration-v9.html">Migrating to v9</a></li>
<li class="toctree-l1"><a class="reference internal" href="migration-v8.html">Migrating to v8</a></li>
<li class="toctree-l1"><a class="reference internal" href="migration-v7.html">Migrating to v7</a></li>
<li class="toctree-l1"><a class="reference internal" href="migration-v6.html">Migrating to v6</a></li>
<li class="toctree-l1"><a class="reference internal" href="migration-v5.html">Migrating to v5</a></li>
<li class="toctree-l1"><a class="reference internal" href="sandbox.html">Sandbox</a></li>
<li class="toctree-l1"><a class="reference internal" href="backlog.html">Backlog</a></li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="index.html">Documentation overview</a><ul>
<li>Previous: <a href="quickstart.html" title="previous chapter">Quick Start</a></li>
<li>Next: <a href="examples.html" title="next chapter">Examples</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>
<a href="https://github.com/kennethreitz/responder" class="github">
<img src="_static/forkme_right_darkblue.png" alt="Fork me on GitHub" class="github"/>
</a>
</body>
</html>