mirror of
https://github.com/kennethreitz/responder.git
synced 2026-10-24 05:59:12 +00:00
398 lines
35 KiB
HTML
398 lines
35 KiB
HTML
<!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>Building a REST API — 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="Using SQLAlchemy" href="tutorial-sqlalchemy.html" />
|
||
<link rel="prev" title="Testing" href="testing.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="building-a-rest-api">
|
||
<h1>Building a REST API<a class="headerlink" href="#building-a-rest-api" title="Link to this heading">¶</a></h1>
|
||
<p>This tutorial walks you through building a complete REST API from scratch.
|
||
By the end, you’ll have a working API with CRUD operations, request
|
||
validation, error handling, and interactive documentation.</p>
|
||
<p>We’ll build a simple book catalog — a service that lets you create, read,
|
||
update, and delete books.</p>
|
||
<section id="project-setup">
|
||
<h2>Project Setup<a class="headerlink" href="#project-setup" title="Link to this heading">¶</a></h2>
|
||
<p>Create a new file called <code class="docutils literal notranslate"><span class="pre">app.py</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">responder</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">"Book Catalog"</span><span class="p">,</span>
|
||
<span class="n">version</span><span class="o">=</span><span class="s2">"1.0"</span><span class="p">,</span>
|
||
<span class="n">openapi</span><span class="o">=</span><span class="s2">"3.0.2"</span><span class="p">,</span>
|
||
<span class="n">docs_route</span><span class="o">=</span><span class="s2">"/docs"</span><span class="p">,</span>
|
||
<span class="n">sessions</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We enable OpenAPI documentation from the start. <code class="docutils literal notranslate"><span class="pre">openapi="3.0.2"</span></code> serves the
|
||
machine-readable schema at <code class="docutils literal notranslate"><span class="pre">/schema.yml</span></code>, and <code class="docutils literal notranslate"><span class="pre">docs_route="/docs"</span></code> mounts
|
||
interactive Swagger UI on top of it — visit <code class="docutils literal notranslate"><span class="pre">/docs</span></code> at any point to explore
|
||
and exercise your API from the browser.</p>
|
||
<p><code class="docutils literal notranslate"><span class="pre">sessions=False</span></code> tells Responder this service is stateless. Without it,
|
||
Responder’s secure-by-default sessions would mint a throwaway signing key on
|
||
startup and warn you about it; since we never touch <code class="docutils literal notranslate"><span class="pre">req.session</span></code>, we simply
|
||
switch them off. See <a class="reference internal" href="guide-config.html"><span class="doc">Configuration</span></a> for sessions and secret keys.</p>
|
||
</section>
|
||
<section id="define-your-models">
|
||
<span id="tutorial-rest-models"></span><h2>Define Your Models<a class="headerlink" href="#define-your-models" title="Link to this heading">¶</a></h2>
|
||
<p>We’ll use <a class="reference external" href="https://docs.pydantic.dev/">Pydantic</a> to define our data
|
||
models. Pydantic models serve double duty — they validate incoming data
|
||
<em>and</em> generate OpenAPI schemas automatically:</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">BookIn</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""What the client sends when creating a book."""</span>
|
||
<span class="n">title</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">author</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">year</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">isbn</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
|
||
<span class="k">class</span><span class="w"> </span><span class="nc">Book</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
|
||
<span class="w"> </span><span class="sd">"""What the API returns."""</span>
|
||
<span class="nb">id</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">title</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">author</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="n">year</span><span class="p">:</span> <span class="nb">int</span>
|
||
<span class="n">isbn</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">BookIn</span></code> is the <em>input</em> model — it doesn’t have an <code class="docutils literal notranslate"><span class="pre">id</span></code> because the
|
||
server assigns that. <code class="docutils literal notranslate"><span class="pre">Book</span></code> is the <em>output</em> model — it includes
|
||
everything. This input/output separation is a common REST API pattern.</p>
|
||
</section>
|
||
<section id="in-memory-storage">
|
||
<h2>In-Memory Storage<a class="headerlink" href="#in-memory-storage" title="Link to this heading">¶</a></h2>
|
||
<p>For this tutorial, we’ll store books in a simple dict. In a real
|
||
application, you’d use a database (see <a class="reference internal" href="tutorial-sqlalchemy.html"><span class="doc">Using SQLAlchemy</span></a>):</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">books_db</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">dict</span><span class="p">]</span> <span class="o">=</span> <span class="p">{}</span>
|
||
<span class="n">next_id</span> <span class="o">=</span> <span class="mi">1</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="list-all-books">
|
||
<h2>List All Books<a class="headerlink" href="#list-all-books" title="Link to this heading">¶</a></h2>
|
||
<p>The first endpoint lists every book. It’s a <code class="docutils literal notranslate"><span class="pre">GET</span></code> request to <code class="docutils literal notranslate"><span class="pre">/books</span></code>,
|
||
with an optional <code class="docutils literal notranslate"><span class="pre">?author=</span></code> filter wired up as a typed query parameter:</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="nd">@api</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s2">"/books"</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">"GET"</span><span class="p">])</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">list_books</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">author</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</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="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="n">Book</span><span class="p">]:</span>
|
||
<span class="n">books</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">books_db</span><span class="o">.</span><span class="n">values</span><span class="p">())</span>
|
||
<span class="k">if</span> <span class="n">author</span><span class="p">:</span>
|
||
<span class="n">books</span> <span class="o">=</span> <span class="p">[</span><span class="n">b</span> <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">books</span> <span class="k">if</span> <span class="n">b</span><span class="p">[</span><span class="s2">"author"</span><span class="p">]</span> <span class="o">==</span> <span class="n">author</span><span class="p">]</span>
|
||
<span class="k">return</span> <span class="n">books</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">Query(None)</span></code> declares an <em>optional</em> query parameter — use <code class="docutils literal notranslate"><span class="pre">Query(...)</span></code> to
|
||
make one required, in which case a missing or mis-typed value returns a <code class="docutils literal notranslate"><span class="pre">422</span></code>
|
||
automatically. Responder reads <code class="docutils literal notranslate"><span class="pre">?author=...</span></code> from the query string, coerces
|
||
it to the annotated type, and passes it to your handler as a keyword argument.
|
||
The <code class="docutils literal notranslate"><span class="pre">-></span> <span class="pre">list[Book]</span></code> annotation validates and serializes the response, places
|
||
the same shape in OpenAPI, and types generated clients.</p>
|
||
<p>In REST API design, <code class="docutils literal notranslate"><span class="pre">GET</span></code> requests should never modify data. They’re <em>safe</em>
|
||
and <em>idempotent</em> — calling them many times has the same effect as calling them
|
||
once.</p>
|
||
</section>
|
||
<section id="create-a-book">
|
||
<h2>Create a Book<a class="headerlink" href="#create-a-book" title="Link to this heading">¶</a></h2>
|
||
<p>To create a book, the client sends a <code class="docutils literal notranslate"><span class="pre">POST</span></code> request with a JSON body.
|
||
Annotate a parameter with your input model and Responder validates the body
|
||
for you, handing the handler a parsed <code class="docutils literal notranslate"><span class="pre">BookIn</span></code> — no manual <code class="docutils literal notranslate"><span class="pre">req.media()</span></code>,
|
||
and an automatic <code class="docutils literal notranslate"><span class="pre">422</span></code> with error details when the data is bad:</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">"/books"</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">"POST"</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="n">status_code</span><span class="o">=</span><span class="mi">201</span>
|
||
<span class="p">)</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">create_book</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">data</span><span class="p">:</span> <span class="n">BookIn</span><span class="p">)</span> <span class="o">-></span> <span class="n">Book</span><span class="p">:</span>
|
||
<span class="k">global</span> <span class="n">next_id</span>
|
||
|
||
<span class="n">book</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"id"</span><span class="p">:</span> <span class="n">next_id</span><span class="p">,</span> <span class="o">**</span><span class="n">data</span><span class="o">.</span><span class="n">model_dump</span><span class="p">()}</span>
|
||
<span class="n">books_db</span><span class="p">[</span><span class="n">next_id</span><span class="p">]</span> <span class="o">=</span> <span class="n">book</span>
|
||
<span class="n">next_id</span> <span class="o">+=</span> <span class="mi">1</span>
|
||
|
||
<span class="k">return</span> <span class="n">book</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">data</span></code> arrives as a validated <code class="docutils literal notranslate"><span class="pre">BookIn</span></code> instance, so we call
|
||
<code class="docutils literal notranslate"><span class="pre">data.model_dump()</span></code> to turn it back into a plain dict. Returning
|
||
<code class="docutils literal notranslate"><span class="pre">status_code=201</span></code> declares the success status — <code class="docutils literal notranslate"><span class="pre">201</span> <span class="pre">Created</span></code> tells the
|
||
client a new resource was created (more informative than a generic <code class="docutils literal notranslate"><span class="pre">200</span> <span class="pre">OK</span></code>).
|
||
The <code class="docutils literal notranslate"><span class="pre">-></span> <span class="pre">Book</span></code> return annotation runs the outgoing payload through the <code class="docutils literal notranslate"><span class="pre">Book</span></code>
|
||
model, coercing types and stripping any field the model doesn’t declare.</p>
|
||
</section>
|
||
<section id="get-a-single-book">
|
||
<h2>Get a Single Book<a class="headerlink" href="#get-a-single-book" title="Link to this heading">¶</a></h2>
|
||
<p>Retrieve a book by its ID. The <code class="docutils literal notranslate"><span class="pre">{book_id:int}</span></code> converter ensures only
|
||
integer IDs match, so <code class="docutils literal notranslate"><span class="pre">/books/abc</span></code> 404s before your handler even runs:</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">"/books/{book_id:int}"</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">"GET"</span><span class="p">])</span>
|
||
<span class="k">def</span><span class="w"> </span><span class="nf">get_book</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">book_id</span><span class="p">)</span> <span class="o">-></span> <span class="n">Book</span><span class="p">:</span>
|
||
<span class="k">if</span> <span class="n">book_id</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">books_db</span><span class="p">:</span>
|
||
<span class="n">responder</span><span class="o">.</span><span class="n">abort</span><span class="p">(</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="sa">f</span><span class="s2">"Book </span><span class="si">{</span><span class="n">book_id</span><span class="si">}</span><span class="s2"> not found"</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">books_db</span><span class="p">[</span><span class="n">book_id</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">responder.abort()</span></code> raises a proper HTTP error from anywhere in your code —
|
||
no Starlette imports, no juggling <code class="docutils literal notranslate"><span class="pre">resp.status_code</span></code> by hand. Responder
|
||
renders framework-generated errors as <code class="docutils literal notranslate"><span class="pre">application/problem+json</span></code> by default.
|
||
Pass <code class="docutils literal notranslate"><span class="pre">problem_details=False</span></code> to <code class="docutils literal notranslate"><span class="pre">API(...)</span></code> to keep the legacy JSON/plain-text
|
||
error negotiation.</p>
|
||
<p>You can enrich every framework-generated problem payload with
|
||
<code class="docutils literal notranslate"><span class="pre">problem_handler=</span></code>. The handler receives <code class="docutils literal notranslate"><span class="pre">(payload,</span> <span class="pre">request,</span> <span class="pre">exc)</span></code> and may
|
||
mutate the payload in place or return a replacement dict:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">problem_handler</span><span class="p">(</span><span class="n">payload</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">exc</span><span class="p">):</span>
|
||
<span class="n">payload</span><span class="p">[</span><span class="s2">"type"</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"https://example.com/problems/</span><span class="si">{</span><span class="n">payload</span><span class="p">[</span><span class="s1">'status'</span><span class="p">]</span><span class="si">}</span><span class="s2">"</span>
|
||
<span class="n">payload</span><span class="p">[</span><span class="s2">"instance"</span><span class="p">]</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">url</span><span class="o">.</span><span class="n">path</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">problem_handler</span><span class="o">=</span><span class="n">problem_handler</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>When request ID middleware or structured logging is enabled, problem payloads
|
||
also include <code class="docutils literal notranslate"><span class="pre">request_id</span></code>.</p>
|
||
</section>
|
||
<section id="update-a-book">
|
||
<h2>Update a Book<a class="headerlink" href="#update-a-book" title="Link to this heading">¶</a></h2>
|
||
<p><code class="docutils literal notranslate"><span class="pre">PUT</span></code> replaces a resource entirely, so the client sends every field. The same
|
||
body injection and <code class="docutils literal notranslate"><span class="pre">abort</span></code> apply here:</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">"/books/{book_id:int}"</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">"PUT"</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">def</span><span class="w"> </span><span class="nf">update_book</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">book_id</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="n">BookIn</span><span class="p">)</span> <span class="o">-></span> <span class="n">Book</span><span class="p">:</span>
|
||
<span class="k">if</span> <span class="n">book_id</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">books_db</span><span class="p">:</span>
|
||
<span class="n">responder</span><span class="o">.</span><span class="n">abort</span><span class="p">(</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="sa">f</span><span class="s2">"Book </span><span class="si">{</span><span class="n">book_id</span><span class="si">}</span><span class="s2"> not found"</span><span class="p">)</span>
|
||
|
||
<span class="n">book</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"id"</span><span class="p">:</span> <span class="n">book_id</span><span class="p">,</span> <span class="o">**</span><span class="n">data</span><span class="o">.</span><span class="n">model_dump</span><span class="p">()}</span>
|
||
<span class="n">books_db</span><span class="p">[</span><span class="n">book_id</span><span class="p">]</span> <span class="o">=</span> <span class="n">book</span>
|
||
<span class="k">return</span> <span class="n">book</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">book_id</span></code> comes from the URL and <code class="docutils literal notranslate"><span class="pre">data</span></code> from the request body — Responder
|
||
injects both as keyword arguments.</p>
|
||
</section>
|
||
<section id="delete-a-book">
|
||
<h2>Delete a Book<a class="headerlink" href="#delete-a-book" title="Link to this heading">¶</a></h2>
|
||
<p><code class="docutils literal notranslate"><span class="pre">DELETE</span></code> removes a resource. The convention is to return <code class="docutils literal notranslate"><span class="pre">204</span> <span class="pre">No</span> <span class="pre">Content</span></code>
|
||
with an empty body on success:</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">"/books/{book_id:int}"</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">"DELETE"</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">def</span><span class="w"> </span><span class="nf">delete_book</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">book_id</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="n">book_id</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">books_db</span><span class="p">:</span>
|
||
<span class="n">responder</span><span class="o">.</span><span class="n">abort</span><span class="p">(</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="sa">f</span><span class="s2">"Book </span><span class="si">{</span><span class="n">book_id</span><span class="si">}</span><span class="s2"> not found"</span><span class="p">)</span>
|
||
|
||
<span class="k">del</span> <span class="n">books_db</span><span class="p">[</span><span class="n">book_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>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="error-handling">
|
||
<h2>Error Handling<a class="headerlink" href="#error-handling" title="Link to this heading">¶</a></h2>
|
||
<p><code class="docutils literal notranslate"><span class="pre">responder.abort()</span></code> handles HTTP errors, but you can also map your own
|
||
exception types to clean responses. Here, any <code class="docutils literal notranslate"><span class="pre">ValueError</span></code> that escapes a
|
||
handler becomes a tidy <code class="docutils literal notranslate"><span class="pre">400</span></code> instead of a generic <code class="docutils literal notranslate"><span class="pre">500</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">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">"error"</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>
|
||
</section>
|
||
<section id="run-it">
|
||
<h2>Run It<a class="headerlink" href="#run-it" title="Link to this heading">¶</a></h2>
|
||
<p>Add the standard entry point at the bottom of your file:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
|
||
<span class="n">api</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Start the server:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>$ python app.py
|
||
</pre></div>
|
||
</div>
|
||
<p>Visit <code class="docutils literal notranslate"><span class="pre">http://localhost:5042/docs</span></code> to see your interactive API
|
||
documentation. You can test every endpoint directly from the browser.</p>
|
||
</section>
|
||
<section id="try-it-out">
|
||
<h2>Try It Out<a class="headerlink" href="#try-it-out" title="Link to this heading">¶</a></h2>
|
||
<p>Using <code class="docutils literal notranslate"><span class="pre">curl</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span># Create a book
|
||
$ curl -X POST http://localhost:5042/books \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"title": "Dune", "author": "Frank Herbert", "year": 1965}'
|
||
|
||
# List all books
|
||
$ curl http://localhost:5042/books
|
||
|
||
# Filter by author
|
||
$ curl "http://localhost:5042/books?author=Frank+Herbert"
|
||
|
||
# Get a specific book
|
||
$ curl http://localhost:5042/books/1
|
||
|
||
# Update a book
|
||
$ curl -X PUT http://localhost:5042/books/1 \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"title": "Dune", "author": "Frank Herbert", "year": 1965, "isbn": "978-0441172719"}'
|
||
|
||
# Delete a book
|
||
$ curl -X DELETE http://localhost:5042/books/1
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="what-s-next">
|
||
<h2>What’s Next<a class="headerlink" href="#what-s-next" title="Link to this heading">¶</a></h2>
|
||
<p>This tutorial used in-memory storage. For a real application, you’ll want a
|
||
database. See <a class="reference internal" href="tutorial-sqlalchemy.html"><span class="doc">Using SQLAlchemy</span></a> for integrating SQLAlchemy with
|
||
Responder using the lifespan pattern and per-request dependency injection.</p>
|
||
<p>For the full picture on typed parameters, Pydantic validation, dependency
|
||
injection, and OpenAPI generation, see the <a class="reference internal" href="tour.html"><span class="doc">Feature Tour</span></a>.</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>
|
||
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quick Start</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="tour.html">Feature Tour</a></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 class="current">
|
||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Building a REST API</a><ul>
|
||
<li class="toctree-l2"><a class="reference internal" href="#project-setup">Project Setup</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#define-your-models">Define Your Models</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#in-memory-storage">In-Memory Storage</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#list-all-books">List All Books</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#create-a-book">Create a Book</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#get-a-single-book">Get a Single Book</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#update-a-book">Update a Book</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#delete-a-book">Delete a Book</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#error-handling">Error Handling</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#run-it">Run It</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#try-it-out">Try It Out</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#what-s-next">What’s Next</a></li>
|
||
</ul>
|
||
</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="testing.html" title="previous chapter">Testing</a></li>
|
||
<li>Next: <a href="tutorial-sqlalchemy.html" title="next chapter">Using SQLAlchemy</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">
|
||
©2018-2026, Kenneth Reitz.
|
||
|
||
|
|
||
<a href="_sources/tutorial-rest.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> |