Files
responder/tutorial-rest.html
2026-07-14 04:14:56 +00:00

398 lines
35 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>Building a REST API &#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="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">&quot;Book Catalog&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="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=&quot;3.0.2&quot;</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=&quot;/docs&quot;</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">&quot;&quot;&quot;What the client sends when creating a book.&quot;&quot;&quot;</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">&quot;&quot;&quot;What the API returns.&quot;&quot;&quot;</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">&quot;/books&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_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">-&gt;</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">&quot;author&quot;</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">-&gt;</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">&quot;/books&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="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">-&gt;</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">&quot;id&quot;</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">-&gt;</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">&quot;/books/{book_id:int}&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">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">-&gt;</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">&quot;Book </span><span class="si">{</span><span class="n">book_id</span><span class="si">}</span><span class="s2"> not found&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="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">&quot;type&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;https://example.com/problems/</span><span class="si">{</span><span class="n">payload</span><span class="p">[</span><span class="s1">&#39;status&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="n">payload</span><span class="p">[</span><span class="s2">&quot;instance&quot;</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">&quot;/books/{book_id:int}&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="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">-&gt;</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">&quot;Book </span><span class="si">{</span><span class="n">book_id</span><span class="si">}</span><span class="s2"> not found&quot;</span><span class="p">)</span>
<span class="n">book</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;id&quot;</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">&quot;/books/{book_id:int}&quot;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;DELETE&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">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">&quot;Book </span><span class="si">{</span><span class="n">book_id</span><span class="si">}</span><span class="s2"> not found&quot;</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">&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>
</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">&quot;__main__&quot;</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 &quot;Content-Type: application/json&quot; \
-d &#39;{&quot;title&quot;: &quot;Dune&quot;, &quot;author&quot;: &quot;Frank Herbert&quot;, &quot;year&quot;: 1965}&#39;
# List all books
$ curl http://localhost:5042/books
# Filter by author
$ curl &quot;http://localhost:5042/books?author=Frank+Herbert&quot;
# Get a specific book
$ curl http://localhost:5042/books/1
# Update a book
$ curl -X PUT http://localhost:5042/books/1 \
-H &quot;Content-Type: application/json&quot; \
-d &#39;{&quot;title&quot;: &quot;Dune&quot;, &quot;author&quot;: &quot;Frank Herbert&quot;, &quot;year&quot;: 1965, &quot;isbn&quot;: &quot;978-0441172719&quot;}&#39;
# 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">
&#169;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>