<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[IO.IO: Tips &amp; Tutorial For The Modern Web Developer]]></title><description><![CDATA[Hi there ! Having a hard time keeping up with the ever changing world of web development ? You've come to the right place ! Come with me on a journey of exploration of modern web technologies.]]></description><link>https://dev.indooroutdoor.io</link><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 19:11:12 GMT</lastBuildDate><atom:link href="https://dev.indooroutdoor.io/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Asyncio Demystified: Rebuilding it From Scratch One Yield at a Time]]></title><description><![CDATA[I don't know about you, but each time I await something in Python, I get a tiny itch in the back of my brain.
Not because I don’t know what it does or how to use it, that part’s fine. I write my async defs, slap some await in there, and it just works...]]></description><link>https://dev.indooroutdoor.io/asyncio-demystified-rebuilding-it-from-scratch-one-yield-at-a-time</link><guid isPermaLink="true">https://dev.indooroutdoor.io/asyncio-demystified-rebuilding-it-from-scratch-one-yield-at-a-time</guid><category><![CDATA[Python]]></category><category><![CDATA[asyncio]]></category><category><![CDATA[asynchronous]]></category><category><![CDATA[Event Loop]]></category><category><![CDATA[Deep Dive]]></category><dc:creator><![CDATA[Jb Rocher]]></dc:creator><pubDate>Mon, 05 May 2025 15:17:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746276915541/edd82058-5cb5-4ef2-8d5f-7c3a616d6014.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I don't know about you, but each time I await something in Python, I get a tiny itch in the back of my brain.</p>
<p>Not because I don’t know what it does or how to use it, that part’s fine. I write my async defs, slap some await in there, and it just works™️. No, what's bothering me is: why does it work?</p>
<p>If we’re not blocking the main process... who’s doing the actual work I’m awaiting? And more importantly, how does the result get back into my Python function?</p>
<p>Take a typical FastAPI route. Maybe we’re calling an external API:</p>
<pre><code class="lang-python">
<span class="hljs-keyword">import</span> httpx
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> APIRouter

router = APIRouter()

<span class="hljs-meta">@router.get("/user/{id}")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_user</span>(<span class="hljs-params">id: str</span>):</span>
    <span class="hljs-keyword">async</span> <span class="hljs-keyword">with</span> httpx.AsyncClient() <span class="hljs-keyword">as</span> client:
        response = <span class="hljs-keyword">await</span> client.get(<span class="hljs-string">f"https://api.example.com/users/<span class="hljs-subst">{id}</span>"</span>)
    <span class="hljs-keyword">return</span> response.json()
</code></pre>
<p>The request comes in, the interpreter hits await client.get(...), and somehow... the server keeps handling other requests while the HTTP call is doing its thing.</p>
<p>Doing its thing <em>where</em>, exactly?</p>
<p>And once it's done, who taps the interpreter on the shoulder to say: "Hey, here’s your response. You can resume now." ?</p>
<p>It all feels a bit too magical, and I <em>hate</em> magic. That's why I always end up playing a sneaky archer in Skyrim.</p>
<p>This post is about removing the magic and understanding how it all works under the hood. We'll build our own version of asyncio step by step , from a simple generator all the way to running a fully working echo server using only our custom event loop.</p>
<p>Here is what the end result will look like :</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> socket <span class="hljs-keyword">import</span> SO_REUSEADDR, SOL_SOCKET, socket
<span class="hljs-keyword">from</span> scheduler.future <span class="hljs-keyword">import</span> AcceptSocket, ReadSocket, Sleep
<span class="hljs-keyword">from</span> scheduler.scheduler <span class="hljs-keyword">import</span> Scheduler


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">read</span>(<span class="hljs-params">conn</span>):</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> ReadSocket(conn)


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">accept</span>(<span class="hljs-params">sock</span>):</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> AcceptSocket(sock)


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">echo</span>(<span class="hljs-params">sock</span>):</span>
    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
        data = <span class="hljs-keyword">await</span> read(sock)
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> data:
            sock.close()

        print(<span class="hljs-string">f"received <span class="hljs-subst">{data}</span>"</span>)
        <span class="hljs-comment"># assume non-blocking</span>
        sock.send(data)


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">echo_server</span>(<span class="hljs-params">scheduler: Scheduler, port: int</span>):</span>
    print(<span class="hljs-string">"creating socket..."</span>)
    sock = socket()
    sock.bind((<span class="hljs-string">"localhost"</span>, port))

    sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, <span class="hljs-number">1</span>)
    sock.listen(<span class="hljs-number">100</span>)
    sock.setblocking(<span class="hljs-literal">False</span>)
    print(<span class="hljs-string">"socket created waiting for connection"</span>)
    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
        conn = <span class="hljs-keyword">await</span> accept(sock)
        <span class="hljs-comment"># Schedule new concurrent connection</span>
        scheduler.create_task(echo(conn))


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    scheduler = Scheduler()
    scheduler.create_task(echo_server(scheduler, <span class="hljs-number">1235</span>))
    scheduler.run_forever()
</code></pre>
<p>To do that, we’ll need three things:</p>
<ul>
<li><p>A way to pause a function and resume where we left off.</p>
</li>
<li><p>A way to switch between those paused/resumable functions.</p>
</li>
<li><p>A way to start the work somewhere else, and get notified when it’s done.</p>
</li>
</ul>
<p>Sound familiar? That first point should ring a bell: <em>generators</em>! Generators let us pause in the middle of a function and resume it later. And fun fact: that’s very close to how asyncio actually works under the hood.</p>
<p>(Want to skip ahead? Check out the <a target="_blank" href="https://github.com/jbrocher/asyncio-model">full repo here</a> with all the code we'll write in this post.)</p>
<h2 id="heading-primer-on-generators-and-coroutines">Primer on Generators and Coroutines</h2>
<p>First a small primer on generators, and their twin coroutines.</p>
<p>In python, generators are created (among other ways) using <code>yield</code>. A yield statement pauses the callee and returns something to the caller. This allows to do cool stuff like maintaining state across multiple function executions.</p>
<p>Here's an example generator that when iterated over return the Fibonacci sequence:</p>
<pre><code class="lang-python">
<span class="hljs-keyword">from</span> time <span class="hljs-keyword">import</span> sleep


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">fibonnaci_generator</span>():</span>
    n_1 = <span class="hljs-number">0</span>
    n_2 = <span class="hljs-number">1</span>
    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
        n = n_1 + n_2
        <span class="hljs-keyword">yield</span> n

        n_2 = n_1
        n_1 = n


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    generator = fibonnaci_generator()
    <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> generator:
        print(x)
        sleep(<span class="hljs-number">1</span>)
</code></pre>
<p><img src="https://res.cloudinary.com/dllztfozz/image/upload/c_crop,w_500,h_300,g_north_west/v1746179004/blog/kata-asyncio/jfdt9snkapccjtah3qlk.gif" alt="fibo.gif" class="image--center mx-auto" /></p>
<p>Calling fibonacci_generator() gives us a generator object. Iterating over it calls yield repeatedly, pausing and resuming the function between each number.</p>
<p>But yield has a hidden superpower: it doesn’t just let a generator produce values, it also allows it to receive values using <code>my_generator.send(value)</code>. This turns the generator into a data consumer, not just a producer.</p>
<p>A generator that can receive data like this is often referred to as a <a target="_blank" href="https://en.wikipedia.org/wiki/Coroutine">coroutine</a>. Why <strong>co</strong>routine? Because unlike regular subroutines (which run top-to-bottom and return once) they can pause, yield control, and be resumed later with external data, possibly by some external scheduler. That makes them perfect for cooperative multitasking, where functions can run concurrently by explicitly handing control back and forth, and sending values to each other, rather than stacking up on the call stack like traditional functions.</p>
<blockquote>
<p>⚠️ Quick note on terminology:</p>
<p>In this post, I’m using the term <strong>coroutine</strong> in its broader, general sense. In Python, this includes generator functions that use <code>yield</code> and <code>.send()</code> to cooperate with a scheduler.</p>
<p>These generator-based coroutines were the first way to write asynchronous code in Python. Since version 3.5, Python introduced <strong>native coroutines</strong> using the <code>async def</code> and <code>await</code> keywords. Native coroutines are more ergonomic, but conceptually, they do the same thing: pause, wait for something, then resume.</p>
<p>We’ll explore those native coroutines, and how our library supports them, later in the post.</p>
</blockquote>
<p><code>send</code> will come in handy when we want to send the result of an asynchronous operation back into the paused coroutine.</p>
<p>Let’s see this in action. We’ll write a coroutine that filters even numbers from the Fibonacci sequence (yes, it’s very useful):</p>
<pre><code class="lang-python">
<span class="hljs-keyword">from</span> time <span class="hljs-keyword">import</span> sleep


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">fibonnaci_generator</span>():</span>
    n_1 = <span class="hljs-number">0</span>
    n_2 = <span class="hljs-number">1</span>
    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
        n = n_1 + n_2
        <span class="hljs-keyword">yield</span> n
        sleep(<span class="hljs-number">1</span>)

        n_2 = n_1
        n_1 = n


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">filter_even</span>():</span>
    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
        x = <span class="hljs-keyword">yield</span>
        <span class="hljs-keyword">if</span> x &gt; <span class="hljs-number">200</span>:
            <span class="hljs-keyword">return</span> x
        <span class="hljs-keyword">if</span> x % <span class="hljs-number">2</span> == <span class="hljs-number">0</span>:
            print(x)


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    filter = filter_even()
    generator = fibonnaci_generator()
    filter.send(<span class="hljs-literal">None</span>)
    <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> generator:
        filter.send(x)
</code></pre>
<p><img src="https://res.cloudinary.com/dllztfozz/image/upload/c_crop,w_500,h_300,g_north_west/v1746187915/blog/kata-asyncio/hgbhby99fmav3qjxfukp.gif" alt="filter_even.gif" class="image--center mx-auto" /></p>
<p>Notice how <code>filter_even</code> consumes values generated by <code>fibonnaci_generator</code> ? That's the difference between generator and coroutines. Of course, a generator can do both, but that gets messy quickly, and I won’t get into why here. (You can find a good explanation <a target="_blank" href="https://www.dabeaz.com/coroutines/Coroutines.pdf#page=16">p.16 of A Curious Course on Coroutines and Concurrency</a>)</p>
<p>That's enough theory on coroutines for now. But look, we've already completed step 1: we now have a way to pause and resume execution. Next up: running coroutines concurrently with a scheduler.</p>
<h2 id="heading-building-a-scheduler">Building a Scheduler</h2>
<p>We'll build a Scheduler that well be responsible for, that's right, scheduling our coroutines. First, let's write a <code>Task</code> wrapper to encapsulate all the coroutine stuff:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Generator
<span class="hljs-keyword">import</span> uuid


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Task</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, coro: Generator</span>):</span>
        self.id = uuid.uuid4()
        self._coro = coro
        self.val = <span class="hljs-literal">None</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self._coro.send(self.val)
</code></pre>
<p>the <code>val</code> attribute represents the next value we want to send to the coroutine. We’ll use it to give back some result to the wrapped coroutine later on. After initialization, it’s <code>None</code>. Sending None to a coroutine is equivalent to calling <code>next</code> on it, and will advance to the next <code>yield</code> statement.</p>
<p>Next, we write a simple scheduler to manage the tasks queue:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> collections <span class="hljs-keyword">import</span> deque
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Generator

<span class="hljs-keyword">from</span> scheduler.task <span class="hljs-keyword">import</span> Task


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Scheduler</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self._queue = deque([])

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_task</span>(<span class="hljs-params">self, coro: Generator</span>):</span>
        task = Task(coro)
        self._queue.append(task)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run_forever</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">while</span> self._queue:
            task = self._queue.pop()
            <span class="hljs-keyword">try</span>:
                task.run()
                self._queue.appendleft(task)
            <span class="hljs-keyword">except</span> StopIteration <span class="hljs-keyword">as</span> err:
                print(err.value)
</code></pre>
<p>This is as simple while loop that take the next task in the queue and run it once. Then either:</p>
<ul>
<li><p>We reschedule the task at the back of the queue</p>
</li>
<li><p>StopIteration is raised, meaning the coroutine is over, and the task isn't rescheduled</p>
</li>
</ul>
<blockquote>
<p>💡 <code>StopIteration</code> is raised when a generator (or generator-style coroutine) finishes execution.<br />This happens either:</p>
<ul>
<li><p>When it reaches the end of the function, or</p>
</li>
<li><p>When it hits a <code>return</code> statement (in which case the <code>return</code> value is stored in the exception’s <code>.value</code> attribute)</p>
</li>
</ul>
</blockquote>
<p>Let's see this in action. We'll build two simple coroutines, <code>ping</code> and <code>pong</code>, that print their own name and yield. We can use your brand new <code>Scheduler</code> to run them.</p>
<pre><code class="lang-python">
<span class="hljs-keyword">import</span> time

<span class="hljs-keyword">from</span> scheduler.scheduler <span class="hljs-keyword">import</span> Scheduler


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">ping</span>():</span>
    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
        print(<span class="hljs-string">"ping"</span>)
        time.sleep(<span class="hljs-number">1</span>)
        (<span class="hljs-keyword">yield</span>)


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pong</span>():</span>
    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
        print(<span class="hljs-string">"pong"</span>)
        time.sleep(<span class="hljs-number">1</span>)
        (<span class="hljs-keyword">yield</span>)


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    scheduler = Scheduler()
    scheduler.create_task(ping())
    scheduler.create_task(pong())
    scheduler.run_forever()
</code></pre>
<p><img src="https://res.cloudinary.com/dllztfozz/image/upload/c_crop,w_500,h_300,g_north_west/v1746188103/blog/kata-asyncio/opwtar3s50nhi0hfnjxm.gif" alt="ping_pong.gif" class="image--center mx-auto" /></p>
<p>Great! As you can see, our scheduler alternates between the <code>ping</code> and <code>pong</code> tasks. That’s step 2 done.</p>
<p>However, for now, we still can’t really “await” anything. If we modify <code>ping</code> to use a blocking <code>sleep(30)</code>, it will pause for 30 seconds before the scheduler can switch to <code>pong</code>.</p>
<p>Right now, our scheduler behaves similarly to running both tasks in separate threads, except we are the ones deciding when the switch happens. This model is called <a target="_blank" href="https://en.wikipedia.org/wiki/Cooperative_multitasking#:~:text=Cooperative%20multitasking%2C%20also%20known%20as,running%20process%20to%20another%20process."><strong>cooperative multitasking</strong></a>, but we are still missing our third ingredient: something to delegate the work and be notify when it's done.</p>
<p>This is where the <code>Future</code> class comes into play</p>
<h2 id="heading-adding-a-future">Adding a Future</h2>
<p>A Future is similar to a Promise in TypeScript. It represents a value that’s not available yet, but will be eventually. That’s exactly what we need: we want our coroutine to yield a Future, and then have our scheduler “pause” it until that future is resolved.</p>
<p>Here’s a base Future class we can work with:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Future</span>(<span class="hljs-params">TaskProtocol, metaclass=ABCMeta</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self._done_callbacks = []
        self.is_done = <span class="hljs-literal">False</span>
        self._result = <span class="hljs-literal">None</span>
        self._coro = self._run()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add_done_callback</span>(<span class="hljs-params">self, callback: Callable[[Any], None]</span>):</span>
        self._done_callbacks.append(callback)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run</span>(<span class="hljs-params">self</span>):</span>
        self._coro.send(<span class="hljs-literal">None</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_run</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
            result = self._check_result()
            <span class="hljs-keyword">if</span> result:
                self._done(result)
                <span class="hljs-keyword">return</span> result
            <span class="hljs-keyword">yield</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_done</span>(<span class="hljs-params">self, result: Any</span>):</span>
        self.is_done = <span class="hljs-literal">True</span>
        self._result = result
        <span class="hljs-keyword">for</span> callback <span class="hljs-keyword">in</span> self._done_callbacks:
            callback(self._result)

<span class="hljs-meta">    @abstractmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_check_result</span>(<span class="hljs-params">self</span>) -&gt; Any:</span>
        <span class="hljs-keyword">pass</span>
</code></pre>
<p>A few things to notice here:</p>
<p>The Future has a run method, just like our Task. This is because the Future will replace the task in the queue, having a task-like interface allows us to treat it like a normal task.</p>
<blockquote>
<p>💡 The <code>TaskProtocol</code> is define elsewhere, it simply enforces this shared interface between <code>Future</code> and <code>Task</code></p>
</blockquote>
<p>The Future wraps a coroutine in _run, which polls for the result. When it’s ready, we mark the future as done and trigger any callbacks waiting on it.</p>
<p>When a Task yields a Future, that future replaces the task in the queue, and holds a reference to the task. That’s how we “pause” the coroutine. Once the future is complete, it will call its registered callback to reschedule the waiting task with the result, resuming it. In our case, the scheduler will be responsible for setting this callback.</p>
<pre><code class="lang-python">
<span class="hljs-keyword">from</span> collections <span class="hljs-keyword">import</span> deque
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Generator

<span class="hljs-keyword">from</span> scheduler.future <span class="hljs-keyword">import</span> Future
<span class="hljs-keyword">from</span> scheduler.task <span class="hljs-keyword">import</span> Task
<span class="hljs-keyword">from</span> scheduler.task_protocol <span class="hljs-keyword">import</span> TaskProtocol


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Scheduler</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self._queue = deque([])

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_task</span>(<span class="hljs-params">self, coro: Generator</span>):</span>
        task = Task(coro)
        self._schedule(task)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_schedule</span>(<span class="hljs-params">self, task: TaskProtocol</span>):</span>
        self._queue.appendleft(task)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_continue</span>(<span class="hljs-params">self, task</span>):</span>
        <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">callback</span>(<span class="hljs-params">result</span>):</span>
            task.val = result
            self._schedule(task)

        <span class="hljs-keyword">return</span> callback

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run_forever</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">while</span> self._queue:
            task = self._queue.pop()
            <span class="hljs-keyword">try</span>:
                future = task.run()
                <span class="hljs-keyword">if</span> isinstance(future, Future):
                    future.add_done_callback(self._continue(task))
                    self._schedule(future)
                <span class="hljs-keyword">else</span>:
                    self._schedule(task)
            <span class="hljs-keyword">except</span> StopIteration:
                <span class="hljs-keyword">pass</span>
</code></pre>
<p>Now when a task yields a Future, the scheduler sets a callback that:</p>
<ul>
<li><p>Assigns the result back to the task (task.val)</p>
</li>
<li><p>Reschedules the task so it can resume execution</p>
</li>
</ul>
<p>This is not very easy to visualize, so here is a sequence diagram showing what happens:</p>
<p><img src="https://www.mermaidchart.com/raw/dfe16b2d-172b-4342-b126-5d7d8eeb9488?theme=light&amp;version=v0.1&amp;format=svg" alt="Handling a Future in the Event Loop" class="image--center mx-auto" /></p>
<hr />
<p>Let’s put this to the test. Here’s an implementation of a non-blocking sleep using this future logic:</p>
<pre><code class="lang-python">


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Sleep</span>(<span class="hljs-params">Future</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, wait_for: int, *args, **kwargs</span>):</span>
        super().__init__(*args, **kwargs)
        self._wait_for = timedelta(seconds=wait_for)
        self._ready_at = datetime.now(tz=timezone.utc) + self._wait_for

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_check_result</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">if</span> datetime.now(tz=timezone.utc) &gt;= self._ready_at:
            <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
</code></pre>
<p>And now let's do some non-blocking awaiting:</p>
<pre><code class="lang-python">
<span class="hljs-keyword">import</span> time

<span class="hljs-keyword">from</span> scheduler.future <span class="hljs-keyword">import</span> Sleep
<span class="hljs-keyword">from</span> scheduler.scheduler <span class="hljs-keyword">import</span> Scheduler


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">sleep</span>(<span class="hljs-params">n: int</span>):</span>
    <span class="hljs-keyword">return</span> (<span class="hljs-keyword">yield</span> Sleep(n))


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">ping</span>():</span>
    print(<span class="hljs-string">"waiting for result before ping..."</span>)
    result = <span class="hljs-keyword">yield</span> <span class="hljs-keyword">from</span> sleep(<span class="hljs-number">5</span>)

    print(<span class="hljs-string">f"result: <span class="hljs-subst">{result}</span>"</span>)
    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
        print(<span class="hljs-string">"ping"</span>)
        time.sleep(<span class="hljs-number">1</span>)
        <span class="hljs-keyword">yield</span> <span class="hljs-keyword">from</span> sleep(<span class="hljs-number">0</span>)


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pong</span>():</span>
    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
        print(<span class="hljs-string">"pong"</span>)
        time.sleep(<span class="hljs-number">1</span>)
        <span class="hljs-keyword">yield</span> <span class="hljs-keyword">from</span> sleep(<span class="hljs-number">0</span>)


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    scheduler = Scheduler()
    scheduler.create_task(ping())
    scheduler.create_task(pong())
    scheduler.run_forever()
</code></pre>
<p><img src="https://res.cloudinary.com/dllztfozz/image/upload/c_crop,w_500,h_300,g_north_west/v1746188175/blog/kata-asyncio/jklx673pvmkomrwcchen.gif" alt="non_blocking_ping_pong.gif" class="image--center mx-auto" /></p>
<p>We did it! ping pauses for 5 seconds, and during that time, pong can continue doing its thing. Just like with the real <code>asyncio.sleep</code>.</p>
<p>Also, note the use of yield from when nesting coroutines.If we had just done yield Sleep(n), we’d yield a generator instead of the result we wanted. If this seems too magical, <a target="_blank" href="https://github.com/jbrocher/asyncio-model/tree/step/nested-coroutines">check out the branch step/nested-coroutines</a> in the linked repo. In that branch, I use stack in the <code>Task</code> class to handle nested co-routine manually.</p>
<p>Now we could stop here for our scheduler and skip to doing some I/O. But the real <code>asyncio</code> lib works with the <code>async/await</code> keywords. Wouldn’t it be nice if we could do that as well ?</p>
<h2 id="heading-adhering-to-asyncawait">Adhering to async/await</h2>
<p>The keywords <code>async</code> and <code>await</code> don't introduce new features to Python's execution model. They simply organize and improve what we've been doing with <code>yield from</code> and generators.</p>
<p>There's no magic involved. <code>await</code> works like <code>yield from</code>, and <code>async</code> alters our function type from <code>Generator</code> to <code>Coroutine</code>. This helps keep asynchronous generators/coroutines separate from regular generators.</p>
<p>To convert our generator-based coroutines into true coroutines, we just need to add <code>async</code> before them. Then we can use <code>await</code> with them (which is a drop-in replacement for <code>yield from</code>).</p>
<p>To integrate our custom <code>Future</code> with Python's <code>await</code> system, we need to make it <em>awaitable</em>. This means implementing the <code>__await__</code> method, which Python uses under the hood when you write <code>await my_future</code>.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Future</span>(<span class="hljs-params">TaskProtocol, metaclass=ABCMeta</span>):</span>
   <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
       self._done_callbacks = []
       self.is_done = <span class="hljs-literal">False</span>
       self._result = <span class="hljs-literal">None</span>
       self._coro = self._run()

   <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__await__</span>(<span class="hljs-params">self</span>):</span>
       <span class="hljs-keyword">return</span> (<span class="hljs-keyword">yield</span> self)


    <span class="hljs-comment"># ... rest of the class</span>
</code></pre>
<p>And that's it, now we can change <code>sleep</code> like so</p>
<pre><code class="lang-python"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">sleep</span>(<span class="hljs-params">n: int</span>):</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> Sleep(n)
</code></pre>
<p>which is functionally the same as calling <code>Sleep(n).__await__()</code>.</p>
<p>Let me say that again because it is important: <code>async</code> and <code>await</code> do <strong>no</strong> magic whatsoever. They are just part of a protocol, much like the <code>Iterator</code> protocol. Their purpose is to keep us from mixing up regular generators with coroutines that are meant to be managed by a scheduler or an event loop.</p>
<p>And now that our futures are awaitable, we can use <code>async</code> and <code>await</code> everywhere. In fact, we must use them, because once a function is declared with <code>async def</code>, the interpreter expects <code>await</code> inside, not <code>yield</code>.</p>
<pre><code class="lang-python">
<span class="hljs-keyword">from</span> scheduler.future <span class="hljs-keyword">import</span> Sleep
<span class="hljs-keyword">import</span> time
<span class="hljs-keyword">from</span> scheduler.scheduler <span class="hljs-keyword">import</span> Scheduler


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">sleep</span>(<span class="hljs-params">n: int</span>):</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> Sleep(n)


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">ping</span>():</span>
    print(<span class="hljs-string">"waiting for result before ping..."</span>)
    result = <span class="hljs-keyword">await</span> sleep(<span class="hljs-number">5</span>)
    print(<span class="hljs-string">f"result: <span class="hljs-subst">{result}</span>"</span>)
    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
        print(<span class="hljs-string">"ping"</span>)
        time.sleep(<span class="hljs-number">1</span>)
        <span class="hljs-comment"># yield control to the event loop. We cannot use (yield in a "true" coroutine)</span>
        <span class="hljs-keyword">await</span> sleep(<span class="hljs-number">0</span>)


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pong</span>():</span>
    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
        time.sleep(<span class="hljs-number">1</span>)
        <span class="hljs-keyword">await</span> sleep(<span class="hljs-number">0</span>)
        print(<span class="hljs-string">"pong"</span>)


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    scheduler = Scheduler()
    scheduler.create_task(ping())
    scheduler.create_task(pong())
    scheduler.run_forever()
</code></pre>
<blockquote>
<p>You can still use <code>yield</code> inside an async function in the context of <a target="_blank" href="https://peps.python.org/pep-0525/">Asynchronous Generators</a>. But this is outside the scope of this post.</p>
</blockquote>
<p>Look at that ! Pretty much exactly what this would look like using the regular <code>asyncio</code>, except it comes from our own library. Pretty neat, uh ?</p>
<h2 id="heading-adding-real-io-to-our-async-framework">Adding Real I/O to Our Async Framework</h2>
<p>So far so good, but we still haven't handled any real I/O. What we have, though, is a framework that lets us build custom <code>Future</code> subclasses. Each of them can poll for a result and resume a task once that result is available.</p>
<p>At the beginning of the post, in the final code example, you may have noticed these two lines:</p>
<pre><code class="lang-python"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">read</span>(<span class="hljs-params">conn</span>):</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> ReadSocket(conn)

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">accept</span>(<span class="hljs-params">sock</span>):</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> AcceptSocket(sock)
</code></pre>
<p>Well guess what? Both <code>ReadSocket</code> and <code>AcceptSocket</code> are subclasses of our <code>Future</code> class.</p>
<p><code>AcceptSocket</code> allows us to suspend execution while waiting for new incoming connections on a socket. <code>ReadSocket</code> does the same for receiving data from a connection. Now that all the async plumbing is in place, their implementation is surprisingly simple:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> selectors
<span class="hljs-keyword">from</span> socket <span class="hljs-keyword">import</span> socket

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AcceptSocket</span>(<span class="hljs-params">Future</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, sock: socket, *args, **kwargs</span>):</span>
        super().__init__(*args, **kwargs)
        self._select = selectors.DefaultSelector()
        self._sock = sock
        self._select.register(sock, selectors.EVENT_READ)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_check_result</span>(<span class="hljs-params">self</span>):</span>
        events = self._select.select(<span class="hljs-number">0</span>)
        <span class="hljs-keyword">if</span> events:
            conn, addr = self._sock.accept()
            print(<span class="hljs-string">"accepted"</span>, conn, <span class="hljs-string">"from"</span>, addr)
            conn.setblocking(<span class="hljs-literal">False</span>)
            <span class="hljs-keyword">return</span> conn

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ReadSocket</span>(<span class="hljs-params">Future</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, sock: socket, *args, **kwargs</span>):</span>
        super().__init__(*args, **kwargs)
        self._select = selectors.DefaultSelector()
        self._sock = sock
        self._select.register(sock, selectors.EVENT_READ)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_check_result</span>(<span class="hljs-params">self</span>):</span>
        events = self._select.select(<span class="hljs-number">0</span>)
        <span class="hljs-keyword">if</span> events:
            <span class="hljs-keyword">return</span> self._sock.recv(<span class="hljs-number">1000</span>)
</code></pre>
<p>I won't go into the details of how sockets work in Python. If you need a refresher (I did!), the <a target="_blank" href="https://docs.python.org/3/library/socket.html">socket module docs</a> are a great place to start.</p>
<p>The key thing here is the <a target="_blank" href="https://docs.python.org/3/library/selectors.html"><code>selectors</code></a> module. It lets us monitor socket readiness without blocking the main thread. And that, finally, answers the question I posed at the beginning of this article:</p>
<blockquote>
<p><strong>"Who's doing the work while we await?"</strong></p>
</blockquote>
<p>The answer is simple. <strong>The operating system is</strong>. When we call <code>register()</code>, we ask the OS to monitor the socket for readiness. Each time we call <code>select(0)</code>, we check for any ready events without blocking. This makes implementing the <code>_check_result</code> overrides a piece of cake. We simply check if something is ready in the socket, and if so we return it. If not, we’ll check again during the next loop iteration. Our scheduler handles resuming the appropriate task.</p>
<hr />
<p>Now that everything is in place, we can finally build a working echo server:</p>
<pre><code class="lang-python">

<span class="hljs-keyword">from</span> socket <span class="hljs-keyword">import</span> SO_REUSEADDR, SOL_SOCKET, socket
<span class="hljs-keyword">from</span> scheduler.future <span class="hljs-keyword">import</span> AcceptSocket, ReadSocket, Sleep
<span class="hljs-keyword">from</span> scheduler.scheduler <span class="hljs-keyword">import</span> Scheduler


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">sleep</span>(<span class="hljs-params">n: int</span>):</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> Sleep(n)


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">read</span>(<span class="hljs-params">conn</span>):</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> ReadSocket(conn)


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">accept</span>(<span class="hljs-params">sock</span>):</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> AcceptSocket(sock)


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">echo</span>(<span class="hljs-params">sock</span>):</span>
    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
        data = <span class="hljs-keyword">await</span> read(sock)
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> data:
            sock.close()

        print(<span class="hljs-string">f"received <span class="hljs-subst">{data}</span>"</span>)
        <span class="hljs-comment"># assume non-blocking</span>
        sock.send(data)


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">echo_server</span>(<span class="hljs-params">scheduler: Scheduler, port: int</span>):</span>
    print(<span class="hljs-string">"creating socket..."</span>)
    sock = socket()
    sock.bind((<span class="hljs-string">"localhost"</span>, port))

    sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, <span class="hljs-number">1</span>)
    sock.listen(<span class="hljs-number">100</span>)
    sock.setblocking(<span class="hljs-literal">False</span>)
    print(<span class="hljs-string">"socket created waiting for connection"</span>)
    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
        conn = <span class="hljs-keyword">await</span> accept(sock)
        <span class="hljs-comment"># Schedule new concurrent connection</span>
        scheduler.create_task(echo(conn))


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    scheduler = Scheduler()
    scheduler.create_task(echo_server(scheduler, <span class="hljs-number">1235</span>))
    scheduler.run_forever()
</code></pre>
<p><img src="https://res.cloudinary.com/dllztfozz/image/upload/c_crop,g_north_west/v1746273388/blog/kata-asyncio/fi0dyjap69smswi4y2ir.gif" alt="echo_server.gif" class="image--center mx-auto" /></p>
<p>The <code>echo</code> coroutine handles a single client connection. The <code>echo_server</code> coroutine waits for new clients and launches a new concurrent instance of <code>echo</code> for each one. You can run the script and connect multiple clients to port <code>1235</code> to see it in action. The <a target="_blank" href="https://github.com/jbrocher/asyncio-model">echo_server is avaible in the <code>scheduler</code> package of the repository</a></p>
<h2 id="heading-conclusion">🎉 Conclusion</h2>
<p>If you've made it this far, congratulations! Our implementation is pretty close to the real thing, and asyncio should feel way less mysterious.</p>
<p>Of course, it gets more complex when you want to support timeouts, cancellation, priorities, etc., but the foundation remains the same. If you want to dig deeper, check out the <a target="_blank" href="https://github.com/python/cpython/blob/main/Lib/asyncio/futures.py">asyncio source in the CPtyon repository</a>, it should look pretty familiar now.</p>
<p>Turns out, the real magic isn’t the <code>await</code>. It's the yield we wrote along the way.</p>
<h2 id="heading-references">References</h2>
<ol>
<li><p><a target="_blank" href="https://www.dabeaz.com/coroutines/">https://www.dabeaz.com/coroutines/</a></p>
</li>
<li><p><a target="_blank" href="https://docs.python.org/3/library/asyncio-task.html">https://docs.python.org/3/library/asyncio-task.html</a></p>
</li>
<li><p><a target="_blank" href="https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/">https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/</a></p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[How to Revert a Merge Commit And Then Merge Again]]></title><description><![CDATA[A few days ago,  I stumbled upon a thread talking about the trade-offs of squash-merging a PR VS creating a merge commit. One of the topics of discussion was whether or not reverting merge commits was possible. Here’s the thread in question:
https://...]]></description><link>https://dev.indooroutdoor.io/how-to-revert-a-merge-commit-and-then-merge-again</link><guid isPermaLink="true">https://dev.indooroutdoor.io/how-to-revert-a-merge-commit-and-then-merge-again</guid><category><![CDATA[GitHub]]></category><category><![CDATA[Git]]></category><category><![CDATA[#howtos]]></category><category><![CDATA[version control]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Jb Rocher]]></dc:creator><pubDate>Mon, 28 Nov 2022 11:00:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1669453866610/MRI82WNOI.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A few days ago,  I stumbled upon a thread talking about the trade-offs of squash-merging a PR VS creating a merge commit. One of the topics of discussion was whether or not reverting merge commits was possible. Here’s the thread in question:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/AdamRackis/status/1591437649369534465">https://twitter.com/AdamRackis/status/1591437649369534465</a></div>
<p>There seemed to be some confusion surrounding the subject which isn't surprising. Indeed,  while reverting a merge commit is definitely possible, it can have unintended consequences that are a pain to deal with. Specifically, once you’ve reverted the merge, re-introducing the changes from your PR is not straightforward.  Having struggled with this a few times myself, I thought I’d do a small write-up to clear things up! </p>
<p>By the end of this post, you should know how to revert merge commits, and more importantly, how to re-introduce your changes once you’re done with your fix. This assumes basic knowledge of git. </p>
<p>Let’s dive in!</p>
<h1 id="heading-whats-in-a-merge-commit">What’s in a merge commit</h1>
<p>First things first, we need to clarify a few things about merge commits, this will be important later on. When you’re merging a branch, say a feature branch into master, git does basically 3 things : </p>
<ul>
<li>Determine the closest common ancestor of the two branches. This allows git to compute the diff between the two branches, and detect eventual conflicts.</li>
<li>Create a commit containing the changes from both branches. This is the merge commit</li>
<li>Add the last commits of the branches you’re merging as ancestors to the merge commit. This means <strong>every merge commit has two ancestors.</strong></li>
</ul>
<p>Once the merging is done, you’ll end up  with something like this: </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669363589872/ShLlP0m0q.png" alt="Merge Commit" class="image--center mx-auto" /></p>
<p>So far so good. But now let’s say you missed something. Something big. The <code>buggy-feature</code> branch above <em>unexpectedly</em> introduces a critical bug in production. </p>
<p>Ideally, you would be able to quickly narrow down the issue to one commit and deploy a hotfix to resolve the issue. However sometimes you simply cannot pinpoint which changes broke something, and you can’t afford to let the bug sit in production much longer. </p>
<p>In this sort of situation, you might want to revert the merge and sort out the rest later. Let’s see how to do that! </p>
<h1 id="heading-reverting-the-merge-commit">Reverting the merge commit</h1>
<p>First of all, you can definitely revert a merge commit. It just works a bit differently than a regular commit. Indeed, if you try to revert it using <code>git revert 181c8d1</code> you’ll be greeted by the following error message:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669363757240/X-XOEklcJ.png?auto=compress" alt="Revert Error Message" class="image--center mx-auto" /></p>
<p>The revert failed, and what’s this “-m option” stuff about? </p>
<p>Well, remember,  merge commits have two ancestors. Thus, git must know which ancestor it should use as a reference to compute the diffs you want to remove. The content of the revert commit will differ depending on which ancestor you’re reverting to. And that’s what the <code>-m</code> option is for. Let’s see how it works. </p>
<p><a target="_blank" href="https://git-scm.com/docs/git-revert#Documentation/git-revert.txt--mparent-number">The <code>-m</code> option takes the index of the ancestor you want to use as a reference</a>. It can be either  <code>1</code> or <code>2</code>. Most of the time, if you’re reverting a merge commit from a PR into main, you want to revert to the previous main commit which means you’ll want <code>-m 1</code>. </p>
<p>If you want to be sure tho, just use <code>git show</code> on the merge commit: </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669363757240/X-XOEklcJ.png?auto=compress" alt="Show ancestors" class="image--center mx-auto" /></p>
<p>Notice the “Merge:” line. It indicates both ancestors. When you pass <code>-m 1</code> or <code>-m 2</code> to the revert command, it tells git to use the first or second ancestor as listed on this line. </p>
<p>Now you’ve reverted the merge, and production is working again. You can breathe. Seriously just take the time to breathe, it does wonder for your health. Not breathing is strongly discouraged by most medical practitioners.  </p>
<p>This is not over though. Once you’ve determined what’s wrong with your branch, you’ll probably want to add a commit to fix it and re-merge it into the master. And this is where things start to get hairy. </p>
<h1 id="heading-re-introducing-the-feature-after-the-fix">Re-introducing the feature after the fix</h1>
<h2 id="heading-why-re-merging-doesnt-work">Why re-merging doesn’t work</h2>
<p>Now with production up, you had time to figure out what's wrong with your branch. You’ve added a commit to fix that, and you’re ready to re-merge the branch. If you do so, you should end up with the following git history :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669363835833/kCuKGJ11I.png?auto=compress" alt="Git History" class="image--center mx-auto" /></p>
<p>This looks fine at first, but you’ll probably quickly notice something is wrong again and might even create a new bug. Why is that? Well if you inspect the content of the master branch, you’ll discover that the content of the commits prior to the fix, <code>Good Commit #1</code> and <code>Buggy Commit #1</code> is missing. <strong>This is probably not what you want.</strong>  </p>
<p>To understand why the commits are missing, we need to discuss <strong>what git does when you revert a merge commit</strong> : </p>
<ul>
<li>First, it looks at the difference between the merge commit and the ancestor you provide using the <code>-m</code> option</li>
<li>then, it inverts the diff and commits the result. This is the “revert” commit.</li>
</ul>
<p>What it doesn’t do, however, is delete the meta-data that indicates that the feature branch has already been merged. In the example above, this means, that <code>Buggy</code> commit is still an ancestor of the first merge commits. Which means that when you re-merge your branch, the following things happen:</p>
<ul>
<li>Git determines the closest common ancestor. <strong>Since the first merge is still in history, the closest common ancestor here is <code>Buggy Commit</code>.</strong></li>
<li>Git determines the difference. <strong>In this case, it creates a diff between the <code>Revert commit</code> and <code>Buggy Commit</code>.</strong> </li>
<li>Git creates the merge commits, with two ancestors. </li>
</ul>
<p><code>Good Commit #1</code> (and <code>Buggy Commit #1</code>) are part of the master branch history now. They cannot be included again. </p>
<p>What can you do then? Well, the <a target="_blank" href="https://git-scm.com/docs/git-revert#Documentation/git-revert.txt--mparent-number">git documentation</a> isn’t really optimistic about the subject …</p>
<blockquote>
<p>Reverting a merge commit declares that you will never want the tree changes brought in by the merge</p>
</blockquote>
<p>… but there are solutions! Let’s look at a few of them! </p>
<h2 id="heading-how-to-re-merge-your-pr">How to “re-merge” your PR</h2>
<p>Re-merging directly doesn’t do the trick, so we’ll have to be clever to force git to accept the changes from the branch. To do so they are a few alternatives</p>
<h2 id="heading-reverting-the-revert">Reverting the revert</h2>
<p>This is the most straightforward way to do it. To make your changes part of the master again, you can simply revert the revert commit. This will make it so the initial revert never happened, with both reverts canceling each other out. This might sound a bit confusing, so here’s what it looks like with our example :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669363906861/olPCes4iI.png?auto=compress" alt="Reverting The Revert" class="image--center mx-auto" /></p>
<p>Once you’ve done that you can simply merge the fix you’ve made to your PR and be done</p>
<p>Be careful tho, between the instant you revert the revert and the moment you merge the fix, the bug will be re-introduced. So if you’re using some kind of automation to auto-deploy pushes made to the main branch, be sure to perform this operation, before deploying. Otherwise, you risk re-deploying the bug. </p>
<p>This method is described in more detail in <a target="_blank" href="https://github.com/git/git/blob/master/Documentation/howto/revert-a-faulty-merge.txt">the official git how-to</a>. (The not-so-obvious location of this doc might explain some of the confusion around this subject)</p>
<h2 id="heading-cherry-picking-and-rebasing">Cherry-Picking and Rebasing</h2>
<p>Another alternative is starting a new branch from the main, and cherry pick the commit from your previous branch, including the fix. This will change the hash of the commit, so it won’t detect them in the branch history. (This also works using  <code>rebase --on-to</code>  , but not <code>rebase</code> alone as it will automatically use the closes common ancestor, which again will by “Buggy Commit #1”). If all goes well you should end up with something like this. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669363951491/J_FQPtUVP.png?auto=compress" alt="Rebased changes" class="image--center mx-auto" /></p>
<p>The commits from “buggy-feature” are duplicated with a different hash and can be “re-merged” into master. </p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Hope this clears things up! I think the main thing to take away here is that reverting a merge commit is very painful. Even tho they are alternatives, you might still end up with a lot of conflict resolution, especially if other people are working on the same repo. You could always ask your coworkers to stop working for a bit while you’re sorting this out, but this isn’t ideal either, especially on a big project.</p>
<p>What should you do then? Well,  Linus explains it best:  </p>
<blockquote>
<p>If you find a problem that got merged
into the main tree, rather than revert the merge, try <em>really</em> hard to
bisect the problem down into the branch you merged, and just fix it, or
try to revert the individual commit that caused it.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Authentication Patterns and Best Practices For SPAs]]></title><description><![CDATA[Introduction
 Authentication is something most web applications need, and that can be difficult to get right. Recently I had to implement it for a React app I was developing, and wanted to list the options available to me. So I did a bit of research ...]]></description><link>https://dev.indooroutdoor.io/authentication-patterns-and-best-practices-for-spas</link><guid isPermaLink="true">https://dev.indooroutdoor.io/authentication-patterns-and-best-practices-for-spas</guid><category><![CDATA[Frontend Development]]></category><category><![CDATA[backend]]></category><category><![CDATA[authentication]]></category><category><![CDATA[authorization]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Jb Rocher]]></dc:creator><pubDate>Sat, 09 Apr 2022 16:43:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1649496823483/2dtuQP4D-.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p> Authentication is something most web applications need, and that can be difficult to get right. Recently I had to implement it for a React app I was developing, and wanted to list the options available to me. So I did a bit of research and, to my surprise, I found it's really hard to get a straightforward answer on the proper way
 to implement authentication between an SPA and an API backend. </p>
<p> Since I had to do quite a bit of work to identify the distinct patterns I could choose from, I decided to compile them into an article so others could benefit from them! My goal 
 here is to provide you with a good starting point should you ever want your user to be able to authenticate with your SPA.</p>
<h2 id="heading-setting-the-context">Setting the context</h2>
<p>Before diving deeper into the subject, it's important to have an idea of what we're trying to achieve and what we'd like to avoid. So let's review
what we mean by "Authentication", and the main kind of security issues we need to look out for. However, if you'd like to skip all that and go 
straight to the authentication patterns, <a class="post-section-overview" href="#authenticating-with-an-spa">feel free to do so</a> !</p>
<h3 id="heading-the-three-aspects-of-authentication">The three aspects of "Authentication"</h3>
<p>Usually when we talk about implementing some kind of Authentication system on an application, we're actually talking about
3 different concepts. In a monolithic app, these are rarely explicitly stated, because they're usually tackled at the same time. However,
as we'll see a bit later, some of the Authentication patterns available to SPA don't cover all of them, which means it's important 
to define them. These concepts are <strong>Authorization</strong>, <strong>Authentication</strong> and <strong>Session</strong>:</p>
<ul>
<li>Authorization: Determining if an entity is allowed to perform a specific action. This doesn't necessarily mean we need to know <strong>who</strong> is performing the action. </li>
<li><em>Actual</em> Authentication: Knowing the identity of the user. For example their email address, username or any property that can be used to uniquely identify a user in 
your domain of work. </li>
<li>Session: Maintaining state for one or both of the above concepts</li>
</ul>
<p>Keep that in mind, we'll refer to these definitions often throughout the article!</p>
<h3 id="heading-2-types-of-attack-to-avoid">2 types of attack to avoid</h3>
<p>Now that we know what we want, let's review what we <strong>don't</strong> want. That is, security flaws that could allow an attacker to by 
pass our authentication system. There are an infinite possibilities when it comes to attacking an application, and no system can
claim to be completely secure. However, when building an authentication system, here are the ones we mainly need to worry about: </p>
<ul>
<li>Cross Site Request Forgery (CSRF);</li>
<li>and, Cross Site Scripting (XSS, I guess CSS was already taken) </li>
</ul>
<p>I'll quickly go over them, just so we can understand the mechanism we need to have in place to cover for these!</p>
<h4 id="heading-csrf-attacks">CSRF Attacks</h4>
<p>These kind of attacks target authentication schemes that relies on cookies for storing credentials or session ID. They work by exploiting
the fact that cookies related to a domain are automatically sent by the browser for every request made to the domain. This allows malicious
website to set up forms designed to hit your application, and perform unwanted side-effects if your user is currently logged in. </p>
<p>There is also another kind of "reverse" CSRF attack which specifically targets login form. In these kind of attacks, the malicious website logs in the browser 
with the <em>attacker account</em>. Then when the user goes back to your app, thinking they're logged in with their own account, the attacker can 
gain access to any sensitive data they enter.</p>
<p>It's important to note that CORS settings alone <strong>do not</strong> prevent CSRF attacks. Indeed, with the exception of pre-flighted requests, CORS doesn't
prevent the browser from making the request, it just prevents the response to be read by javascript.<sup id="1"><a class="post-section-overview" href="#ft1">[1]</a></sup></p>
<h4 id="heading-xss-attacks">XSS Attacks</h4>
<p>A Cross-Site Scripting Attack is a really wide category of attacks, where a malicious person manage to inject some foreign javascript
into your application. For example if you render some text coming from user input, without escaping potential HTML code, someone
could pretty much do whatever they want with your SPA. Specifically regarding authentication, they could read any sensitive information
stored in LocalStorage or SessionStorage, which is why you'll often read that you MUST not store session data into LocalStorage.<sup id="2"><a class="post-section-overview" href="#ft2">[2]</a></sup> </p>
<p><em>As a side note, some argue that this is a non subject as if you're vulnerable to XSS attacks, you have bigger issues to deal with anyway. For example 
an attacker could simply modify a login form to send credentials directly to their own server. Personally I disagree entirely as I think security
measures should be self-contained and make no assumptions on the scale of the attack.</em></p>
<hr />
<h3 id="heading-authenticating-with-a-monolith">Authenticating with a monolith</h3>
<p>One more thing : Before diving into the SPA world, I'd like to quickly review how it's done with a monolith. 
This way we'll have a reference point when talking about the specificities of SPA authentication. </p>
<p>With a monolith, usually it works like this: </p>
<center>

<img src="https://media.giphy.com/media/cjyVveMCMgunS/giphy.gif" alt="Monolith" />

 Wait, not that kind of monolith 

</center>


<p>I mean like this:</p>
 <center>

 <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649522722644/rhh797gOE.jpg?auto=compress" alt="Monolith Sequence Diagram" />

 Monolothic auth sequence diagram

 </center>

<p>It's simple really: once the user submits their credentials, the server creates a stateful session. Then it mints an httpOnly cookie containing a session id, 
which will be sent with each subsequent request. Authentication is performed by storing an identifier in the session, and Authorization is checked 
by looking up the rigths/roles/permissions/whatever associated with the identity. The session is maintained natively by the browser and the cookie.</p>
<h4 id="heading-a-word-on-csrf">A word on CSRF</h4>
<p>As outlined in the previous section, using a cookie makes the app vulnerable to CSRF attacks. Most frameworks have a built in  way to deal with it using 
a CSRF token mechanism similar to the one I've included into the sequence diagram. This is good, because building a CSRF token system is <em>hard</em> to do and <em>easy</em> to get wrong. </p>
<h2 id="heading-authenticating-with-an-spa">Authenticating with an SPA</h2>
<p>All right, now that's out of the way, let's start with today's main subject.
I'm sure you're glad you've just read 800 hundred words not related in any way to SPAs, in an article about SPAs. 
But this was necessary, and now we'got all the context we need to review the available SPA authentication patterns in a constructive way! </p>
<h3 id="heading-option-1-stateful-session-with-cookie">Option 1: Stateful session with cookie</h3>
<p>This is the simplest approach, and closely resembles the monolithical one. Here's how it looks : </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649522764299/SzZkNbYBp.jpg?auto=compress" alt="SPA Stateful cookie" /></p>
<p>As with the monolithic architecture, the API creates a stateful session, and a Session Cookie 🍪, with the session ID. The only difference is that the UI is now provided 
by the SPA. It is a big difference though because: </p>
<ul>
<li>The SPA is <em>Authorized</em> to perform some actions on behalf of the user, but the user is only <em>Authenticated</em> with the API. Meaning the SPA doesn't know 
the identity of the user. If you choose this pattern you'll have to create a dedicated route (something like <code>/me</code> or <code>/profile</code>) to fetch the identity of the 
user. </li>
<li>As we're now dealing with two different apps, for this approach to work you need to be able to share the cookie between them. This means they have to be hosted
on the same domain</li>
<li><p>Since we're using a cookie, we're vulnerable to CSRF attack. However <em>contrary</em> to the monolothic approach where it's often handled by the framework, you 
have to deal with it yourself. </p>
<h4 id="heading-dealing-with-csrf-attacks">Dealing with CSRF attacks</h4>
</li>
</ul>
<p>In this case there are two main ways to prevent CSRF attacks: </p>
<ul>
<li>Setting SameSite on the cookie: This prevents the browser from automatically sending it along with requests made from another domain. This is the recommended approach by the OAuth2 specs on browser-based application<sup id="3"><a class="post-section-overview" href="#ft3">[3]</a></sup>. The only caveats is that this setting is only supported by recent browser versions, so users using outdated ones will be vulnerable!<sup id="4"><a class="post-section-overview" href="#ft4">[4]</a></sup></li>
<li>Manually setting up a CSRF mitigation method like a CSRF token. This can definetly work as outlined <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#browser_compatibility">in this article</a> but it's really easy to get wrong, so I'd use this option as a last resort.</li>
</ul>
<h4 id="heading-pros-andamp-cons">Pros &amp; Cons</h4>
<p><strong>Pros</strong></p>
<ul>
<li>Low cost of implementation </li>
</ul>
<p><strong>Cons</strong></p>
<ul>
<li>Older browser are not protected by SameSite cookie, you need to manually implement CSRF</li>
<li>You must be able to share a domain with the server</li>
<li>Doesn't provide direct authentication for the SPA, you need to make another call to a dedicated API route.</li>
</ul>
<h3 id="heading-option-2-stateless-jwt-authentication">Option 2: Stateless JWT authentication</h3>
<p>This pattern uses JWT to exchange authentication data. JWT is a standard for exchanging signed JSON data (signed, not secret !). If you want more details
about how JWT work, Auth0 has <a target="_blank" href="https://jwt.io/introduction">a dedicated website with all the information you'll need</a>. Here it's used to provide a stateless way to manage 
authentication in the SPA and authorization in the API: </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649321585788/0p6NIe-aM.jpg?auto=compress" alt="JWT Sequence Diagram" /></p>
<p>Pretty straightforward, the credentials are exchanged against a JWT that contains : </p>
<ul>
<li>An Access Token used as a bearer token for authorization</li>
<li>A Refresh Token for when the Access Token expires</li>
<li>The identity of the user (often under the "sub" key of the json data)</li>
</ul>
<p>This kind of authentication isn't as exposed to CSRF attacks if you don't store the JWT inside a cookie. </p>
<h4 id="heading-what-about-session">What about session</h4>
<p>Maintaining session is problematic in this case. As explained earlier, we can't just store the Refresh Token inside the local storage, as it's vulnerable to XSS attacks. You
could store it inside an HttpOnly cookie, but you lose the ability to <em>authenticate</em> the user with the JWT in the SPA. In that case I'd recommend using option 1 instead if possible, as it is more battle tested and easier to implement.</p>
<p>There is a way to give the illusion of maintaining an active session, but it requires a more complex approach, that is outlined in the next section.</p>
<h4 id="heading-pros-andamp-cons">Pros &amp; Cons</h4>
<p><strong>Pros</strong></p>
<ul>
<li>Provide both Authorization and Authentication of the SPA</li>
<li>Stateless which <em>may</em> improve performance depending on your architecture. For example by saving a database lookup. </li>
</ul>
<p><strong>Cons</strong></p>
<ul>
<li>Can't really maintain session in a secure way</li>
</ul>
<h3 id="heading-option-3-openid-connect">Option 3: OpenID connect</h3>
<p>OpenId Connect is an extension of the OAuth2 authorization framework that adds authentication capabilities to it. </p>
<p>OAuth2 was originally meant to allow a third-party app
to perform actions in a main application on behalf of the user. Like posting comments on Facebook, or publishing a tweet. This means that "third-party" here is defined from the point of view of the end user. As in "I don't want to give my Facebook password to this random application, but I'd like to allow it to publish status on my behalf". The goal is 
to give the third-party app an Access Token signed by the authentication server (Facebook in our example). This doesn't take care of <em>authenticating</em> the user. </p>
<center>

<img src="https://media.giphy.com/media/ScFZKpCFoTIblL4Epu/giphy.gif" alt="who are you" />

Can't answer that with authorization alone !

</center>

<p>Authentication is enabled by the OpenId Connect protocol that adds a standard for returning an identifier for the user along the access token, that can be decoded and used
by the third party app. </p>
<p>In our case, it can be used by our SPA to Authenticate the user against our API and get an access token to perform some actions. Our SPA <em>is not</em> a third-party as defined by OAuth2 since our user doesn't even need to know that the SPA and the API are two different things. However it allows us to treat our API as an authentication service for our
spa which has several benefits:</p>
<ul>
<li>It scales better in case you DO want to authenticate from other third-party services. </li>
<li>It allows you to isolate your login form making it more secure</li>
<li><p>It allows the implementation of a Silent Authentication for maintaining sessions </p>
<p>Here's how it looks:</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649336853356/1-hPW3ycj.jpg?auto=compress" alt="OpenId connect" /></p>
<p>It's important to note that there are several possible authentication flows when using OpenId Connect. Currently the flow that must be used by SPAs is the Authorization Clode Flow with Proof Key for Code Exchange. I won't describe it here, instead I'll do you one better and link to the awsome Auth0 article that goes into . I <em>strongly</em> recommend you do not try to implement this yourself as it's time consuming, and easy to get wrong. Instead use the recommended lib
from you framework. For example if you're using the great Django Rest Framework, you can easily add OAuth2/OpenID Connect capabilities with <a target="_blank" href="https://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit">Django Oauth Toolkit for DRF</a></p>
<h4 id="heading-maintaining-session">Maintaining Session</h4>
<p>As explained, it is not safe to store the tokens returned by the OpenID Connect flow in the browser. Instead, since you can make use of a <a target="_blank" href="https://auth0.com/docs/authenticate/login/configure-silent-authentication">Silent Authentication Flow</a>. It works by setting a cookie on the Authentication Server and not prompting the user for their credentials
if they are already logged in. CSRF is still an issue here, but since it only concerns the login form, you can use your API framework CSRF token system to mitigate, which is 
quite easy in most cases.</p>
<h4 id="heading-pros-andamp-cons">Pros &amp; Cons</h4>
<p>Pros: </p>
<ul>
<li>The most flexible set up as you can use it to authenticate third-party App</li>
<li>Allows the use of federated identiy provider By proxying other Open id provider like Facebook or Google
Cons: </li>
<li>More costly to implement and hard to get right without using a trusted Framework / Library</li>
<li>I you use a dedictated authentication provider, you might need to subscribe to a paying plan</li>
</ul>
<h3 id="heading-backend-for-frontend">Backend For Frontend</h3>
<p>There is one alternative I haven't listed yet, that opens up new possibilities and new authentication flows. It is the "Backend For Frontend" architecture pattern, which 
means serving your SPA from a server that can also run code. For example a Meta-Framework like NextJS, or just a regular server that happens to also statically serve your app. 
Using this solution changes a lot of things. For example, it might be easier to manually mitigate CSRF threats in option 1, or use a cookie to store the tokens created in Option 2. </p>
<p>However I won't go into the details here, beyond the scope of just choosing and Authentication Solution. Instead I might write 
a dedicated article listing the patterns associated with this architecture</p>
<p>In the meantime, the OAuth2 spec has a <a target="_blank" href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps#section-6.2">great section</a> on the subject if you'd like to know more. </p>
<h3 id="heading-using-an-authentication-provider">Using an Authentication Provider</h3>
<p>Finally, As we've seen with the previous patterns, Authenticating an SPA is not as straightforward as it should be. If you don't want to invest too much time
looking for the perfect solution, you can always use an Authentication and Authorization SaaS. Most of them come with out-of-the-box integrations 
with both you SPA and your framework of choice, which can save you a lot of time. Of course, even though most of them offer a free plan, you might need to purchase
a paying subscription as your user base grows.</p>
<p>Most of them rely on OpenID Connect behind the scenes, meaning the integration with your SPA and your API usually look like this:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649323779155/8pK63Whlg.jpg?auto=compress" alt="Auth Provider Sequence Diagram" /></p>
<ul>
<li>Here are a few examples that provide a great DX: <ul>
<li><a target="_blank" href="https://auth0.com/docs/get-started/auth0-overview">Auth0</a>: Awesome service, and great documentation. However it quickly gets expensive;</li>
<li>[Firebase auth]: GCP authentication solution. Interesitingly they seems to store some token in IndexDB which is not XSS safe; </li>
<li>[AWS cognito]: AWS identiy management solution. Might be a good solution if you're already using AWS;</li>
<li><a target="_blank" href="https://www.keycloak.org/">Keycloack</a>: Open source, yay! </li>
</ul>
</li>
</ul>
<hr />
<h2 id="heading-conclusion">Conclusion</h2>
<p>As often when it comes to programming, there is no silver bullet for handling Authentication with SPAs. With this article I hope to give you 
some insight into what's possible so you can find a solution that best suit your needs. And to make this easier, 
I've compiled what we covered into this handy chart, I hope it helps you in your conception work, it certainly helped me!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1649429989568/eFVYYwWVg.png?auto=compress" alt="Auth patterns comparison chart" /></p>
<p>I might write some dedicated tutorials on one or more of this pattern so stay tuned !</p>
<hr />
<h3 id="heading-references">References</h3>
<ol>
<li><span id="ft1"><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">MDN CORS documentation</a></span></li>
<li><span id="ft2"><a target="_blank" href="http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/">The issues with using jwt for maintaining sessions</a></span></li>
<li><span id="ft3"><a target="_blank" href="https://tools.ietf.org/html/draft-ietf-oauth-browser-based-apps">OAuth2 for browser-based apps</a></span></li>
<li><span id="ft4"><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#browser_compatibility">SameSite cookies</a></span></li>
<li><a target="_blank" href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/which-oauth-2-0-flow-should-i-use">Auth0 which auth flow</a></li>
<li><a target="_blank" href="https://medium.com/tresorit-engineering/modern-csrf-mitigation-in-single-page-applications-695bcb538eec">Mitigating CSRF in spas</a></li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Re-Doing the Django Tutorial With FastAPI And React: Connecting a React app to FastAPI !]]></title><description><![CDATA[Note: This is part 4 of a multi-part tutorial on FastApi and React. If you want to start from the beginning (which I recommend!😉) here's part 1!
Welcome to part 4 of this tutorial! Today we'll see how to connect a React app to our awesome FastAPI ba...]]></description><link>https://dev.indooroutdoor.io/re-doing-the-django-tutorial-with-fastapi-and-react-connecting-a-react-app-to-fastapi</link><guid isPermaLink="true">https://dev.indooroutdoor.io/re-doing-the-django-tutorial-with-fastapi-and-react-connecting-a-react-app-to-fastapi</guid><category><![CDATA[React]]></category><category><![CDATA[backend]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[REST API]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Jb Rocher]]></dc:creator><pubDate>Mon, 06 Dec 2021 17:25:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1638722693766/gzQdNkBLI.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Note: This is part 4 of a multi-part tutorial on FastApi and React. If you want to start from the beginning (which I recommend!😉) here's <a target="_blank" href="https://dev.indooroutdoor.io/the-poll-app-from-django-tutorial-with-fastapi-and-react-setting-up-the-project">part 1</a>!</em></p>
<p>Welcome to part <strong>4</strong> of this tutorial! Today we'll see how to connect a React app to our awesome FastAPI backend! As always, <a target="_blank" href="https://github.com/jbrocher/fast-api-poll-4">here's the repository</a> with the code we'll be writing during this article.</p>
<p>Last time we added the following routes to our API:</p>
<ul>
<li><code>/polls/</code>:  Lists all the existing questions</li>
<li><code>/polls/{id}/</code>:  Displays a poll details, including the associated results</li>
</ul>
<p>Now our goal is to use them to display the same information as in the original <a target="_blank" href="https://docs.djangoproject.com/en/3.2/intro/tutorial03/">Django tutorial</a>, using React: </p>
<ul>
<li>An index page for listing the polls</li>
<li>A Form for each poll</li>
<li>A Result page for each poll</li>
</ul>
<p>In fact, since we'll be using React we can go one step further and merge the two last views together in a mutli-purpose detail view with the following specs: </p>
<ol>
<li>First when arriving on <code>/polss/{id}/</code> the user should see the title of the poll and the available choices</li>
<li>Then the user submits their own vote by clicking on one of the choices</li>
<li>Finally once the vote is processed by the API, the current vote count is displayed to the user under each choice</li>
</ol>
<p>As in the Django tutorial, we'll keep the actual vote submission for the next part though ! </p>
<p>We'll use <a target="_blank" href="https://reactjs.org/docs/create-a-new-react-app.html">Create React App</a> to build our UI in React. CRA is an awesome collection of scripts that take care of bundling, transpiling, and all the boilerplate code you might need setup a React project. This way we can get straight to coding ! </p>
<h2 id="heading-setting-up-the-project">Setting up the project</h2>
<p>For this tutorial, our UI Will live in the same project than our API. In real life though, you'd probably want to have a separate repository. From the root of the project run the following command to create the UI : </p>
<ul>
<li><p><code>yarn create react-app ui --template typescript</code></p>
<p>OR if you prefer npm</p>
</li>
<li><p><code>npx create-react-app ui --template typescript</code></p>
</li>
</ul>
<p>Note: We'll be using <a target="_blank" href="https://www.typescriptlang.org/">typescript</a> for this tutorial. Don't worry you don't need to have a deep understanding of types to follow along tho, we'll stay pretty basic ! This will mainly prevent us to make mistakes when using data coming from the API.</p>
<p>We'll also need the following libraries to build our UI: </p>
<ul>
<li><a target="_blank" href="https://github.com/axios/axios">Axios</a>: An awesome library to make requests. </li>
<li><a target="_blank" href="https://reactrouter.com/">React Router</a>: For client side navigation</li>
<li><a target="_blank" href="https://mui.com/">react-query</a>: Painless data synchronization with the server</li>
<li><a target="_blank" href="https://mui.com/components/lists/">Material UI</a>: Not necessary, but great to quickly prototype something if youd don't have any design skills. (Like me 👌)</li>
</ul>
<p>Note: None of these are <em>strictly</em> necessary, but this is of my go to setup when I need to quicly build a small SPA. I must say I'm pretty satisfied with it, but if you have any feedback <a target="_blank" href="https://twitter.com/home">Reach out on Twitter</a> 🐦! </p>
<p>Our project is ready. Without further ado let's dive in ! </p>
<center>

<img src="https://media.giphy.com/media/dUHdTk3tvry9NETa67/giphy.gif" alt="Bring it on" />

<em>I will!</em>
</center>


<h2 id="heading-setting-up-react-query">Setting up react-query</h2>
<p>We'll start by setting up react-query. React query allows to define a <a target="_blank" href="https://react-query.tanstack.com/guides/default-query-function">default query function</a>. As we'll only be using <code>useQuery</code> to communicate with our API, we'll  set it ot use Axios's GET function. That way we can use our endpoints URLs, both as <a target="_blank" href="https://react-query.tanstack.com/reference/useQuery#_top">query keys</a> and argument for axios. </p>
<p>I like to put my query function in an <code>utils</code> folder like so:</p>
<pre><code class="lang-typescript">
<span class="hljs-comment">// utils/queryFn.ts</span>

<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;

<span class="hljs-comment">// We use the built-in QueryFunction type from `react-query` so we don't have to set it up oursevle</span>
<span class="hljs-keyword">import</span> { QueryFunction } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-query"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> queryFn: QueryFunction = <span class="hljs-keyword">async</span> ({ queryKey }) =&gt; {
  <span class="hljs-comment">// In a production setting the host would be remplaced by an environment variable</span>
  <span class="hljs-keyword">const</span> { data } = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">`http://localhost:80/<span class="hljs-subst">${queryKey[<span class="hljs-number">0</span>]}</span>`</span>);
  <span class="hljs-keyword">return</span> data;
};
</code></pre>
<p>Then we just need to configure the QueryClient to use our default function: </p>
<pre><code class="lang-typescript">
<span class="hljs-comment">// index.tsx</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> ReactDOM <span class="hljs-keyword">from</span> <span class="hljs-string">"react-dom"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./index.css"</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"./App"</span>;
<span class="hljs-keyword">import</span> reportWebVitals <span class="hljs-keyword">from</span> <span class="hljs-string">"./reportWebVitals"</span>;
<span class="hljs-keyword">import</span> { queryFn } <span class="hljs-keyword">from</span> <span class="hljs-string">"./utils/queryFn"</span>;
<span class="hljs-keyword">import</span> { QueryClient, QueryClientProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-query"</span>;

<span class="hljs-comment">// Configuring the queryclient to use</span>
<span class="hljs-comment">// our query function</span>
<span class="hljs-keyword">const</span> queryClient = <span class="hljs-keyword">new</span> QueryClient({
  defaultOptions: {
    queries: {
      queryFn: queryFn,
    },
  },
});

ReactDOM.render(
  &lt;React.StrictMode&gt;
    &lt;QueryClientProvider client={queryClient}&gt;
      &lt;App /&gt;
    &lt;/QueryClientProvider&gt;
  &lt;/React.StrictMode&gt;,
  <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"root"</span>)
);
</code></pre>
<h2 id="heading-setting-up-react-router">Setting up react router</h2>
<p>We also need to setup ou client side routing. As explained in the introduction, we'll create two routes: The Poll index and the Poll details. For now we'll just put some placeholder in there until we get to building the actual views in the next section 😄! </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { BrowserRouter, Routes, Route } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;
<span class="hljs-keyword">import</span> PollIndex <span class="hljs-keyword">from</span> <span class="hljs-string">"routes/Poll"</span>;
<span class="hljs-keyword">import</span> Results <span class="hljs-keyword">from</span> <span class="hljs-string">"routes/Poll/Results"</span>;

<span class="hljs-keyword">import</span> CssBaseline <span class="hljs-keyword">from</span> <span class="hljs-string">"@mui/material/CssBaseline"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"App"</span>&gt;
      &lt;CssBaseline /&gt;
      &lt;BrowserRouter&gt;
        &lt;Routes&gt;
          &lt;Route path=<span class="hljs-string">"/"</span> element={&lt;div&gt;Poll Index&lt;<span class="hljs-regexp">/div&lt;}&gt;&lt;/</span>Route&gt;
          &lt;Route path=<span class="hljs-string">":questionId/"</span> element={&lt;div&gt;Poll Form&lt;<span class="hljs-regexp">/div&lt;} /</span>&gt;
          &lt;Route path=<span class="hljs-string">":questionId/results/"</span> element={&lt;div&gt;Poll Results&lt;<span class="hljs-regexp">/div&lt;} /</span>&gt;
        &lt;/Routes&gt;
      &lt;/BrowserRouter&gt;
    &lt;/div&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Now launch the app with <code>yarn start</code> and both routes should become available!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638712757443/Snlmu2A-x.png?auto=compress" alt="The routes" /></p>
<p>Now all that's left to do is to build a <code>PollIndex</code> and <code>PollResult</code> components to replace the placeholders! These components will be responsible for querying the API using <code>react-query</code> and display the results ! </p>
<h2 id="heading-building-the-poll-index">Building the Poll index</h2>
<p>We'll start building the Poll Index. We want to list all the existing polls, and maybe make them link to the corresponding Form while we're at it !</p>
<center>

<img src="https://media.giphy.com/media/0VxdFBlV73nIWeVSH7/giphy.gif" alt="The time has comme" />

</center>

<p>... (To Lip-Sync FOR YOUR LIFE!) to query our endpoints with <code>useQuery</code>!  </p>
<h3 id="heading-types-definition">Types definition</h3>
<p>First, since we're using typescript, we need to describe the type we expect to receive from our API. That's where FastAPI automatic documentation really shines in my opinion. When you - or others - want to build something that interface with our API (which should be expected when working on an Application Programming <em>Interface</em>), all you have to do is consult the <code>/docs</code> endpoint. </p>
<p>Let's have a look to both our endpoints: </p>
<p>Here's the documented respone shape for <code>/polls/</code></p>
<center>

<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638628547064/HyoxEp3qE.png?auto=compress" alt="Poll endpoint response" />

</center>

<p>And the one for <code>/polls/{id}</code>:</p>
<center>

<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638628618152/SSEyaZMU0.png?auto=compress" alt="Poll detail response" />

</center>

<p>Pretty straightforward, le's translate that into typescript, and we'll be guaranteed to communicate with our API correctly! Here's the types we'll be workgin with:</p>
<pre><code class="lang-typescript">

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> Choice {
  id: <span class="hljs-built_in">number</span>;
  choice_text: <span class="hljs-built_in">string</span>;
  votes: <span class="hljs-built_in">number</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> Question {
  id: <span class="hljs-built_in">number</span>;
  pub_date: <span class="hljs-built_in">string</span>;
  question_text: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> QuestionResults <span class="hljs-keyword">extends</span> Question {
  choices: Choice[];
}
</code></pre>
<p>We're done with typescript !</p>
<p>Now, I like to put all my pages components inside a <code>routes</code> folder and then mimick the actual route structure of the app. With the latest version of <a target="_blank" href="https://reactrouter.com/docs/en/v6/upgrading/v5">react-router out</a>, I need to check what the current best practices are though!</p>
<p>Create <code>routes/Poll/index.ts</code>, with the following Implementation:</p>
<pre><code class="lang-typescript">
<span class="hljs-comment">//Poll/index.ts</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-comment">// The type we've just defined</span>
<span class="hljs-keyword">import</span> { Question } <span class="hljs-keyword">from</span> <span class="hljs-string">"types"</span>;
<span class="hljs-keyword">import</span> { useQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-query"</span>;

<span class="hljs-comment">// Routing</span>
<span class="hljs-keyword">import</span> { Link} <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;


<span class="hljs-comment">// Material ui stuff</span>
<span class="hljs-keyword">import</span> { styled } <span class="hljs-keyword">from</span> <span class="hljs-string">"@mui/material/styles"</span>;
<span class="hljs-keyword">import</span> Card <span class="hljs-keyword">from</span> <span class="hljs-string">"@mui/material/Card"</span>;
<span class="hljs-keyword">import</span> Typography <span class="hljs-keyword">from</span> <span class="hljs-string">"@mui/material/Typography"</span>;
<span class="hljs-keyword">import</span> Container <span class="hljs-keyword">from</span> <span class="hljs-string">"@mui/material/Container"</span>;
<span class="hljs-keyword">import</span> Box <span class="hljs-keyword">from</span> <span class="hljs-string">"@mui/material/Box"</span>;
<span class="hljs-keyword">import</span> Page <span class="hljs-keyword">from</span> <span class="hljs-string">"components/template/Page"</span>;

<span class="hljs-keyword">const</span> StyledLink = styled(Link)<span class="hljs-string">`
  text-decoration: none;
`</span>;

<span class="hljs-keyword">const</span> PollIndex: React.FunctionComponent = <span class="hljs-function">() =&gt;</span> {

  <span class="hljs-comment">// Syncing our data</span>
  <span class="hljs-keyword">const</span> { data: questions, isSuccess } = useQuery&lt;Question[]&gt;(<span class="hljs-string">"polls/"</span>);

  <span class="hljs-comment">// In real life we should handle isError and isLoading</span>
  <span class="hljs-comment">// displaying an error message or a loading animation as required. </span>
  <span class="hljs-comment">// This will do for our tutorial</span>
  <span class="hljs-keyword">if</span> (!isSuccess) {
    <span class="hljs-keyword">return</span> &lt;div&gt; no questions &lt;/div&gt;;
  }

  <span class="hljs-keyword">return</span> (
    &lt;Page title=<span class="hljs-string">"Index"</span>&gt;
      &lt;Container maxWidth=<span class="hljs-string">"sm"</span>&gt;
        {questions?.map(<span class="hljs-function">(<span class="hljs-params">question</span>) =&gt;</span> (
          &lt;Box marginY={<span class="hljs-number">2</span>}&gt;
            &lt;StyledLink to={<span class="hljs-string">`<span class="hljs-subst">${question.id}</span>/results/`</span>}&gt;
              &lt;Card key={question.id}&gt;
                &lt;Typography color=<span class="hljs-string">"primary"</span> gutterBottom variant=<span class="hljs-string">"h3"</span>&gt;
                  {question.question_text}
                &lt;/Typography&gt;
              &lt;/Card&gt;
            &lt;/StyledLink&gt;
          &lt;/Box&gt;
        ))}
        &lt;Outlet /&gt;
      &lt;/Container&gt;
    &lt;/Page&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> PollIndex;
</code></pre>
<p>And then replace the placeholder in <code>App.tsx</code>: </p>
<pre><code class="lang-typescript">
<span class="hljs-comment">// App.tsx</span>


<span class="hljs-keyword">import</span> PollIndex <span class="hljs-keyword">from</span> <span class="hljs-string">"routes/Poll"</span>;

...

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
  ...
  &lt;Route&gt;
    ...

    &lt;Route path=<span class="hljs-string">"/"</span> element={&lt;PollIndex /&gt;}&gt;&lt;/Route&gt;
  &lt;/Routes&gt;
  )
}
</code></pre>
<p>The most important bit here is <code>const { data: questions, isSuccess } = useQuery&lt;Question[]&gt;("polls/");</code>. As you can see I'am passing the <code>useQuery</code> hook the expected type of our response. Otherwise <code>data</code> would be of type <code>unkown</code> and we don't want that ! </p>
<p>For the rest, displaying the list of question is as easy a mapping over the query results. Let's see how that looks:</p>
<center>

<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638629389333/zJka6fykW.png?auto=compress" alt="Poll index" />

</center>


<p>Not bad uh ? </p>
<center>

<img src="https://media.giphy.com/media/p8GJOXwSNzQPu/giphy.gif" alt="It's beautiful" />

 <em>Now, now, no need to cry</em> 

</center>

<p>We'll build the Details view using exactly the same method ! </p>
<h2 id="heading-building-the-detail-page">Building the detail page</h2>
<p>This one will live next to the <code>Polls/index.tsx</code> page, let's call it <code>Polls/Details.tsx</code>. This time, as this page will be accessed at <code>polls/&lt;poll_id&gt;</code> we'll use the <code>useParam</code> hook from <code>reat-router-dom</code> to retrieve the id, and pass it to our API. Like so : </p>
<pre><code class="lang-typescript">
<span class="hljs-comment">// Detail.tsx</span>

<span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-comment">// types</span>
<span class="hljs-keyword">import</span> { QuestionResults } <span class="hljs-keyword">from</span> <span class="hljs-string">"types"</span>;

<span class="hljs-comment">// routing</span>
<span class="hljs-keyword">import</span> { useParams } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-comment">// querying</span>
<span class="hljs-keyword">import</span> { useQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-query"</span>;


<span class="hljs-comment">// Material ui stuff</span>
<span class="hljs-keyword">import</span> Card <span class="hljs-keyword">from</span> <span class="hljs-string">"@mui/material/Card"</span>;
<span class="hljs-keyword">import</span> Page <span class="hljs-keyword">from</span> <span class="hljs-string">"components/template/Page"</span>;
<span class="hljs-keyword">import</span> Chip <span class="hljs-keyword">from</span> <span class="hljs-string">"@mui/material/Chip"</span>;
<span class="hljs-keyword">import</span> CardContent <span class="hljs-keyword">from</span> <span class="hljs-string">"@mui/material/CardContent"</span>;
<span class="hljs-keyword">import</span> CardHeader <span class="hljs-keyword">from</span> <span class="hljs-string">"@mui/material/CardHeader"</span>;
<span class="hljs-keyword">import</span> CardActionArea <span class="hljs-keyword">from</span> <span class="hljs-string">"@mui/material/CardActionArea"</span>;
<span class="hljs-keyword">import</span> Typography <span class="hljs-keyword">from</span> <span class="hljs-string">"@mui/material/Typography"</span>;
<span class="hljs-keyword">import</span> Grid <span class="hljs-keyword">from</span> <span class="hljs-string">"@mui/material/Grid"</span>;


<span class="hljs-keyword">const</span> Details = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { questionId } = useParams();

  <span class="hljs-comment">// This state variable controls</span>
  <span class="hljs-comment">// displaying the results</span>
  <span class="hljs-keyword">const</span> [hasVoted, setHasVoted] = useState(<span class="hljs-literal">false</span>);

  <span class="hljs-comment">// We can use the id from use param</span>
  <span class="hljs-comment">// directly with the useQuery hook</span>
  <span class="hljs-keyword">const</span> questionQuery = useQuery&lt;QuestionResults&gt;(<span class="hljs-string">`polls/<span class="hljs-subst">${questionId}</span>/`</span>);

  <span class="hljs-keyword">if</span> (!questionQuery.isSuccess) {
    <span class="hljs-keyword">return</span> &lt;div&gt; loading &lt;/div&gt;;
  }

  <span class="hljs-keyword">return</span> (
    &lt;Page title={questionQuery.data.question_text}&gt;
      &lt;Grid spacing={<span class="hljs-number">2</span>} container&gt;
        &lt;Grid item xs={<span class="hljs-number">12</span>}&gt;
          &lt;Typography variant=<span class="hljs-string">"h2"</span>&gt;
            {questionQuery.data.question_text}
          &lt;/Typography&gt;
        &lt;/Grid&gt;
        {questionQuery.data.choices.map(<span class="hljs-function">(<span class="hljs-params">choice</span>) =&gt;</span> (
          &lt;Grid item xs={<span class="hljs-number">12</span>} md={<span class="hljs-number">6</span>}&gt;
            &lt;Card key={choice.id}&gt;
              &lt;CardActionArea onClick={<span class="hljs-function">() =&gt;</span> setHasVoted(<span class="hljs-literal">true</span>)}&gt;
                &lt;CardHeader title={choice.choice_text}&gt;&lt;/CardHeader&gt;
                &lt;CardContent&gt;
                  {hasVoted &amp;&amp; &lt;Chip label={choice.votes} color=<span class="hljs-string">"success"</span> /&gt;}
                &lt;/CardContent&gt;
              &lt;/CardActionArea&gt;
            &lt;/Card&gt;
          &lt;/Grid&gt;
        ))}
      &lt;/Grid&gt;
    &lt;/Page&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Details;
</code></pre>
<p>That's it! Look pretty much the same as the index, we're juste using <code>map</code> over the choices of a specific poll to display them. The results display are controlled using
a simple <code>useState</code> hook. However if this data was really sensitive, we'd have to restrict access to it on the server as well !</p>
<p>Just replace the placeholder in <code>App.tsx</code> and admire the result ! </p>
<pre><code class="lang-typescript">
<span class="hljs-comment">// App.tsx</span>


<span class="hljs-keyword">import</span> PollDetails <span class="hljs-keyword">from</span> <span class="hljs-string">"routes/Poll/Details"</span>;

...

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
  ...
  &lt;Route&gt;
    ...

    &lt;Route path=<span class="hljs-string">"/"</span> element={&lt;PollIndex /&gt;}&gt;&lt;/Route&gt;
    &lt;Route path=<span class="hljs-string">"/"</span> element={&lt;PollDetails /&gt;}&gt;&lt;/Route&gt;
  &lt;/Routes&gt;
  )
}
</code></pre>
<center>

<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638632086531/L3YqjDcfh.gif?auto=compress" alt="Detail results" />

 <em>A very scientific survey I made</em> 

</center>


<p>Looks great !</p>
<h2 id="heading-thanks-for-reading">Thanks for reading !</h2>
<p>That's a wrap for part 4! Hope you liked it, next time we'll see how to actually submit the vote to our API and save it to the database! 😃</p>
<p>As always if you have any question you can reach out to me on <a target="_blank" href="https://twitter.com/JiBRocher">Twitter</a> 🐦!</p>
<h2 id="heading-references">References</h2>
<ol>
<li><a target="_blank" href="https://react-query.tanstack.com/overview">react-query</a></li>
<li><a target="_blank" href="https://reactrouter.com/">react-router</a></li>
<li><a target="_blank" href="https://fastapi.tiangolo.com/">FastAPI</a></li>
</ol>
]]></content:encoded></item><item><title><![CDATA[React Tips & Tricks: Uploading a File With A Progress Bar]]></title><description><![CDATA[Forms are often tricky to get right with React. While there are great libraries like formik or React Final Form to do the heavy lefting for us, handling file upload still isn't always straightforward. 
In today's episode of React Tips & Tricks, we'll...]]></description><link>https://dev.indooroutdoor.io/react-tips-and-tricks-uploading-a-file-with-a-progress-bar</link><guid isPermaLink="true">https://dev.indooroutdoor.io/react-tips-and-tricks-uploading-a-file-with-a-progress-bar</guid><category><![CDATA[React]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Jb Rocher]]></dc:creator><pubDate>Mon, 29 Nov 2021 17:07:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1638205620525/PTRb9M6oZ.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Forms are often tricky to get right with React. While there are great libraries like <a target="_blank" href="https://formik.org/">formik</a> or <a target="_blank" href="https://github.com/final-form/react-final-form">React Final Form</a> to do the heavy lefting for us, handling file upload still isn't always straightforward. </p>
<p>In today's episode of React Tips &amp; Tricks, we'll see how to handle and submit file Data, and how to display a progress bar !</p>
<h2 id="heading-a-basic-form">A basic Form</h2>
<p>Let's say we need to build a form to create blog posts, with an <code>input</code> for the title, and a <code>textarea</code> for the body.</p>
<p>Here's a simple implementation for such a form, using <a target="_blank" href="https://mui.com/">Material UI</a> for the basic components: </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> Box <span class="hljs-keyword">from</span> <span class="hljs-string">"@mui/material/Box"</span>;
<span class="hljs-keyword">import</span> TextField <span class="hljs-keyword">from</span> <span class="hljs-string">"@mui/material/TextField"</span>;
<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"@mui/material/Button"</span>;

<span class="hljs-keyword">interface</span> PostData {
  title: <span class="hljs-built_in">string</span>;
  body: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">const</span> Form: React.FunctionComponent = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [formValues, setFormValues] = useState&lt;PostData&gt;({
    title: <span class="hljs-string">""</span>,
    body: <span class="hljs-string">""</span>,
  });

  <span class="hljs-comment">// Handlers for the input</span>
  <span class="hljs-keyword">const</span> handleTitleChange = <span class="hljs-function">(<span class="hljs-params">event: React.ChangeEvent&lt;HTMLInputElement&gt;</span>) =&gt;</span> {
    setFormValues(<span class="hljs-function">(<span class="hljs-params">prevFormValues</span>) =&gt;</span> ({
      ...prevFormValues,
      title: event.target.value,
    }));
  };

  <span class="hljs-keyword">const</span> handleBodyChange = <span class="hljs-function">(<span class="hljs-params">event: React.ChangeEvent&lt;HTMLInputElement&gt;</span>) =&gt;</span> {
    setFormValues(<span class="hljs-function">(<span class="hljs-params">prevFormValues</span>) =&gt;</span> ({
      ...prevFormValues,
      body: event.target.value,
    }));
  };

  <span class="hljs-keyword">return</span> (
    &lt;Box
      display=<span class="hljs-string">"flex"</span>
      height=<span class="hljs-string">"100%"</span>
      flexDirection=<span class="hljs-string">"column"</span>
      justifyContent=<span class="hljs-string">"center"</span>
      alignItems=<span class="hljs-string">"center"</span>
    &gt;
      &lt;Box marginY={<span class="hljs-number">2</span>}&gt;
        &lt;TextField
          onChange={handleTitleChange}
          value={formValues.title}
          label=<span class="hljs-string">"Post Title"</span>
          name=<span class="hljs-string">"title"</span>
        /&gt;
      &lt;/Box&gt;
      &lt;Box marginY={<span class="hljs-number">2</span>}&gt;
        &lt;TextField
          onChange={handleBodyChange}
          multiline
          minRows={<span class="hljs-number">5</span>}
          label=<span class="hljs-string">"Post Body"</span>
          name=<span class="hljs-string">"body"</span>
        /&gt;
      &lt;/Box&gt;
      &lt;Box marginY={<span class="hljs-number">3</span>}&gt;
        &lt;Button onClick={<span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"submit"</span>)}&gt;Submit Post &lt;/Button&gt;
      &lt;/Box&gt;
    &lt;/Box&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Form;
</code></pre>
<p><em>Note: I'm not using any Form libraries here, as I want to focus on file handling. In a production setting I'd really recommend using somethign like <a target="_blank" href="https://formik.org/docs/api/formik">Formik</a> to avoid re-inventing the wheel!</em></p>
<p>This works like a charm, and renders the following output: </p>
<center>

<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636902278730/212jbOdu_.png?auto=compress" alt="Post Form" />

</center>

<p>Great! But now say we also want to submit an image along with the title and the body, to serve as a cover for the article. This is a bit more complicated as we're not juste maniuplating strings anymore.</p>
<h2 id="heading-adding-an-image-to-the-post">Adding an image to the post</h2>
<p>In order to be able to submit an image, we need to add 3 things to our Form : </p>
<ul>
<li>A button to upload a file from the client's computer;</li>
<li>A way to handle the file and store it in the sate;</li>
<li>A handler to submit our form;</li>
</ul>
<p>Let's dive in ! </p>
<h3 id="heading-adding-the-button">Adding the button</h3>
<p>To add a file upload button to the form, we use an <code>input</code> of type <code>file</code>, wrapped in a <code>Button</code> component :</p>
<pre><code class="lang-JSX">  <span class="hljs-comment">//Form.tsx</span>

<span class="hljs-keyword">const</span> Form: React.FunctionComponent = <span class="hljs-function">() =&gt;</span> {

  ...

  return (
    ...

    &lt;Box marginY={<span class="hljs-number">2</span>}&gt;
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TextField</span>
        <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleBodyChange}</span>
        <span class="hljs-attr">multiline</span>
        <span class="hljs-attr">minRows</span>=<span class="hljs-string">{5}</span>
        <span class="hljs-attr">label</span>=<span class="hljs-string">"Post Body"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"body"</span>
      /&gt;</span></span>
    &lt;/Box&gt;

    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">variant</span>=<span class="hljs-string">"contained"</span> <span class="hljs-attr">component</span>=<span class="hljs-string">"label"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-attr">hidden</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Button</span>&gt;</span></span>

    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Box</span> <span class="hljs-attr">marginY</span>=<span class="hljs-string">{3}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> console.log("submit")}&gt;Submit Post <span class="hljs-tag">&lt;/<span class="hljs-name">Button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Box</span>&gt;</span></span>
  )
}
</code></pre>
<p> Here we leverage the fact that a label (Here rendered as a Button) is <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label">programmatically</a> linked to its input. Meaning, any click event on our "Button" component will be passed to the hidden input. This trick allows us to display any component we want to the user, while still benefiting fro the built-in file handling system.</p>
<h3 id="heading-controlling-the-component">Controlling the component</h3>
<p> For now our input is <a target="_blank" href="https://reactjs.org/docs/uncontrolled-components.html">uncontrolled</a>: it's not linked to any state variable, so we can't declaratively use its value when submitting the form. We need to change that : </p>
 <center>

 <img src="https://media.giphy.com/media/YO3icZKE2G8OoGHWC9/giphy.gif" alt="Give me Control" />

  <em>I agree with Dwight!</em> 

 </center>


<p> To control our input, as with a normal input, we need to pass it a handler. This handler uses the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/File">File API</a> to retrieve the fiels data we interested in: </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">interface</span> PostData {
  title: <span class="hljs-built_in">string</span>;
  body: <span class="hljs-built_in">string</span>;
  image: File | <span class="hljs-literal">null</span>;
}

<span class="hljs-keyword">const</span> Form: React.FunctionComponent = <span class="hljs-function">() =&gt;</span> {

  <span class="hljs-comment">// Add an image attribute</span>
  <span class="hljs-comment">// to our formData</span>
  <span class="hljs-keyword">const</span> [formValues, setFormValues] = useState&lt;PostData&gt;({
    title: <span class="hljs-string">""</span>,
    body: <span class="hljs-string">""</span>,
    image: <span class="hljs-literal">null</span>,
  });
  ...

  <span class="hljs-comment">// Set up the handler</span>
  <span class="hljs-keyword">const</span> handleImageChange = <span class="hljs-function">(<span class="hljs-params">event: React.ChangeEvent&lt;HTMLInputElement&gt;</span>) =&gt;</span> {
    setFormValues(<span class="hljs-function">(<span class="hljs-params">prevFormValues</span>) =&gt;</span> ({
      ...prevFormValues,
      image: event.target.files ? event.target.files[<span class="hljs-number">0</span>] : <span class="hljs-literal">null</span>,
    }));
  };

  ...


<span class="hljs-keyword">return</span> (
    ...
      &lt;Button variant=<span class="hljs-string">"contained"</span> component=<span class="hljs-string">"label"</span>&gt;
        {formValues.image?.name ?? <span class="hljs-string">"Upload File"</span>}
        {<span class="hljs-comment">/* Bind the handler to the input */</span>}
        &lt;input onChange={handleImageChange} <span class="hljs-keyword">type</span>=<span class="hljs-string">"file"</span> hidden /&gt;
      &lt;/Button&gt;
    ...
  )
}
</code></pre>
<p>Now when the user uploads an image using our button, the <code>image</code> attribute will be populated with a File object. This object has a lot of useful
<a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/File">properties</a>, like the name of the file, and its type. We can use them to display the name file currently selected by the user inside our button. Also note that <code>target.files</code> is an <strong>array</strong>. Here we're only interested in the first value as we're only uploading one file, but the same method can be used with multiple files !</p>
<center>

<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636904350691/JxJAKcamS.png?auto=compress" alt="showing the file name in a button" />

</center>

<h3 id="heading-form-submission">Form submission</h3>
<p>Finally, we need a way to submit the data. For testing purposes I've created a small API in Flask you can find it in the <a target="_blank" href="https://github.com/jbrocher/file-upload-progress-bar">repository</a> for this article. It's just a single endpoint that listens for POST requests and returns a 201.</p>
<p>Now, we can't POST our Data as json because we're want to send a file and json doesn't handle binary data. We need to send <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/FormData">form-data</a> instead. We'll use <a target="_blank" href="https://github.com/axios/axios">axios</a> to send the request, as it comes in handy to display the progress as we'll see in the next section.</p>
<p><strong>Note</strong>: <em>Alternatively, we could <a target="_blank" href="https://stackoverflow.com/questions/6150289/how-can-i-convert-an-image-into-base64-string-using-javascript">encode our image in BASE64</a> and send it as a string in the json payload. Of course in that case we'd also need to decode it in the backend.</em></p>
<pre><code class="lang-typescript">
  <span class="hljs-keyword">const</span> handleSubmit = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> formData = <span class="hljs-keyword">new</span> FormData();
    formData.append(<span class="hljs-string">"title"</span>, formValues.title);
    formData.append(<span class="hljs-string">"body"</span>, formValues.body);
    formValues.image &amp;&amp; formData.append(<span class="hljs-string">"image"</span>, formValues.image);

    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.post(&lt;YOUR-API-ENDPOINT&gt;, formData, {
      headers: {
        <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"multipart/form-data"</span>,
      },
    });

    <span class="hljs-keyword">return</span> response.data

  };
</code></pre>
<p>Several things are happening here : </p>
<ul>
<li>First we create a new <code>FormData</code> object;</li>
<li>Then we add our fomvalues to the data;</li>
<li>Finally we post it to our endpoint using the correct content headers</li>
</ul>
<h2 id="heading-showing-progress">Showing progress</h2>
<p>Our form submisssion is working hooray ! But we're not done yet ! </p>
<center>

<img src="https://media.giphy.com/media/JNrWNUUNZlIhG/giphy.gif" alt="Hooray" />

</center>

<p>Maybe the image our user will posting are going to be heavy, and maybe we'll do some slow processing server side too. 
As it's probably gonna take some times to process the request, we'd like to show a progress bar.  </p>
<p>That's where Axios saves the day! It comes with two built-ins callback hook to process progress data: </p>
<ul>
<li><code>onUploadProgress</code>: send event during the upload phase;</li>
<li><code>onDownloadProgress</code>: during the download phase;</li>
</ul>
<p>Now all we have to do is to create a new state variable to stor the progress value and monitor the requests states ! Might as well write this logic in a custom hook, as
we'll probably want to reuse it later. (It's also easier to read). Here's how this looks : </p>
<pre><code class="lang-typescript">
<span class="hljs-comment">// hooks.ts</span>

<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useUploadForm = <span class="hljs-function">(<span class="hljs-params">url: <span class="hljs-built_in">string</span></span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> [isSuccess, setIsSuccess] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [progress, setProgress] = useState(<span class="hljs-number">0</span>);

  <span class="hljs-keyword">const</span> uploadForm = <span class="hljs-keyword">async</span> (formData: FormData) =&gt; {
    setIsLoading(<span class="hljs-literal">true</span>);
    <span class="hljs-keyword">await</span> axios.post(url, formData, {
      headers: {
        <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"multipart/form-data"</span>,
      },
      onUploadProgress: <span class="hljs-function">(<span class="hljs-params">progressEvent</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> progress = (progressEvent.loaded / progressEvent.total) * <span class="hljs-number">50</span>;
        setProgress(progress);
      },
      onDownloadProgress: <span class="hljs-function">(<span class="hljs-params">progressEvent</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> progress = <span class="hljs-number">50</span> + (progressEvent.loaded / progressEvent.total) * <span class="hljs-number">50</span>;
        <span class="hljs-built_in">console</span>.log(progress);
        setProgress(progress);
      },
    });
    setSuccess(<span class="hljs-literal">true</span>)
  };

  <span class="hljs-keyword">return</span> { uploadForm, isSuccess, progress };
};
</code></pre>
<p>Here I made the choice to represent the progress as evenly distributed between the uplaod and download steps, but you're free to do as you please ! It all depends on what you 
want to display to your users. I've also added <code>success</code> boolean we can use to do some conditionnal rendering. </p>
<p>Now all we have to do is use our custom hook to submit the form, and somehow display the progress value! I'm using <a target="_blank" href="https://mui.com/api/linear-progress/">linear progress</a> for thatfrom Material UI here.</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> Form: React.FunctionComponent = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { isSuccess, uploadForm, progress } = useUploadForm(
    <span class="hljs-string">"http://localhost:5000/post"</span>
  );
  ...

  <span class="hljs-keyword">const</span> handleSubmit = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> formData = <span class="hljs-keyword">new</span> FormData();
    formData.append(<span class="hljs-string">"title"</span>, formValues.title);
    formData.append(<span class="hljs-string">"body"</span>, formValues.body);
    formValues.image &amp;&amp; formData.append(<span class="hljs-string">"image"</span>, formValues.image);
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> uploadForm(formData);
  };

}

...

<span class="hljs-keyword">const</span> Form: React.FunctionComponent = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (

    ...

    &lt;Box marginY={<span class="hljs-number">3</span>}&gt;
      &lt;Button onClick={handleSubmit}&gt;Submit Post &lt;/Button&gt;
      &lt;LinearProgress variant=<span class="hljs-string">"determinate"</span> value={progress} /&gt;
    &lt;/Box&gt;
  )
}
</code></pre>
<p>Here's what it looks like : </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636906969200/aYHOb-Czr.gif?auto=compress" alt="Progress Bar Demonstration" /></p>
<p>Pretty neat  !</p>
<h2 id="heading-bonus-round">Bonus Round !</h2>
<p>I thought it would be a nice addition to show how to display a little success message after the bar reach 100%.</p>
<p>To do so we'll use our <code>isSuccess</code> indicator. But first well add an artificial pause after the request complete to let he user
admire the progress bar reaching 100%. Otherwise React will merge the states updates and dipslay the success message before the progress bar has finished animating.</p>
<pre><code class="lang-typescript">
<span class="hljs-comment">//hooks.ts</span>

  <span class="hljs-keyword">const</span> uploadForm = <span class="hljs-keyword">async</span> (formData: FormData) =&gt; {

    ...

    <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve</span>) =&gt;</span> {
      <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> resolve(<span class="hljs-string">"success"</span>), <span class="hljs-number">500</span>);
    });
    setIsSuccess(<span class="hljs-literal">true</span>);
    setProgress(<span class="hljs-number">0</span>);
  };
</code></pre>
<p>And now using <code>isSuccess</code> we can conditionnaly render a success message : </p>
<pre><code class="lang-typescript">

{ isSuccess ? (
  &lt;Box color=<span class="hljs-string">"success.main"</span> display=<span class="hljs-string">"flex"</span>&gt;
    &lt;CheckIcon color=<span class="hljs-string">"success"</span> /&gt;
    &lt;Typography&gt;Success&lt;/Typography&gt;
  &lt;/Box&gt;
  ) : (
  &lt;&gt;
    &lt;Button onClick={handleSubmit}&gt;Submit Post &lt;/Button&gt;
    &lt;LinearProgress variant=<span class="hljs-string">"determinate"</span> value={progress} /&gt;
  &lt;/&gt;
)}
</code></pre>
<center>

<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636908105155/WSu_9b6N_.gif?auto=compress" alt="Success message" />

</center>


<h2 id="heading-thanks-for-reading">Thanks for reading !</h2>
<p>That's it for today, hope you learned something ! Form handling in React is not easy, as they are so many ways to do it, and so many ways it could go wrong. All the more reason
to keep trying and learning !</p>
<p><strong>Hungry for more React tips ?</strong> ➡️  <a target="_blank" href="https://twitter.com/JiBRocher">Follow Me on Twitter !</a></p>
<h2 id="heading-references">References</h2>
<ol>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/File">File API documentation</a></li>
<li><a target="_blank" href="https://github.com/axios/axios#axios-api">Axios</a></li>
<li><a target="_blank" href="https://mui.com/api/linear-progress/">Material Linear Progress Bar</a></li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Re-Doing the Django Tutorial With FastAPI And React: Building the CRUD endpoints!]]></title><description><![CDATA[Note: This is part 3 of a multi-part tutorial on FastApi and React, if you've not read the previous part, start here !
Welcome to part 3 of the tutorial, today we'll see how to use path operations to return information from our Database. It's CRUD ti...]]></description><link>https://dev.indooroutdoor.io/re-doing-the-django-tutorial-with-fastapi-and-react-building-the-crud-endpoints</link><guid isPermaLink="true">https://dev.indooroutdoor.io/re-doing-the-django-tutorial-with-fastapi-and-react-building-the-crud-endpoints</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Python]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Django]]></category><category><![CDATA[backend]]></category><dc:creator><![CDATA[Jb Rocher]]></dc:creator><pubDate>Mon, 22 Nov 2021 07:21:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1637503655481/SKzsrog5u.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Note: This is part 3 of a multi-part tutorial on FastApi and React, if you've not read the previous part, start <a target="_blank" href="https://dev.indooroutdoor.io/series/fastapi-react-poll-app">here</a> !</em></p>
<p>Welcome to part 3 of the tutorial, today we'll see how to use path operations to return information from our Database. It's CRUD time ! We'll start with R though ! 😋</p>
<p>Last time in part 2, we set up our Postgres Database, wrote the models and generated the associated migrations. Today we'll pick up where we left off and start doing something useful with our endpoints: returning some information stored in the DB. </p>
<p>As in the Django tutorial we'll display the list of existing polls, and the associated results. There is one key difference though. Django being a <a target="_blank" href="https://en.wikipedia.org/wiki/Monolithic_application">monolithic Framework</a>, it handles both querying the database AND rendering the resulting information to the client using an HTML templating language. FastAPI, however is only concerned with the API side of things, meaning we'll have to build the UI separately. </p>
<p>We'll use React and the excellent <a target="_blank" href="https://reactjs.org/docs/create-a-new-react-app.html">create-react-app</a> to build the UI. I initially did both - The CRUD and the UI - in this article, but it ended up being super long so I decided to split it. I'll post the React part later this week so stay tuned ! 🚀</p>
<p>On Today's Menu: </p>
<ul>
<li>Creating some CRUD utilities</li>
<li>Using path operations to expose some information from our database</li>
</ul>
<h2 id="heading-crud">Crud</h2>
<p>It's time to rip the benefits of all the set up we did in part 2. Thanks to our previous work, our database if already fully, set up so run <code>docker-compose up</code> from the root of the project to spin up everything and let's get coding! The first thing we'll do is creating some crud utilities for reading from our database.  Well create two functions:</p>
<ul>
<li><code>get_question</code> to fetch a specific question based on an ID; </li>
<li><code>list_questions</code> to list all the existing polls</li>
</ul>
<p>Add a <code>crud.py</code> file in your poll app and write the following code in it:</p>
<pre><code class="lang-python">
 <span class="hljs-comment"># crud.py</span>

<span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> select
<span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> Session

<span class="hljs-keyword">from</span> polls <span class="hljs-keyword">import</span> models
<span class="hljs-keyword">from</span> polls <span class="hljs-keyword">import</span> schemas


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_question</span>(<span class="hljs-params">db: Session, question_id: int</span>):</span>
    question_query = select(models.Question).where(models.Question.id == question_id)
    question = db.execute(question_query).one().Question
    <span class="hljs-keyword">return</span> question


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">list_questions</span>(<span class="hljs-params">db: Session</span>):</span>
    question_query = select(models.Question)
    questions = db.execute(question_query).scalars().all()
    <span class="hljs-keyword">return</span> questions
</code></pre>
<p>He're we're using a Session object to communicate with the database using our orm, SQLAlchemy. We'll worry about passing a session to our functions later, for now let's just assume we have one. As you can see, SQLAlchemy once again is a bit more explicit in its use than <code>django-orm</code>. The querying syntax looks a lot like SQL, and that's what I like it so much: if you'realready comfortable working with SQL, learning to use SQLAlchemy is a breeze! </p>
<p><em>Yes I am aware I've written SQLAlchemy 3 times in 3 sentences, and here's another one for your trouble</em></p>
<p>For example <code>select(models.Question).where(models.Question.id == question_id)</code> is equivalent to the following SQL statement: </p>
<pre><code class="lang-sql">
  <span class="hljs-keyword">SELECT</span>
   * 
  <span class="hljs-keyword">FROM</span>
    polls_question 
  <span class="hljs-keyword">WHERE</span> polls_question.id = <span class="hljs-number">1</span>
</code></pre>
<p>The structure is almost exactly the same, the table name are simply replaced with our models name !</p>
<center>

<img src="https://media.giphy.com/media/3oEjHYibHwRL7mrNyo/giphy.gif" alt="pam nice" />

</center>

<p>The only thing that might not be obvious here is the way the related choices are fetched. In SQL land we'd need to perform the following join to 
get the choices associated with each question (and in so doing, creating duplicate questions in the results !). </p>
<pre><code class="lang-SQL">
<span class="hljs-keyword">SELECT</span> 
  *
<span class="hljs-keyword">FROM</span> 
  polls_question <span class="hljs-keyword">as</span> question
  <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> polls_choice <span class="hljs-keyword">as</span> choice <span class="hljs-keyword">on</span> choice.question_id = question.id
</code></pre>
<p>With SQLAlchemy, the <a target="_blank" href="https://docs.sqlalchemy.org/en/14/orm/relationships.html">relationship</a> will take care of that for us, as we'll see in the next section ! </p>
<h2 id="heading-pydantic-models">Pydantic models</h2>
<p>Now that our CRUD is ready, we'll also need a way to convert the results into  a format that can be send in an HTTP response. <a target="_blank" href="https://pydantic-docs.helpmanual.io/">Pydantic</a>  will take care of that for us ! We already used it in part 2 to load our environment variables, but it can do much more ! If you're familiar with DRF pydantic <em>models</em>, they fill the same functino as DRF's serializers:</p>
<ul>
<li>Deserializing and validating input from outside the API</li>
<li>Serializing and validating output from our endpoints</li>
</ul>
<p>Regarding validations errors, it's important to note that error raise when deserializing the data from a request will automatically rais a 422 HTTP error, while validation error when serializer the endpionts output will raise an unhandled exception. This is because a validation error when serializing data on which we have complete control point to a configuration error on our part, and not a user error.</p>
<p>One last thing before we dive in: pydantic like SQLAlchemy uses the term "model". This tends to make this kind of tutorial a bit confusing if it's your first time workng with both. Thus from now on, like in the Fastapi doc,  I will be refering to Pydantic models as <em>schemas</em>!</p>
<p>With this out of the way, let's write schemas we need to serialize the our CRUD's ouput. Add a <code>schemas.py</code> file to your poll app, and create the following <em>schemas</em> : </p>
<pre><code class="lang-python">
<span class="hljs-comment"># app/polls/schemas.py</span>

<span class="hljs-keyword">import</span> datetime
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> List

<span class="hljs-keyword">import</span> pydantic


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Choice</span>(<span class="hljs-params">pydantic.BaseModel</span>):</span>
    choice_text: str
    votes: int

    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span>:</span>
        orm_mode = <span class="hljs-literal">True</span>


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Question</span>(<span class="hljs-params">pydantic.BaseModel</span>):</span>

    id: int
    question_text: str
    pub_date: datetime.datetime

    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span>:</span>
        orm_mode = <span class="hljs-literal">True</span>


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ReadQuestionChoices</span>(<span class="hljs-params">ReadQuestion</span>):</span>
    id: int
    question_text: str
    pub_date: datetime.datetime
    choices: List[BaseChoice]

    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span>:</span>
        orm_mode = <span class="hljs-literal">True</span>
</code></pre>
<p>Contrary to DRF theres is no out-of-the-box way to generate the schemas from the ORM declaration. However as you can see the <code>Question</code> <em>schema</em> and the <code>Question</code> <em>model</em>, are <em>very</em> similar. This is not ideal, as this can lead to a bit of code duplication. </p>
<p><a target="_blank" href="https://twitter.com/tiangolo">Tiangolo</a>, the author of FastAPI, got us covered tho ! A few months ago he released <a target="_blank" href="https://sqlmodel.tiangolo.com/">SQLModel</a>, a library  that aims to fill that gap by wrapping Pydantic and SQLAlchemy. I will not dive more into it for now, , as I believe it's important to understand the problem a tool is designed to solve before using it! I might do a dedicated article later in this tutorial though ! </p>
<p>Of note here is the use of nested <em>schemas</em>, and the orm modes. Setting <code>orm_mode</code> to true means that pydantic will know to access the attributes with the <code>object.attribute</code> syntax rather than <code>dict['key']</code>. And when accessing the <code>choices</code> attribute, SQLAlchemy will take care of making the necessary joins, which will offer us a nice nested structure where each question is associated with an array of related choices.</p>
<p>One last thing: you might have noticed than <code>ReadQuestion</code> and <code>ReadQuestionChoices</code> have most of their attributes in common, which is not very DRY. Pydantic support inheriting from existing schemas though, which means we can re-write it like that: </p>
<pre><code class="lang-python">
<span class="hljs-keyword">import</span> datetime
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> List

<span class="hljs-keyword">import</span> pydantic


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BaseChoice</span>(<span class="hljs-params">pydantic.BaseModel</span>):</span>
    choice_text: str
    votes: int

    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span>:</span>
        orm_mode = <span class="hljs-literal">True</span>


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BaseQuestion</span>(<span class="hljs-params">pydantic.BaseModel</span>):</span>
    question_text: str
    pub_date: datetime.datetime

    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span>:</span>
        orm_mode = <span class="hljs-literal">True</span>


<span class="hljs-comment"># We only include the id when reading</span>
<span class="hljs-comment"># That way we can reuse base question</span>
<span class="hljs-comment"># When creating our write schemas</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ReadQuestion</span>(<span class="hljs-params">BaseQuestion</span>):</span>
    id: int


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ReadQuestionChoices</span>(<span class="hljs-params">ReadQuestion</span>):</span>
    choices: List[BaseChoice]
</code></pre>
<p> There all good ! </p>
<h2 id="heading-building-the-endpoints">Building the Endpoints</h2>
<p>Now we've got all the tools we need to create our path operations ! </p>
<p>We'll need to read endpoints:</p>
<ul>
<li>An endpoint to list the existing questions: <code>/polls/</code>;</li>
<li>And endpiont to dipslay the details of the questins: <code>/polls/{id}</code></li>
</ul>
<p>In the Django tutorial, more <em>views</em> are built, because the views are also responsible for rendering the UI, so there need to be at least one per page. However, we're not building a monolith here ! The <em>pages</em> will be created in React in part 4, and the form and result page, for example, will both use the <code>GET /polls/{id}</code> endpoint to fetch the data they need. </p>
<p>In <code>polls/endpoints.py</code> create those two path operations : </p>
<pre><code class="lang-python">
<span class="hljs-comment"># polls/endpoints.py</span>

<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> List

<span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> schemas
<span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> crud

<span class="hljs-meta">@router.get("/", response_model=List[schemas.ReadQuestion])</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>():</span>
    <span class="hljs-keyword">return</span> crud.list_questions(db)


<span class="hljs-meta">@router.get("/{id}/", response_model=schemas.ReadQuestionChoices)</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">question_detail</span>(<span class="hljs-params">id: int</span>):</span>
  <span class="hljs-keyword">try</span>:
      <span class="hljs-keyword">return</span> crud.get_question(db, id)
  <span class="hljs-keyword">except</span> NoResultFound:
      <span class="hljs-keyword">raise</span> HTTPException(
          status_code=<span class="hljs-number">404</span>,
          detail=<span class="hljs-string">"Token does not exist"</span>,
      )
</code></pre>
<p>There are a few things going on here: </p>
<ul>
<li>We've added a second argument to the <code>router.get</code> call : the <code>response_model</code>. This response model will be applied to the value returned by the path operation, before sending the response to the client. We're using the <code>List</code> type from the standard <code>typing</code> package to specify that we returns several questions in the index.</li>
<li>In the <code>question_detail</code> path operation, we specify the {id} in the router url, and FastAPI is smart enough to automatically pass it as the first parameter to our path operations ! </li>
<li>We're using fastapi <a target="_blank" href="https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/?h=exceptions#raise-exceptions">HTTPException</a> to automatically return a 404 error if there is no question matching the providing ID.</li>
</ul>
<p>The type int and path parameters are also used by FastAPI to generate the automatic documentation !</p>
<p>However, If you've been paying attention you might have noticed that something is missing. Our crud utils need a <code>Session</code> objects to communicate with the database. We're passing them a variable called <code>db</code> here, but it's not defined anywhere yet ! </p>
<p>To retrieve this Session objects, we'll use one of FastAPI main selling point: its <a target="_blank" href="https://fastapi.tiangolo.com/tutorial/dependencies/">dependency injection system</a> ! </p>
<p><strong>What is dependeny injection</strong> ? </p>
<p>If you've alredy work with a Framework before, you should be familiar with the term. Dependency injection is a form of inversion of control that allows a "client" to specify
the dependencies it needs an trust that an other agent, oftenime the framework, will provide them at the right time. </p>
<p>For example in Django's view, the <code>self.request</code> object available in Class Based view is a dependency injection pattern ! Hope this clears at up !</p>
<center>

<img src="https://media.giphy.com/media/vp122eOzO0Hxm/giphy.gif" alt="Fascinating" />

 <em>Isn't it ?</em> 

</center>

<p>The dependency we're looking to inject is a Session. To do so, we declare a function that returns a session using the <code>SessionLocal</code> we created in <code>database.py</code> we created in  <a target="_blank" href="https://dev.indooroutdoor.io/building-the-poll-app-from-the-django-tutorial-with-fastapi-and-react-2">part 2</a></p>
<pre><code class="lang-python">
<span class="hljs-comment"># polls/endpoints.py</span>


<span class="hljs-comment"># Database session as a dependency</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_db</span>():</span>
    db = database.SessionLocal()
    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">yield</span> db
    <span class="hljs-keyword">finally</span>:
        db.close()
</code></pre>
<p>The code after the yield instructions will be execute right after our path operation is done sending the response. This way we can make sure the session is closed correctly ! Now we can use the <code>Depends</code> function providing from FastAPI:</p>
<pre><code class="lang-python">
<span class="hljs-comment"># polls/endpoints.py</span>


...

<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> Depends

...


<span class="hljs-meta">@router.get("/", response_model=List[schemas.ReadQuestion])</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>(<span class="hljs-params">db=Depends(<span class="hljs-params">get_db</span>)</span>):</span>
    <span class="hljs-keyword">return</span> crud.list_questions(db)


<span class="hljs-meta">@router.get("/{id}/", response_model=schemas.ReadQuestionChoices)</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">question_detail</span>(<span class="hljs-params">id: int, db=Depends(<span class="hljs-params">get_db</span>)</span>):</span>
    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">return</span> crud.get_question(db, id)
    <span class="hljs-keyword">except</span> NoResultFound:
        <span class="hljs-keyword">raise</span> HTTPException(
            status_code=<span class="hljs-number">404</span>,
            detail=<span class="hljs-string">"Token does not exist"</span>,
        )
</code></pre>
<p>Now everything should be working ! You can try out your endpoints using the automatic documentation available at <code>localhost/docs</code>. Create a few questions using <a target="_blank" href="https://dev.indooroutdoor.io/building-the-poll-app-from-the-django-tutorial-with-fastapi-and-react-2">PGAdmin</a> or a psql client to create a few questions and choices, and try it out ! Everyting should be working smootlhy, and Swagger should display some information about the reponse form that matches our pydantic <em>schemas</em>.</p>
<center>

<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1637488798054/R0775c0Wt.gif?auto=compress" alt="Testing the endpoints" />

</center>


<p>Awesome ! There is one last thing to do before we conclude. If we want to be able to make request from our UI to our API in part4, we need to take care of these pesky CORS headers. In the past setting those headers correctly as been the cause of many headaches, for many developers. With FastAPI howerver, nothing's more simple ! We just need to use the <code>CORSMiddleware</code> in <code>main.py</code>: </p>
<pre><code class="lang-python">
<span class="hljs-comment"># main.py</span>

...

<span class="hljs-keyword">from</span> fastapi.middleware.cors <span class="hljs-keyword">import</span> CORSMiddleware


origins = [<span class="hljs-string">"http://localhost:3000"</span>]

...

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=<span class="hljs-literal">True</span>,
    allow_methods=[<span class="hljs-string">"*"</span>],
    allow_headers=[<span class="hljs-string">"*"</span>],
)

...
</code></pre>
<p>Great ! Now we're ready to build an UI to talk with our brand new API ! </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p> That's it for today, hope you enjoyed it ! Part 4 we'll be coming soon, and we'll be focused on building an UI for our endpoints using React. I'm still debating if I should put everything React-relate into specific articles, I'd be glad to know what you think. Let me know on <a target="_blank" href="https://twitter.com/JiBRocher">Twitter</a>, you feedback is invaluable ! </p>
<h2 id="heading-references">References</h2>
<ol>
<li><a target="_blank" href="https://fastapi.tiangolo.com/tutorial/sql-databases/?h=crud#crud-utils">FastAPI CRUD</a></li>
<li><a target="_blank" href="https://pydantic-docs.helpmanual.io/">Pydantic Documentation</a></li>
<li><a target="_blank" href="https://sqlmodel.tiangolo.com/">SLQModel</a></li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Re-Doing the Django Tutorial With FastAPI and React: Database  & Models]]></title><description><![CDATA[Welcome to part 2 of this series where we're building the Poll App from the Django Tutorial, using FastAPI and React ! In part 1, we saw how to set up a FastAPI project using Docker, and wrote our first path operation. Today in Part 2, we'll go one s...]]></description><link>https://dev.indooroutdoor.io/building-the-poll-app-from-the-django-tutorial-with-fastapi-and-react-database-and-models</link><guid isPermaLink="true">https://dev.indooroutdoor.io/building-the-poll-app-from-the-django-tutorial-with-fastapi-and-react-database-and-models</guid><category><![CDATA[Web Development]]></category><category><![CDATA[backend]]></category><category><![CDATA[Python]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[APIs]]></category><dc:creator><![CDATA[Jb Rocher]]></dc:creator><pubDate>Mon, 15 Nov 2021 07:47:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1637504154118/lDGqUFSUT.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to part 2 of this series where we're building the Poll App from the <a target="_blank" href="https://docs.djangoproject.com/en/3.2/intro/tutorial01/">Django Tutorial</a>, using FastAPI and React ! In <a target="_blank" href="https://dev.indooroutdoor.io/the-poll-app-from-django-tutorial-with-fastapi-and-react-setting-up-the-project">part 1</a>, we saw how to set up a FastAPI project using Docker, and wrote our first path operation. Today in Part 2, we'll go one step further and start working with a relational database. </p>
<p>As usual the source for this part is available on <a target="_blank" href="https://github.com/jbrocher/fastapi-poll-2">Github</a></p>
<p>Here's what's on the menu : </p>
<ul>
<li>Spinning up a Postgres Database using docker-composedatabase)</li>
<li>Installing our ORM of choice: SQLAlchemy</li>
<li>Writing the Question and Choice model for the poll app</li>
</ul>
<p><em>Note: In the original Django tutorial, this is also where django-admin is introduced. For our own admin dashboard we'll be using react-admin which will be presented in a later part of this series !</em></p>
<hr />
<h2 id="heading-requirements">Requirements</h2>
<p>We'll be using docker-compose to run our databse as a service. If you haven't installed it yet, the instructions are available <a target="_blank" href="https://docs.docker.com/compose/install/">here</a> !</p>
<p>You don't <strong>have</strong> to use docker-compose to follow along though. If you prefer to set up your PostgresSQL server yourself, just skip directly to <a class="post-section-overview" href="#⚙%EF%B8%8F-configuring-our-orm%3A-sqlalchemy-and-alembic">Instaling the ORM</a></p>
<hr />
<h2 id="heading-spinning-up-the-database">💽 Spinning up the database</h2>
<p>First of all, we need a database to work with. I chose docker-compose to run a Postgres image along with our app. Our API is already dockerized so this will make it easy to managage everything. </p>
<p>I will not dive into the specifics of docker-compose as it's beyond the scope of this article. However if you've never used it before, I encourage you to go read th <a target="_blank" href="https://docs.docker.com/compose/">documentation</a>. It's quite extensive ! </p>
<p>Add a <code>docker-compose.yml</code> at the root of you project,  with the following instructions : </p>
<pre><code class="lang-yaml">
  <span class="hljs-attr">version:</span> <span class="hljs-string">"3.9"</span>
  <span class="hljs-attr">services:</span>
    <span class="hljs-attr">web:</span>
      <span class="hljs-attr">build:</span> <span class="hljs-string">.</span>
      <span class="hljs-attr">depends_on:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">db</span>
      <span class="hljs-attr">ports:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">"80:80"</span>
      <span class="hljs-attr">env_file:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">postgres.env</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">.env</span>
      <span class="hljs-attr">volumes:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">./app:/app</span>
    <span class="hljs-attr">db:</span>
      <span class="hljs-attr">image:</span> <span class="hljs-string">postgres</span>
      <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
      <span class="hljs-attr">volumes:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">data-volume:/var/lib/postgresql/data</span>
      <span class="hljs-attr">env_file:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">postgres.env</span>
    <span class="hljs-attr">pg_admin:</span>
      <span class="hljs-attr">image:</span> <span class="hljs-string">dpage/pgadmin4</span>
      <span class="hljs-attr">environment:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">PGADMIN_DEFAULT_EMAIL=user@domain.com</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">PGADMIN_DEFAULT_PASSWORD=password</span>
      <span class="hljs-attr">ports:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">"81:80"</span>
  <span class="hljs-attr">volumes:</span> 
    <span class="hljs-attr">data-volume:</span>
</code></pre>
<p> This configuration will launch the following: </p>
<p> <strong>The <code>web</code> service</strong></p>
<p> This is our api running on the port 80. We mount the /app folder containg the app code into the container so we don't have to rebuild it everytime we make a change.</p>
<p> We also include two environment files: </p>
<ul>
<li>postgres.env: This will hold the database credentials</li>
<li><p>.env: For environments variables specific to the web servcie</p>
<p><strong>The <code>db</code> service</strong></p>
<p>This service runs a postgres image using the same <code>postgres.env</code> than the API to configure the database. That way we can keep our environment variables DRY. 
It also uses the <code>data-volume</code> declared at the end of the file to persist the data.</p>
<p><strong>The <code>pg_admin</code> service</strong></p>
</li>
</ul>
<p>This runs a <a target="_blank" href="https://www.pgadmin.org/">pgAdmin</a> admin image. pgAdmin is an adminisitration tool for PostgreSQL database. Declaring it as a service will make configuring
the server connection eaiser as we'll benefit from docker-compose custom network. We expose it on port 81 to avoid conflicting with the API running on the port 80. </p>
<p>To configure the postgres image, create the postgres.env file at the root of the project, with the following variables : </p>
<pre><code> <span class="hljs-comment">#postgres.env </span>

  <span class="hljs-attr">POSTGRES_PASSWORD</span>=password
  <span class="hljs-attr">POSTGRES_USER</span>=poll
  <span class="hljs-attr">POSTGRES_DATABASE</span>=poll
  <span class="hljs-attr">POSTGRES_HOST</span>=db
</code></pre><p> We'ready to launch everything! Just run <code>docker-compose up</code> and you should see the output for each service. Now you can access pgAdmin at <code>localhost:81</code>. To connect to the postgres database, simply add a new server and enter information from <code>postgres.env</code> like so : </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636630576084/fdyP6gEP5.gif" alt="demo pg admin" /></p>
<p>That's it ! </p>
<p>Now that our environment is up and running in let's dive in ! </p>
<center>

<img src="https://media.giphy.com/media/L48RzCCKfpwHK/giphy.gif" alt="dive in" />

</center>

<hr />
<h2 id="heading-configuring-our-orm-sqlalchemy-and-alembic">⚙️  Configuring our ORM: SQLAlchemy and Alembic</h2>
<p>Contrary to Django, FastAPI doesn't ship with a built-in ORM solution  like <a target="_blank" href="https://docs.djangoproject.com/en/3.2/topics/db/queries/">django-orm</a>. FastAPI focuses on enabling you to build perfomant and robust APIs super efficiently, and let you make your own choices for everything else. You could avoid relational databases altogether and use <a target="_blank" href="https://www.mongodb.com/compatibility/mongodb-and-django">MongoDB</a> for instance. This lets you build an architecture tailored to your needs. </p>
<p>It does mean that we have to configure our ORM ourserlves though.  For this tutorial we'll use <a target="_blank" href="https://www.sqlalchemy.org/">SQLAlchemy</a>, a reliable solution under active developement. </p>
<p>First let's install a few dependencies. From you <code>app</code> folder run <code>poetry add sqlalchemy psycopg2-binary alembic pydantic</code>. 
Besides SQLAlchemy this will install the followingd packages: </p>
<ul>
<li>psycopg2: A python adaptater for PostgresSQL. Required by <code>SQLAlchemy</code> (And also by Django when working with Postgres)</li>
<li><a target="_blank" href="https://alembic.sqlalchemy.org/en/latest/">alembic</a>: This will be our migrations management tools</li>
<li><a target="_blank" href="https://pydantic-docs.helpmanual.io/">pydantic</a>: Data validation and settings management using python type annotations. If you've worked with <a target="_blank" href="https://www.django-rest-framework.org/">DRF</a> before,  you can think of pydantic models as the equivalent of serializers. </li>
</ul>
<h3 id="heading-using-environment-variables">Using environment variables :</h3>
<p>We will need to access the environement variables in order to set up the connection with the database. We'll use a special <code>pydantic</code> class called <code>BaseSettings</code>. Add a <code>config.py</code> file to <code>app/app</code> with the following code : </p>
<pre><code class="lang-python">
<span class="hljs-comment">#app/app/config.py</span>

<span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseSettings


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Settings</span>(<span class="hljs-params">BaseSettings</span>):</span>
    POSTGRES_USER: str
    POSTGRES_PASSWORD: str
    POSTGRES_DATABASE: str
    POSTGRES_HOST: str
</code></pre>
<p>When instantiating the class with <code>settings = Settings()</code>, pydantic will read the variables from the environment and validate them against the types we defined,  raising an error if anything is missing. This class is actually a great selling point of pydantic, offering tons of other functionalities like automatic parsing of list and dict type. More information is available <a target="_blank" href="https://pydantic-docs.helpmanual.io/usage/settings/#parsing-environment-variable-values">here</a>.</p>
<h3 id="heading-sqlalchemy">SQLAlchemy</h3>
<p>Let's configure SQLAlchemy to connect to our Database. Create <code>app/app/database.py</code> and put the following code in it : </p>
<pre><code class="lang-python"><span class="hljs-comment"># app/app/database.py</span>

<span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> create_engine
<span class="hljs-keyword">from</span> sqlalchemy.ext.declarative <span class="hljs-keyword">import</span> declarative_base
<span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> sessionmaker

<span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> config


<span class="hljs-comment"># The declarative base we'll use to create our model </span>
Base = declarative_base()
</code></pre>
<p>For now all we're concerned with is writing the models, so we only need to configure the declarative base from which they will inherit. In part 3 we'll come back to this file to create a session maker to use in our path operations. </p>
<h3 id="heading-alembic">Alembic ⚗️</h3>
<p>Last bit of configuration before writing the models: <a target="_blank" href="https://alembic.sqlalchemy.org/en/latest/">Alembic</a>. This is SQLAlchemy own migration manager. It will allows us to do the same thing than django's <code>makemigrations</code> and <code>migate</code> commands, but we need to initialize it first ! </p>
<p>Let's first generate the configuration files for alembic. From the root of the project <code>cd</code> into the <code>app</code> directory and run <code>alembic init</code>. This will create the configuration scripts for alembic. Your project strucutre should now look like this :  </p>
<center>

  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636740239216/C50VE0G_b.png?auto=compress" alt="directory structure" />

</center>

<p>We also need to let Alembic know where to find this scripts, so we can run the commands from the root of the project. We can do so by setting the <code>ALEMBIC_CONFIG</code> environment variable : </p>
<pre><code><span class="hljs-comment">//.env</span>

ALEMBIC_CONFIG<span class="hljs-operator">=</span><span class="hljs-operator">/</span>app<span class="hljs-operator">/</span>app<span class="hljs-operator">/</span>alembic.ini
</code></pre><p>Now let's edit the <code>env.py</code> file to configure the connection to the database. If you open this file, you'll see that it's mostly composed of two functions, corresponding to the two available modes of alembic: </p>
<ul>
<li><code>run_migrations_online</code>: Configure the online mode. This is the one that we will be using. </li>
<li><code>run_migrations_offline</code>: Configure the offline mode. This mode allows the user to generate SQL instructions instead of running the migrations directly against the database</li>
</ul>
<p>In both case, we need to edit the way the connection URL is set up. Instead of reading it from <code>alembic.ini</code> we'll use the Setting class we created earlier to generate it from our environement variables. Like so : </p>
<pre><code class="lang-python">

  <span class="hljs-keyword">from</span> app.config <span class="hljs-keyword">import</span> Settings

  settings = Settings()

  ...

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run_migrations_offline</span>():</span>

    ...

    url = <span class="hljs-string">f"postgresql://<span class="hljs-subst">{settings.POSTGRES_USER}</span>:<span class="hljs-subst">{settings.POSTGRES_PASSWORD}</span>@<span class="hljs-subst">{settings.POSTGRES_HOST}</span>/<span class="hljs-subst">{settings.POSTGRES_DATABASE}</span>"</span>

    context.configure(
        url=url,
        target_metadata=target_metadata,
        literal_binds=<span class="hljs-literal">True</span>,
        dialect_opts={<span class="hljs-string">"paramstyle"</span>: <span class="hljs-string">"named"</span>},
        compare_server_default=<span class="hljs-literal">True</span>,
    )

  ...

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run_migrations_online</span>():</span>

    ...

    url = <span class="hljs-string">f"postgresql://<span class="hljs-subst">{settings.POSTGRES_USER}</span>:<span class="hljs-subst">{settings.POSTGRES_PASSWORD}</span>@<span class="hljs-subst">{settings.POSTGRES_HOST}</span>/<span class="hljs-subst">{settings.POSTGRES_DATABASE}</span>"</span>
    connectable = engine_from_config(
        {<span class="hljs-string">"sqlalchemy.url"</span>: url},
        prefix=<span class="hljs-string">"sqlalchemy."</span>,
        poolclass=pool.NullPool,
    )
</code></pre>
<p>To try it out, drop into the web container, and run  <code>alembic revision -m "test revision"</code>. This will create an empty revision in the file <code>alembic/version</code> folder :  </p>
<pre><code class="lang-python">
<span class="hljs-string">"""test revision


Revision ID: 2af1b91bea53
Revises: 27259876f63d
Create Date: 2021-11-12 18:21:26.442182

"""</span>
<span class="hljs-keyword">from</span> alembic <span class="hljs-keyword">import</span> op
<span class="hljs-keyword">import</span> sqlalchemy <span class="hljs-keyword">as</span> sa


<span class="hljs-comment"># revision identifiers, used by Alembic.</span>
revision = <span class="hljs-string">'2af1b91bea53'</span>
down_revision = <span class="hljs-string">'27259876f63d'</span>
branch_labels = <span class="hljs-literal">None</span>
depends_on = <span class="hljs-literal">None</span>


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">upgrade</span>():</span>
    <span class="hljs-keyword">pass</span>


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">downgrade</span>():</span>
    <span class="hljs-keyword">pass</span>
</code></pre>
<p>If the file is generated wihtout errors, then Alembic is correctly configured ! You can delete this revision safely, we'll generate a new one from the models in a moment. </p>
<p>Now we're finally ready to write our models and generate the associated migrations ! </p>
<hr />
<h2 id="heading-writing-the-models">📝 Writing the models</h2>
<p>We'll need two model for our Poll app : </p>
<ul>
<li>A <code>Question</code> model : This will hold the question text</li>
<li>A <code>Choice</code> model: A possible answer for a question.  </li>
</ul>
<p>There is a one-to-many relation between a <code>Question</code> and a <code>Choice</code></p>
<p>To create them, add a <code>models.py</code> file to the poll folder, with the following code in it : </p>
<pre><code class="lang-python">
<span class="hljs-comment">#/app/polls/models.py</span>

<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime

<span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> Column
<span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> DateTime
<span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> ForeignKey
<span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> Integer
<span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> String
<span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> relationship

<span class="hljs-keyword">from</span> app.database <span class="hljs-keyword">import</span> Base


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Question</span>(<span class="hljs-params">Base</span>):</span>
    __tablename__ = <span class="hljs-string">"poll_question"</span>

    id = Column(Integer, index=<span class="hljs-literal">True</span>, primary_key=<span class="hljs-literal">True</span>)
    question_text = Column(String(<span class="hljs-number">200</span>), nullable=<span class="hljs-literal">False</span>)
    pub_date = Column(DateTime, nullable=<span class="hljs-literal">False</span>, default=datetime.utcnow)

    choices = relationship(<span class="hljs-string">"Choice"</span>, backref=<span class="hljs-string">"question"</span>)


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Choice</span>(<span class="hljs-params">Base</span>):</span>
    __tablename__ = <span class="hljs-string">"poll_choice"</span>

    id = Column(Integer, index=<span class="hljs-literal">True</span>, primary_key=<span class="hljs-literal">True</span>)
    choice_text = Column(String(<span class="hljs-number">200</span>), nullable=<span class="hljs-literal">False</span>)
    votes = Column(Integer, default=<span class="hljs-number">0</span>, nullable=<span class="hljs-literal">False</span>)
    question_id = Column(Integer, ForeignKey(<span class="hljs-string">"poll_question.id"</span>))
</code></pre>
<p>If you're coming from Django, it's important to note a few key differences with <code>django-orm</code>: </p>
<ul>
<li>You have to set the tablename yourself with the <code>__tablename__</code> class attribute. Here I followed the Django convention <code>&lt;app_name&gt;_&lt;model_name&gt;</code> but you're free to do as you please ! </li>
<li>We also need to designate a column to serve as the primary key.</li>
<li>The column are nullable by default, we have to explicetly set <code>nullable=False</code></li>
<li>Relationships need to be explicitely declared with the <a target="_blank" href="https://docs.sqlalchemy.org/en/14/orm/relationships.html">relationship</a> functions. This enables ORM features like loading a question's choices using <code>question.choices</code> </li>
</ul>
<p>As you can see, SQLAlechmy is a bit more "low level" than Django-ORM which means it needs more configuration. However, it also means that you have a more fine-grained control
over its behavior, and as we'll see later in this tutorial it makes querying more explicit! </p>
<p>Now that our models are declared let's see how we can automatically generate the corresponding migrations with Alembic.</p>
<center>

<img src="https://media.giphy.com/media/XI7rk6UYBM4LWp2rw0/giphy.gif" alt="The great migration" />

<em>the great migration</em>
</center>

<p>To automatically generate our migrations, we need to let Alembic know about our models. To do that, we'll simply import them into our <code>env.py</code> file. This is a bit like registering a new app into a django app <code>settings.py</code>. </p>
<p>We also need to import <code>Base</code>, as it is our declartive base that contains all the information for building the tables corresponding to our model. Each time a class inherits from <code>Base</code>, it adds its own instructions to <code>Base.metadata</code>.  These instructions are passed to Alembic through the <code>target_metadata</code> variable, so we need to assign it to <code>Base.metadata</code>.</p>
<pre><code class="lang-python">
<span class="hljs-comment">#app/app/alembic/env.py</span>

<span class="hljs-comment"># We import the models and our declartive base</span>
<span class="hljs-keyword">import</span> polls.models
<span class="hljs-keyword">from</span> app.database <span class="hljs-keyword">import</span> Base

<span class="hljs-meta">... </span>

<span class="hljs-comment"># Find this line near the top of the file </span>
<span class="hljs-comment"># And replace None with Base.metadata</span>
target_metadata = Base.metadata
</code></pre>
<p>All done ! Run <code>alembic version --autogenerate -m "create question and choice models"</code> to automatically creathe the migration file. 
It should generate the following instructions : </p>
<pre><code class="lang-python">
<span class="hljs-string">"""create_question_and_choice

Revision ID: 27259876f63d
Revises: 
Create Date: 2021-11-11 10:52:50.386678

"""</span>
<span class="hljs-keyword">from</span> alembic <span class="hljs-keyword">import</span> op
<span class="hljs-keyword">import</span> sqlalchemy <span class="hljs-keyword">as</span> sa


<span class="hljs-comment"># revision identifiers, used by Alembic.</span>
revision = <span class="hljs-string">'27259876f63d'</span>
down_revision = <span class="hljs-literal">None</span>
branch_labels = <span class="hljs-literal">None</span>
depends_on = <span class="hljs-literal">None</span>


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">upgrade</span>():</span>
    <span class="hljs-comment"># ### commands auto generated by Alembic - please adjust! ###</span>
    op.create_table(<span class="hljs-string">'poll_question'</span>,
    sa.Column(<span class="hljs-string">'id'</span>, sa.Integer(), nullable=<span class="hljs-literal">False</span>),
    sa.Column(<span class="hljs-string">'question_text'</span>, sa.String(length=<span class="hljs-number">200</span>), nullable=<span class="hljs-literal">False</span>),
    sa.Column(<span class="hljs-string">'pub_date'</span>, sa.DateTime(), nullable=<span class="hljs-literal">False</span>),
    sa.PrimaryKeyConstraint(<span class="hljs-string">'id'</span>)
    )
    op.create_index(op.f(<span class="hljs-string">'ix_poll_question_id'</span>), <span class="hljs-string">'poll_question'</span>, [<span class="hljs-string">'id'</span>], unique=<span class="hljs-literal">False</span>)
    op.create_table(<span class="hljs-string">'poll_choice'</span>,
    sa.Column(<span class="hljs-string">'id'</span>, sa.Integer(), nullable=<span class="hljs-literal">False</span>),
    sa.Column(<span class="hljs-string">'choice_text'</span>, sa.String(length=<span class="hljs-number">200</span>), nullable=<span class="hljs-literal">False</span>),
    sa.Column(<span class="hljs-string">'votes'</span>, sa.Integer(), nullable=<span class="hljs-literal">False</span>),
    sa.Column(<span class="hljs-string">'question_id'</span>, sa.Integer(), nullable=<span class="hljs-literal">True</span>),
    sa.ForeignKeyConstraint([<span class="hljs-string">'question_id'</span>], [<span class="hljs-string">'poll_question.id'</span>], ),
    sa.PrimaryKeyConstraint(<span class="hljs-string">'id'</span>)
    )
    op.create_index(op.f(<span class="hljs-string">'ix_poll_choice_id'</span>), <span class="hljs-string">'poll_choice'</span>, [<span class="hljs-string">'id'</span>], unique=<span class="hljs-literal">False</span>)
    <span class="hljs-comment"># ### end Alembic commands ###</span>


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">downgrade</span>():</span>
    <span class="hljs-comment"># ### commands auto generated by Alembic - please adjust! ###</span>
    op.drop_index(op.f(<span class="hljs-string">'ix_poll_choice_id'</span>), table_name=<span class="hljs-string">'poll_choice'</span>)
    op.drop_table(<span class="hljs-string">'poll_choice'</span>)
    op.drop_index(op.f(<span class="hljs-string">'ix_poll_question_id'</span>), table_name=<span class="hljs-string">'poll_question'</span>)
    op.drop_table(<span class="hljs-string">'poll_question'</span>)
    <span class="hljs-comment"># ### end Alembic commands ###</span>
</code></pre>
<p><em>Always check the migrations generated by Alembic to verify that everything is correct. You can find more information about what alembic can auto-generate <a target="_blank" href="https://alembic.sqlalchemy.org/en/latest/autogenerate.html">here</a></em></p>
<p>Everything checks out, now run <code>alembic upgrade head</code> to apply the migration, then head over to pgAdmin, and you should see the new tables ! </p>
<center>

<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1636750848803/G0q1QnxsB.png?auto=compress" alt="tables" />

</center>

<p>Congrats ! You're models are ready ! </p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Thats all for today, hope you enjoyed it! Now that we've set up our relational database, in part 3 we'll see how to communicate with in it our path operations using SQLAlchemy session. </p>
<p>In the meantime you reach out to me on <a target="_blank" href="https://twitter.com/JiBRocher">Twitter</a>. Questions and feedback are most welcomed ! </p>
<h3 id="heading-references">References</h3>
<ol>
<li><a target="_blank" href="https://fastapi.tiangolo.com/tutorial/sql-databases/">FastAPI database docs</a></li>
<li><a target="_blank" href="https://docs.sqlalchemy.org/en/13/orm/extensions/declarative/api.html">SQLAlchemy docs</a></li>
<li><a target="_blank" href="https://alembic.sqlalchemy.org/en/latest/">Alembic docs</a></li>
<li><a target="_blank" href="https://docs.docker.com/compose/">docker-compose</a></li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Agile Software Architecture using Archimate and the C4 Model]]></title><description><![CDATA[Introduction
Recently, I decided the time had come for an overhaul of the way we handle software architecture in my team. As our numbers grow, we had an urgent need for tools and processes that allowed us to keep our agility, while offering strong gu...]]></description><link>https://dev.indooroutdoor.io/agile-software-architecture-using-archimate-and-the-c4-model</link><guid isPermaLink="true">https://dev.indooroutdoor.io/agile-software-architecture-using-archimate-and-the-c4-model</guid><category><![CDATA[software architecture]]></category><category><![CDATA[agile]]></category><category><![CDATA[software development]]></category><category><![CDATA[tools]]></category><dc:creator><![CDATA[Jb Rocher]]></dc:creator><pubDate>Mon, 08 Nov 2021 07:32:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1636024290588/RRWghD81z.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Recently, I decided the time had come for an overhaul of the way we handle software architecture in my team. As our numbers grow, we had an urgent need for tools and processes that allowed us to keep our agility, while offering strong guarantees on the quality of our codebase.</p>
<p>After some research, I settled on a process based on the <a target="_blank" href="https://c4model.com/">C4 model</a> and <a target="_blank" href="https://www.archimatetool.com/">Archi</a>. I am quite satisfied with it, so I figured I'd share it! First we'll go over what thoses tools are, and how to use them. Then we'll apply them to a pratical example: Modeling the Poll App from <a target="_blank" href="https://dev.indooroutdoor.io/series/fastapi-react-poll-app">my on going FastAPI tutorial</a>! </p>
<p><em>The models for the poll app are also available in <a target="_blank" href="https://github.com/jbrocher/poll-app-archi">this repository</a></em></p>
<h2 id="heading-the-specificiations">📋 The Specificiations</h2>
<p>First let's go over our needs. We're looking for a software architecture method with the following characterisitcs : </p>
<ul>
<li>Flexible enough to be used in an agile environment</li>
<li>Easy to adopt for both senior and junior developers</li>
<li>Collaborative</li>
<li>Reusable and Maintainable</li>
</ul>
<p>I feel the last point is most difficult to attain. Without the right tools,  it's hard for a team to go beyond the initial drawing created during the conception phase, and actually maintain a model representative of the current codebase.</p>
<p>With that in mind, let's see how the C4 + Archi combination fits the bill ! </p>
<h2 id="heading-the-tools">🔧 The Tools</h2>
<h3 id="heading-the-c4-model">The C4 Model</h3>
<p>So what is the C4 model ?</p>
<p>The <a target="_blank" href="https://c4model.com/">C4 model</a> is a set of software architecture viewpoints designed by <a target="_blank" href="https://twitter.com/simonbrown">Simon Brown</a>. The official documentation (which I encourage you to read  👓) describes it as : </p>
<blockquote>
<p>The C4 model is an "abstraction-first" approach to diagramming software architecture, based upon abstractions that reflect how software architects and developers think about and build software.</p>
</blockquote>
<p>A C4 model for a given system is composed of 4 diagrams, representing 4 different levels of abstraction. Each lower level diagram "zoom in" on a concept from the previous higher level one. Here's a brief description of each of them:  </p>
<ul>
<li><p><strong>Context Diagram</strong>: This is the most abstract level. It shows how a system interacts with its users and other systems</p>
</li>
<li><p><strong>Containers Diagram</strong>: Shows the runtimes that power the system. For example a database is a container.</p>
</li>
<li><p><strong>Components Diagram</strong>: Displays the logical units that make up a container and their relationships. What a "logical unit" is depends on the use case, and what you're trying to show in this diagram. </p>
</li>
<li><p><strong>Code Diagram</strong>: This would show the exact implementation of each component. However it would be really tedious to maintain. Instead, this is where the expertise of the engineering team and the best practices in place should kick in. </p>
</li>
</ul>
<center>

  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635926716300/tSOz29bCb.png?auto=compress" alt="The C4 model" />



  <em>The 4 levels of the C4 model</em>


</center>


<p><strong>A note on UML</strong></p>
<p>You might notice some similarities with the Unified Modeling Language. While it serves a similar purpose, the C4 model is simpler and way more flexible, which it makes its adoption by a team more likely. </p>
<p>This is actually my issue with UML. While its extensive specification might sound good in theory, in practice it is quite heavy to use. In my opinion, unless you're working with a waterfall methodology with a long period dedicated to creating the models, and tools that can auto-generate code from UML, the payoff is jut not worth the trouble. </p>
<p>Of course this doesn't prevent you from using complementary diagrams coming from UML if you'd like to. Personnaly I find the <a target="_blank" href="https://en.wikipedia.org/wiki/Sequence_diagram">Sequence Diagram</a> often comes in handy !</p>
<h3 id="heading-archimate-andamp-archi">Archimate &amp; Archi</h3>
<p>Now that we've talked about the model (or rather meta-model, as the C4 is a tool for building models), let's see how we can go about producing the diagrams. Remember that we want actual modeling capablilites here, not a simple drawing tool. <a target="_blank" href="https://c4model.com/#Modelling">Simon explains it well</a>, modeling allows to easily reuse the same concepts across different views, and enable a static analysis of your architecture. </p>
<p>To generate our models we'll use the following: </p>
<ul>
<li><a target="_blank" href="https://www.archimatetool.com/#">Archimate</a> : It is an open specificiation modeling language. This is the language that our models will be written in. </li>
<li><a target="_blank" href="https://www.archimatetool.com/#">Archi</a>: an archimate editor. This makes working with archimate models super easy ! 
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635926684052/Rzfd8uLWL.png?auto=compress" alt="Achi" /></li>
</ul>
<p> What I love about Archimate and Archi, is that it maintains a clear separation between model and view, while still offering a great WYSIWG editor. </p>
<h2 id="heading-the-methodology">🎓 The Methodology</h2>
<p>Now that we've got the tools, let's see about using them !</p>
<h3 id="heading-collaboration-and-versionning">Collaboration and Versionning</h3>
<p>First of all, for our models to be maintainable, we need to be able to collaborate on them. The great thing about using a modelling langage like archimate instead of boxes and arrows, is that we can version our architecture.  All we need to do is to store our models in repositories and we can even create pull request to discuss the changes !</p>
<p>The only issue is that the archimate files genreated by Archi aren't very git friendly. Each model is stored in a huge .xml file which can makes conflicts really hard to solve. Luckily, there is a plug-in that can take care of that for us : <a target="_blank" href="https://github.com/archimatetool/archi-modelrepository-plugin/wiki">CoArchi</a>. CoArchi put each archimate concept into its own file, making PR way easier to read !</p>
<h3 id="heading-using-the-c4-meta-model-in-archi">Using The C4 Meta-model In Archi</h3>
<p>So now we can collaborate, but how do we actually create the C4 diagram in Archi? </p>
<p>Our method for this is heavily inspired by this <a target="_blank" href="https://www.archimatetool.com/blog/2020/04/18/c4-model-architecture-viewpoint-and-archi-4-7/">excellent article</a>, which explains how to map Archimate concepts to the C4 meta model. We can actually save these Mapping in a dedicated model, and use a view as a toolbox from where we can copy paste the C4 building blocks as needed. Here is the end result :</p>
 <center>

 <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635929287462/tbtGcryaD.png?auto=compress" alt="ToolBox" />

 </center>

<ul>
<li>The C4 <strong>persons</strong> are maped to an archimate <strong>business actors</strong></li>
<li>The C4 <strong>software systems</strong> and containers are mapped to archimate <strong>application components</strong></li>
<li>The C4 components are mapped to <strong>application functions</strong></li>
</ul>
<h3 id="heading-extending-the-c4-model">Extending The C4 Model</h3>
<p>I won't go into too much details here, because it really depends on your organisation, but I think it's good to extend the C4 model with your own set of rules to better suit you needs. </p>
<p>For example in my team we agree on the following : </p>
<ul>
<li>In containers diagrams relationship must include the protocol (HTPPS, SSH..) used by the containers to communicate</li>
<li><p>In the component diagrams the presence of a relationship between component alwasy imply the existence of a public api/method/attribute</p>
<p>We even have special rules for describing React apps: </p>
<ul>
<li>Coumpound components are described using composition relation ship</li>
<li>.. on so on. </li>
</ul>
</li>
</ul>
<h2 id="heading-putting-everything-in-practice">Putting Everything in Practice</h2>
<p>Finally let's have a look at a concrete example. Here's how I would describe the architecture of the Poll app from my <a target="_blank" href="https://dev.indooroutdoor.io/the-poll-app-from-django-tutorial-with-fastapi-and-react-1">FastAPI tutorial</a>, using the methods outlined in this article. </p>
<p><strong>The Context</strong></p>
<p>First let's describe the context for our Poll App. In a real life situation I would discuss this diagram with my Product Manager to be sure we are aligned on the systems affected by the project: </p>
<center>

 <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635929821232/vVXP1CB1d.png?auto=compress" alt="System Diagram" />
  <em>Context Diagram</em>

 </center>


<p> This is pretty self explanatory, here our Poll App is used by both an administrator responsbile for creating the polls, and the end user that answer them. I've also included an imaginary "stats app" to show inter system relationships.</p>
<p> <strong>The Poll App Container</strong></p>
<p> Now let's zoom in and explore which containers are necessary for our app. Here we decide what kind of technologies will be necessary for our projects,
 and how everything will interacts. </p>
 <center>

  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635930891270/rkchp5K5E.png?auto=compress" alt="Container Diagram" />
  <em>Container Diagram</em>

</center>

<p>In this case we're using a rather classic paradigm with an API hosting the business logic and an UI handled by SPAs.</p>
<p> <strong>The Poll Api Components</strong></p>
<p> Let'ts zoom agin, this time on the API container. In a real life scenario, depending on the complexity of the SPAs, I'd also create a 
 component Diagram for the UI containers. </p>
 <center>

   <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635931596637/-PU8eajVf5.png?auto=compress" alt="Component Diagram" />
   <em>Component Diagram</em>

</center>

<p>To be honest,  this one feels a bit overkill for our Poll APP,  as it is a very simple use case. Usually in situation with more complex business logic to implement, I'd probably have a single CRUD component. </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Thanks for reading, I hoped you found this article useful ! How do your team handle Software Architecture in an agile environment ? Let me know in the comments or on <a target="_blank" href="https://twitter.com/JiBRocher">Twitter</a> !</p>
<h2 id="heading-references">References</h2>
<ol>
<li><a target="_blank" href="https://c4model.com/">C4 model website</a></li>
<li><a target="_blank" href="https://www.opengroup.org/archimate-forum/archimate-overview">Archimate</a></li>
<li><a target="_blank" href="https://www.archimatetool.com/">Archi</a></li>
<li><a target="_blank" href="https://github.com/archimatetool/archi-modelrepository-plugin/wiki">Archi Collaboration Plugin</a></li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Re-Doing the Django Tutorial With FastAPI And React: Setting up a FastAPI project !]]></title><description><![CDATA[FastAPI is an awesome modern Python framework designed to enable developers to quickly build robust and performant APIs. 
Once you start getting the hang of it, you can become super efficient in building new endpoints and implementing 
complex busine...]]></description><link>https://dev.indooroutdoor.io/the-poll-app-from-django-tutorial-with-fastapi-and-react-setting-up-the-project</link><guid isPermaLink="true">https://dev.indooroutdoor.io/the-poll-app-from-django-tutorial-with-fastapi-and-react-setting-up-the-project</guid><category><![CDATA[Django]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[backend]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Jb Rocher]]></dc:creator><pubDate>Mon, 01 Nov 2021 07:07:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1637504092267/kLiowVjJT.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>FastAPI is an awesome modern Python framework designed to enable developers to quickly build robust and performant APIs. 
Once you start getting the hang of it, you can become super efficient in building new endpoints and implementing 
complex business logic. However,  it might be destabilizing at first when coming from more opiniated Framework like <a target="_blank" href="https://www.djangoproject.com/">Django</a> that 
offers more functionnalities out of the box.</p>
<p>Thus, I thought it could be interesting (and fun !) to re-recreate the well known polls app from the Django tutorial, but this time using FastAPI
and React ! In this multi part series I'll try to follow the same path as the original as much as possible, although I'll probably split
some parts in two to avoid an overload of information. </p>
<p>Today is part 1 : Setting up the project, creating the polls app and making your first django view <strong>path operation</strong></p>
<p>The code for this part is available <a target="_blank" href="https://github.com/jbrocher/fastapi-poll-1">here</a> but I encourage you to code along instead ! </p>
<hr />
<h2 id="heading-setting-up-the-project">🔧 Setting up the project</h2>
<p>FastAPI is quite easy to setup, in fact all you need to run a FastAPI app is a singe file instantiating
the main app and an <a target="_blank" href="https://asgi.readthedocs.io/en/latest/">ASGI</a> worker to run it. Of course we'll go a bit further. We'll run 
the app in a Docker container so you'll need to have it installed on your machine</p>
<h3 id="heading-the-docker-image">The docker image</h3>
<p>First we need to install a few dependencies and we'll use <a target="_blank" href="https://python-poetry.org/docs/">poetry</a> to manage them. Poetry is a modern package manager for python, that also helps you setup your virtual environment. If you don't have poetry, don't worry ! The <a target="_blank" href="https://python-poetry.org/docs/#osx--linux--bashonwindows-install-instructions">installation instructions</a> are straightforward, and its use is pretty intuitive. </p>
<p>To initialize the project run <code>poetry init</code> and follow the instructions. The packages we want to install are : </p>
<ul>
<li>FastAPI: The FastAPI framework ❤️</li>
<li>Uvicorn: The ASGI server that will run our app</li>
</ul>
<p>Once you're done you should see a <code>pyproject.toml</code> file at the root of your project resembling that one: </p>
<pre><code class="lang-toml">
<span class="hljs-comment">#pyproject.toml</span>

<span class="hljs-section">[tool.poetry]</span>
<span class="hljs-attr">name</span> = <span class="hljs-string">"fastapi-poll-1"</span>
<span class="hljs-attr">version</span> = <span class="hljs-string">"0.1.0"</span>
<span class="hljs-attr">description</span> = <span class="hljs-string">""</span>
<span class="hljs-attr">authors</span> = [<span class="hljs-string">"io.io"</span>]

<span class="hljs-section">[tool.poetry.dependencies]</span>
<span class="hljs-attr">python</span> = <span class="hljs-string">"^3.8"</span>
<span class="hljs-attr">fastapi</span> = <span class="hljs-string">"^0.70.0"</span>
<span class="hljs-attr">uvicorn</span> = <span class="hljs-string">"^0.15.0"</span>

<span class="hljs-section">[tool.poetry.dev-dependencies]</span>
<span class="hljs-attr">black</span> = <span class="hljs-string">"^21.9b0"</span>
<span class="hljs-attr">reorder-python-imports</span> = <span class="hljs-string">"^2.6.0"</span>

<span class="hljs-section">[build-system]</span>
<span class="hljs-attr">requires</span> = [<span class="hljs-string">"poetry-core&gt;=1.0.0"</span>]
<span class="hljs-attr">build-backend</span> = <span class="hljs-string">"poetry.core.masonry.api"</span>
</code></pre>
<p>Now let's create a the Dockerfile for our app! You can create it at the root of your working directory, and add the following instructions in it: </p>
<pre><code class="lang-python">
FROM python:<span class="hljs-number">3.9</span>

WORKDIR /app

<span class="hljs-comment"># Install Poetry</span>
RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | POETRY_HOME=/opt/poetry python &amp;&amp; \
    cd /usr/local/bin &amp;&amp; \
    ln -s /opt/poetry/bin/poetry &amp;&amp; \
    poetry config virtualenvs.create false


<span class="hljs-comment"># Copy using poetry.lock* in case it doesn't exist yet</span>
COPY ./app/pyproject.toml ./app/poetry.lock* /app/

RUN poetry install --no-root --no-dev

COPY ./app /app


CMD [<span class="hljs-string">"uvicorn"</span>, <span class="hljs-string">"app.main:app"</span>, <span class="hljs-string">"--reload"</span>, <span class="hljs-string">"--host"</span>, <span class="hljs-string">"0.0.0.0"</span>, <span class="hljs-string">"--port"</span>, <span class="hljs-string">"80"</span>]
</code></pre>
<p>This needs a bit of commenting. </p>
<p>The way dependencies are installed might seems a bit strange. We need to first install poetry, 
then run <code>install</code> without creating a virtual env (that <code>--no-root</code> bit). What's great about this approach is that you can use
the same <code>pyproject.toml</code> in your developement environment so things like static analysis tools uses the same dependencies. In a production 
settings we would probably creat a multi-stage build and discard <code>poetry</code> to have a slimer image. </p>
<p>The <code>app</code> folder will be created in the next section that's where our code will live. </p>
<p>Finally, notice the <code>--reload</code> option we pass to uvicorn which will allows us to work without having to restart the worker everytime we make
a change. </p>
<p>We'll launch the container a bit later, first we need to initialize the app ! </p>
<h3 id="heading-the-fastapi-app">The fastapi app</h3>
<p>  First things first, let's create the folder where our code will live: <code>mkidr app</code></p>
<p>  Now as I said we'd only need to create a main.py file if we wanted to. But to stay true to the theme of this serie, I'd like 
  to create a directory structure similar to django apps. That's why we'll create a second <code>app</code> directory that will be our "main" 
  app. Later we'll also create a <code>polls</code> directory. </p>
<p>  For now simply run <code>mkdir app/app</code> to create the folder, and create a <code>main.py</code> file inside of it. Your directory strucutre should now 
  look like this : </p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635364028526/bjhcE5_tz.png?auto=compress" alt="directory structure" /></p>
<p> Perfect ! And now buckle up as the obligatory <code>hello world !</code> is approaching. Inside <code>main.py</code> write the following lines : </p>
<pre><code class="lang-python">
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI


app = FastAPI()


<span class="hljs-meta">@app.get("/")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">root</span>():</span>
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"message"</span>: <span class="hljs-string">"Hello World"</span>}
</code></pre>
<p>This will initialize the app and create our first <a target="_blank" href="https://fastapi.tiangolo.com/tutorial/first-steps/">path operation</a>. This one 
is straight from the docs ! But you can't see the message yet as we still need to launch the container. We're almost there, 
head over to the next section ! </p>
<h3 id="heading-launching-the-app">Launching the app</h3>
<p>Before launching the app we need to build the image first. From the root of your project run <code>docker build -t poll-app .</code></p>
<p>Now we can launch the container: <code>docker run -it -p 80:80 -v "$(pwd)"/app:/app poll-app</code>. This will also mount
our app directory so we don't have to recreate it each time we edit the files. </p>
<p>We're now ready to test our endpoint. In your browser navigate to <code>localhost</code> and behold ! </p>
<center>

<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1637342998855/jrEsBi2XW.png?auto=compress" alt="the endpoint" />

</center>

<center>

<img src="https://media.giphy.com/media/lIzAEoZEn571u/giphy.gif" alt="Hello world" />

<em>... general kenobi</em>

</center>

<p>Now that's already good, but what is <em>great</em> is that FastAPI comes with a built in and Swagger for which the open api configuration 
is autogeneated. That means that if you go to <code>localhost/docs</code> you'll see the following : </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635274196047/B9w6r4ub3.png?auto=compress" alt="docs" /></p>
<p>FastAPI automatically generate your endpoint documentation ! Pretty neat feature for an API to have if you ask me. </p>
<hr />
<h2 id="heading-creating-a-poll-app">📜 Creating a poll app</h2>
<p> Now that <code>Hello world !</code> is out of the way (I recommend adding it to your personnal collection), let's get to the actual polls app. As
 I mentionned earlier we'll create a <code>polls</code> directory along side the main app. Once that's done add a file called <code>endpoints.py</code> to 
 this new folder, your project' structure should look like that : </p>
 <center>

 <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635368833896/fu9-1nq1Y.png?auto=compress" alt="Directory Structure" />

 </center>

<p> Don't forget to create the relevant <code>__init__.py</code> file so the folder are recognized as Python modules</p>
<p> Now let's create an index for our polls app. This index will later be responsible for listing the available polls in our database but for now it will return a simple message. Add the following code to the <code>polls/endpoints.py</code>: </p>
<pre><code class="lang-python">
  <span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> APIRouter

  router = APIRouter()


<span class="hljs-meta">  @router.get("/")</span>
  <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>():</span>
      <span class="hljs-keyword">return</span> {<span class="hljs-string">"message"</span>: <span class="hljs-string">"polls index"</span>}
</code></pre>
<p>Here we use <a target="_blank" href="https://fastapi.tiangolo.com/tutorial/bigger-applications/#apirouter">APIrouter</a> to declare our path operations. It works exatcly the same as 
the <code>FastaAPI()</code>. Now all we need to do to make our endpoint available, is to declare it in <code>main.py</code>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> polls.endpoints

app.include_router(polls.endpoints.router, prefix=<span class="hljs-string">"/polls"</span>)
</code></pre>
<p> The prefix indidates that all the routes coming from the polls endpoints start with polls. Your polls index should now be available both
 at <code>localhost/polls</code> and in the Swagger documentation ! Let's take a look ! </p>
<p>   <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635363516528/OAkgF4bj5.gif?auto=compress" alt="swagger polls" /></p>
<p> Great ! Our polls index works, although it doesn't return anything yet, we'll chnage that in part 2!</p>
<hr />
<h2 id="heading-conclusion">Conclusion</h2>
<p>That's is for part 1, let me know if you found it useful ! In part 2, we'll see how to set up an actual database to store our polls !</p>
]]></content:encoded></item><item><title><![CDATA[FastAPI Tips & Tricks: Testing a Database]]></title><description><![CDATA[If you haven't heard of it yet, FastAPI is a micro-framewok that allows developers to make full use of modern Python."Micro" here, means that rather than trying to cover every use case, it focuses on doing a single thing extremely well: giving you th...]]></description><link>https://dev.indooroutdoor.io/fastapi-tips-and-tricks-testing-a-database</link><guid isPermaLink="true">https://dev.indooroutdoor.io/fastapi-tips-and-tricks-testing-a-database</guid><category><![CDATA[Web Development]]></category><category><![CDATA[Python]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[backend]]></category><category><![CDATA[APIs]]></category><dc:creator><![CDATA[Jb Rocher]]></dc:creator><pubDate>Mon, 25 Oct 2021 06:27:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1637507996654/cAyATJLY6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you haven't heard of it yet, <a target="_blank" href="https://fastapi.tiangolo.com/">FastAPI</a> is a micro-framewok that allows developers to make full use of modern Python."Micro" here, means that rather than trying to cover every use case, it focuses on doing a single thing extremely well: giving you the tools to build a fast (duh) API. Everything else is up to you which allows you to tailor you architecture to perfectly fit your needs. </p>
<p>  However it does mean that you have to set up yourself some things that usually comes out of the box in other frameworks. For example, if you want to use a relational database, 
  you'll have to choose and install an ORM. SQLAlchemy is the one documented by Fast API. There is a <a target="_blank" href="https://fastapi.tiangolo.com/tutorial/sql-databases/">guide</a> to help you set it up, and a <a target="_blank" href="https://fastapi.tiangolo.com/advanced/testing-database/?h=test+data">tutorial</a> wich gives some indications on how to go about testing it. But if like me, you come from Django, you might still struggle to configure everything so that each test works in isolation, and leaves the database as it was before running. </p>
<p>  That's what we'll be covering in this article ! </p>
<p>  <em>The code presented in this article is fully available on <a target="_blank" href="https://github.com/jbrocher/hashnode-testing-database-fastapi">Github</a>. Simply clone the repo and call <code>docker-compose up</code> to launch everything.</em> </p>
<hr />
<h2 id="heading-setting-up-the-project">Setting up The project</h2>
<p>  First things first, we need something to test ! We'll create a small project with an endpoint to create an "Item" with a title and a description and store it in the database. I personnaly prefer to use docker-compose to run a Fastapi image as well as a Postgres database next to it. But it's not mandatory.</p>
<p>  Let's get to it then ! </p>
<h3 id="heading-setting-up-sqlalchemy">Setting up SQLAlchemy</h3>
<p>  SQLAlchemy works by creating a <a target="_blank" href="https://docs.sqlalchemy.org/en/14/orm/mapping_api.html#sqlalchemy.orm.declarative_base">declarative base</a>, which allows us to describe our Database tables as Python classes that inherits it. Let's configure it in a file called <code>database.py</code>:</p>
<pre><code class="lang-python">
    <span class="hljs-comment"># database.py</span>

    <span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> create_engine
    <span class="hljs-keyword">from</span> sqlalchemy.ext.declarative <span class="hljs-keyword">import</span> declarative_base
    <span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> sessionmaker

    <span class="hljs-comment"># We're using postgres but you could use</span>
    <span class="hljs-comment"># any other engine supported by SQlAlchemy</span>
    SQLALCHEMY_DATABASE_URL = <span class="hljs-string">"postgresql://test-fastapi:password@db/db"</span>

    engine = create_engine(
        SQLALCHEMY_DATABASE_URL
    )
    SessionLocal = sessionmaker(autocommit=<span class="hljs-literal">False</span>, autoflush=<span class="hljs-literal">False</span>, bind=engine)

    Base = declarative_base()
</code></pre>
<p>  Now we can create our Item model by inherting <code>Base</code></p>
<h3 id="heading-models-schemas-and-crud-utils">Models, schemas, and CRUD utils</h3>
<p>  For our <code>Item</code> model, we simply create a file called <code>models.py</code> and declare it using the <code>Base</code> we've juste configured :</p>
<pre><code class="lang-python">
      <span class="hljs-comment"># models.py</span>


      <span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> Column
      <span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> Integer
      <span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> String

      <span class="hljs-keyword">from</span> .database <span class="hljs-keyword">import</span> Base


      <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Item</span>(<span class="hljs-params">Base</span>):</span>
          __tablename__ = <span class="hljs-string">"items"</span>

          <span class="hljs-comment"># Don't forget to set an index ! </span>
          id = Column(Integer, primary_key=<span class="hljs-literal">True</span>, index=<span class="hljs-literal">True</span>)

          title = Column(String, index=<span class="hljs-literal">True</span>)

          description = Column(String, index=<span class="hljs-literal">True</span>)
</code></pre>
<p>  We also need to define the corresponding <a target="_blank" href="https://pydantic-docs.helpmanual.io/">Pydantic</a> schemas. FastAPI uses them to perform validation and serailization : </p>
<pre><code class="lang-python">
    <span class="hljs-comment"># schemas.py</span>
    <span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Optional

    <span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel


    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ItemBase</span>(<span class="hljs-params">BaseModel</span>):</span>
        title: str
        description: Optional[str] = <span class="hljs-literal">None</span>


    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ItemCreate</span>(<span class="hljs-params">ItemBase</span>):</span>
        <span class="hljs-keyword">pass</span>


    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Item</span>(<span class="hljs-params">ItemBase</span>):</span>
        id: int

        <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span>:</span>
            orm_mode = <span class="hljs-literal">True</span>
</code></pre>
<p>  And finally some CRUD utils for handling our <code>Item</code> instances :  </p>
<pre><code class="lang-python">
    <span class="hljs-comment"># crud.py</span>

    <span class="hljs-keyword">from</span> sqlalchemy <span class="hljs-keyword">import</span> select
    <span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> Session

    <span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> schemas
    <span class="hljs-keyword">from</span> .models <span class="hljs-keyword">import</span> Item


    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_items</span>(<span class="hljs-params">db: Session</span>):</span>
        items = select(Item)
        <span class="hljs-keyword">return</span> db.execute(items).scalars().all()


    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_item</span>(<span class="hljs-params">db: Session, item: schemas.ItemCreate</span>):</span>
        db_item = Item(**item.dict())
        db.add(db_item)
        db.commit()
        db.refresh(db_item)
        <span class="hljs-keyword">return</span> db_item
</code></pre>
<p> Now that we're done with everything database related, we can create the endpoint that we'll be testing ! </p>
<h3 id="heading-the-endpoint-itself">The endpoint itself</h3>
<p>  Create a <code>main.py</code> file and add the following lines to it : </p>
<pre><code class="lang-python">
      <span class="hljs-comment"># main.py</span>

      <span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> List

      <span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> Depends
      <span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI
      <span class="hljs-keyword">from</span> sqlalchemy.orm <span class="hljs-keyword">import</span> Session

      <span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> crud
      <span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> models
      <span class="hljs-keyword">from</span> . <span class="hljs-keyword">import</span> schemas
      <span class="hljs-keyword">from</span> .database <span class="hljs-keyword">import</span> engine
      <span class="hljs-keyword">from</span> .database <span class="hljs-keyword">import</span> SessionLocal

      app = FastAPI()

      <span class="hljs-comment"># Here we create all the tables directly in the app</span>
      <span class="hljs-comment"># in a real life situation this would be handle by a migratin tool</span>
      <span class="hljs-comment"># Like alembic</span>
      models.Base.metadata.create_all(bind=engine)


      <span class="hljs-comment"># Dependency</span>
      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_db</span>():</span>
          db = SessionLocal()
          <span class="hljs-keyword">try</span>:
              <span class="hljs-keyword">yield</span> db
          <span class="hljs-keyword">finally</span>:
              db.close()

<span class="hljs-meta">      @app.post("/items/", response_model=schemas.Item)</span>
      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_item</span>(<span class="hljs-params">item: schemas.ItemCreate, db: Session = Depends(<span class="hljs-params">get_db</span>)</span>):</span>
          <span class="hljs-keyword">return</span> crud.create_item(db, item)
</code></pre>
<p>Notice that we pass the SQLAlchemy Session as a dependency to our endpoint. This is important to note as it'll help us easily test our endpoint as we'll see in the next section. </p>
<p>We can check that our endpoint works thanks to the Swagger available on the <code>/docs</code> endpoint. Another awesome feature of FastAPI ! </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635078348118/SE73hLXOC.png?auto=compress" alt="Items endpoint" /></p>
<p>That's great! But our goal here is to test the endpoint automatically with some unit tests. We need to make sure that our tests don't affect the main database. We also want them to be deterministic and reusable, let's see how to do exactly that ! </p>
<hr />
<h2 id="heading-testing-the-endpoint">Testing the endpoint</h2>
<h3 id="heading-basic-testing">Basic testing</h3>
<p>  First let's try the method described in the <a target="_blank" href="https://fastapi.tiangolo.com/advanced/testing-database/">official documentation</a>. The idea here is to 
  leverage FastAPI <a target="_blank" href="https://fastapi.tiangolo.com/tutorial/dependencies/">Dependency</a> system. Since our endpoint receives its session by dependency injection, we can use <a target="_blank" href="https://fastapi.tiangolo.com/tutorial/dependencies/">Dependency overrides</a> to replace it with a session pointing to a test Database. </p>
<p>  To to that, create a file called <code>test_database.py</code> to write our tests, and add the following code to it : </p>
<pre><code class="lang-python">
  <span class="hljs-comment"># test_database.py</span>
  SQLALCHEMY_DATABASE_URL = <span class="hljs-string">"postgresql://test-fastapi:password@db/test-fastapi-test"</span>

  engine = create_engine(SQLALCHEMY_DATABASE_URL)
  TestingSessionLocal = sessionmaker(autocommit=<span class="hljs-literal">False</span>, autoflush=<span class="hljs-literal">False</span>, bind=engine)

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">override_get_db</span>():</span>
      <span class="hljs-keyword">try</span>:
          db = TestingSessionLocal()
          <span class="hljs-keyword">yield</span> db
      <span class="hljs-keyword">finally</span>:
          db.close()

  app.dependency_overrides[get_db] = override_get_db
</code></pre>
<p>This will replace all instances of the <code>get_db</code> dependency with the <code>override_get_db</code> which return a <code>Session</code> connected to our test database. This 
does one aspect of what we're looking for:  we can run ou tests without affecting our main database. </p>
<p>Let's write a test and run it with pytest to see if that works as expected.</p>
<pre><code class="lang-python">
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_post_items</span>():</span>

    <span class="hljs-comment"># We grab another session to check </span>
    <span class="hljs-comment"># if the items are created</span>
    db = override_get_db() 
    client = TestClient(app)

    client.post(<span class="hljs-string">"/items/"</span>, json={<span class="hljs-string">"title"</span>: <span class="hljs-string">"Item 1"</span>})

    client.post(<span class="hljs-string">"/items/"</span>, json={<span class="hljs-string">"title"</span>: <span class="hljs-string">"Item 2"</span>})

    items = crud.get_items(db)
    <span class="hljs-keyword">assert</span> len(items) == <span class="hljs-number">2</span>
</code></pre>
  <center>

  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635068579627/7AFO-ykoa.png?auto=compress" alt="test database" />

  </center>


<p>  It works perfectly ! Or does it ? We also want our tests to be deterministics, so if we run pytest again we should get the same result...</p>
 <center>

  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635074467203/og4MI9utd.png?auto=compress" alt="test fails" />

 </center>

<p> But we dont ! The items created the first time we ran the test are still in the database, so now we have 
 4 of them instead of 2. Now, we could delete the items manually. However,  this is not something we want to do, because this will
 become really tedious to maintain once we have more tests that validate more complex behaviors. </p>
<p> Luckily for use there IS a better way ! </p>
<h3 id="heading-enter-transactions">Enter transactions</h3>
<p> To solve our problem we'll make use of <a target="_blank" href="https://www.w3resource.com/sql/controlling-transactions.php">SQL Transactions</a>. Essentially 
 transactions are a way to keep a set SQL instructions private to other database connections, until they are commited. And, if the changes 
 should not be commited, they can be rolled back ! Let's add try to add that to our dependency override. Disclaimer the following block of code doesn't actually works, we'll see why in a minute :</p>
<pre><code class="lang-python">

    <span class="hljs-comment"># The following doesn't work</span>
    <span class="hljs-comment"># changes won't be rolled back !</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">override_get_db</span>():</span>
        <span class="hljs-keyword">try</span>:
            db = TestingSessionLocal()
            db.begin()
            <span class="hljs-keyword">yield</span> db
        <span class="hljs-keyword">finally</span>:
            db.rollback()
            db.close()

    app.dependency_overrides[get_db] = override_get_db
</code></pre>
<p>  In a FastAPI dependency, everything that comes after the yield instuction is executed after the path operation has run. So that's a great place to 
  try to roll back the changes made during the test. This looks good, but as I said, it doesn't work yet</p>
<p>  That's because a call to commit always commit the <a target="_blank" href="https://docs.sqlalchemy.org/en/14/orm/session_transaction.html#commit-as-you-go">outermost transaction</a>, so we can't nest transactions thath way. This is a conscious choice from the SQLAlchemy developers to 
  avoid creating confusing and hard to predict behaviors. </p>
<p>  Luckily for us they have provided an <a target="_blank" href="https://docs.sqlalchemy.org/en/14/orm/session_transaction.html?highlight=transactions#joining-a-session-into-an-external-transaction-such-as-for-test-suites">escape hatch</a> designed to be used test suites. Indeed, a Session can be bound to an existing transaction, by opening it this way : </p>
<pre><code class="lang-python">
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">override_get_db</span>():</span>
      connection = engine.connect()

      <span class="hljs-comment"># begin a non-ORM transaction</span>
      transaction = connection.begin()

      <span class="hljs-comment"># bind an individual Session to the connection</span>
      db = Session(bind=connection)
      <span class="hljs-comment"># db = Session(engine)</span>

      <span class="hljs-keyword">yield</span> db

      db.rollback()
      connection.close()
</code></pre>
<p>  And this time it will work ! </p>
  <center>

  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635068882325/rIUktA5fT.png?auto=compress" alt="Test passes" />

  </center>

<p>  Now we can run our test as many times as we want, and it will always pass. But we're not quite done yet. This works for data created by the endpoint, 
  but what if we wanted to seed the database during our test, say for testing an endpoint listing the available items ? The dependency override won't work in this case,  because it only deals with 
  the dependency injection system. </p>
<h3 id="heading-fixtures-everywhere">Fixtures everywhere !</h3>
<p>  Let's quickly implement the example I was just talking about: a READ endpoint that lists the existing items in the database : </p>
<pre><code class="lang-python">
<span class="hljs-meta">    @app.get("/items/", response_model=List[schemas.Item])</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">read_items</span>(<span class="hljs-params">db: Session = Depends(<span class="hljs-params">get_db</span>)</span>):</span>
        <span class="hljs-keyword">return</span> crud.get_items(db)
</code></pre>
<p>  In order to test it we'd want to create a bunch of items before calling the endpoint, while having the database revert back to its original state after the test. The solution is simple : define our test database session in a fixture as well : </p>
<pre><code class="lang-python">
    <span class="hljs-comment"># conftest.py</span>

<span class="hljs-meta">    @pytest.fixture(scope="session")</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">db_engine</span>():</span>
        engine = create_engine(SQLALCHEMY_DATABASE_URL)
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> database_exists:
            create_database(engine.url)

        Base.metadata.create_all(bind=engine)
        <span class="hljs-keyword">yield</span> engine


<span class="hljs-meta">    @pytest.fixture(scope="function")</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">db</span>(<span class="hljs-params">db_engine</span>):</span>
        connection = db_engine.connect()

        <span class="hljs-comment"># begin a non-ORM transaction</span>
        transaction = connection.begin()

        <span class="hljs-comment"># bind an individual Session to the connection</span>
        db = Session(bind=connection)
        <span class="hljs-comment"># db = Session(db_engine)</span>

        <span class="hljs-keyword">yield</span> db

        db.rollback()
        connection.close()


    <span class="hljs-comment"># </span>
<span class="hljs-meta">    @pytest.fixture(scope="function")</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">client</span>(<span class="hljs-params">db</span>):</span>
        app.dependency_overrides[get_db] = <span class="hljs-keyword">lambda</span>: db

        <span class="hljs-keyword">with</span> TestClient(app) <span class="hljs-keyword">as</span> c:
            <span class="hljs-keyword">yield</span> c
</code></pre>
<p>  Pytest fixtures works like FastAPI dependencies: everything after the yield instruction is ran after exiting the scope pass as a paramter to the decorator.  Our <code>db</code> fixture rollsback the session after each test, and we can use it to seed the database. I've also put the dependency override in a fixture  alongside the client. That way each 
  time we use the client, the override will be in place.</p>
<p>Now we can create the fixture we want and test our new endpoint :</p>
<pre><code class="lang-python">
<span class="hljs-meta">    @pytest.fixture</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">items</span>(<span class="hljs-params">db</span>):</span>
        create_item(db, schemas.ItemCreate(title=<span class="hljs-string">"item 1"</span>))
        create_item(db, schemas.ItemCreate(title=<span class="hljs-string">"item 2"</span>))
</code></pre>
<pre><code class="lang-python">
<span class="hljs-comment"># test_database.py"</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_list_items</span>(<span class="hljs-params">items, client</span>):</span>
    response = client.get(<span class="hljs-string">"/items"</span>)
    <span class="hljs-keyword">assert</span> len(response.json()) == <span class="hljs-number">2</span>
</code></pre>
<p>And again our tests pass without issues no matter how many time we run them ! </p>
<center>

<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1635075764407/uP77OX5cW.png?auto=compress" alt="test pass" />

</center>


<center>
Hooray !

<img src="https://media.giphy.com/media/msKNSs8rmJ5m/giphy.gif" alt="yes !" />

</center>

<hr />
<h2 id="heading-thats-all-folks">That's all folks !</h2>
<p>That's it for today, now you know how to properly test you FastAPI app when using a relational database. Thanks for reading, I'll 
write another article on FastAPI soon ! </p>
<hr />
<h2 id="heading-references">References</h2>
<ol>
<li><a target="_blank" href="https://media.giphy.com/media/msKNSs8rmJ5m/giphy.gif">FastAPI website</a></li>
<li><a target="_blank" href="https://media.giphy.com/media/msKNSs8rmJ5m/giphy.gif">Testing relational database</a></li>
<li><a target="_blank" href="https://www.sqlalchemy.org/">SQLAlchemy docs</a></li>
<li><a target="_blank" href="https://www.google.com/search?q=transactions+sql+alchemy&amp;oq=transactions+sql+alchemy&amp;aqs=chrome..69i57j69i60l2.3943j0j7&amp;sourceid=chrome&amp;ie=UTF-8">Transactions in SQLAlchemy</a></li>
</ol>
]]></content:encoded></item><item><title><![CDATA[How to extend any HTML element with React and Typescript]]></title><description><![CDATA[In React, we often build  small components to encapsulate specific behaviors. This allows for composition and high re-usability. 
Sometimes for instance,  it makes sense to wrap a basic HTML tag like an <input> or a <button> in a Component,  to add c...]]></description><link>https://dev.indooroutdoor.io/how-to-extend-any-html-element-with-react-and-typescript</link><guid isPermaLink="true">https://dev.indooroutdoor.io/how-to-extend-any-html-element-with-react-and-typescript</guid><category><![CDATA[React]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Jb Rocher]]></dc:creator><pubDate>Wed, 13 Oct 2021 22:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1636914773768/K_2aaK2UI.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In React, we often build  small components to encapsulate specific behaviors. This allows for <a target="_blank" href="https://reactjs.org/docs/composition-vs-inheritance.html">composition</a> and high re-usability. </p>
<p>Sometimes for instance,  it makes sense to wrap a basic HTML tag like an <code>&lt;input&gt;</code> or a <code>&lt;button&gt;</code> in a Component,  to add conditional styling or some extra piece of functionality. However, in that case it stops being a real DOM element and accepting html attributes. </p>
<p>Wouldn't it be nice to keep the original interface of the element we're wrapping ? </p>
<p>We'll learn how to do that in today's article ! </p>
<h2 id="heading-making-a-button">Making a Button</h2>
<p>Let's say you want to make a button that takes a <code>valid</code> property and displays a ✔️ if this prop evaluates to true, and a ❌ otherwise. Sounds easy enough, let's get to it!</p>
<p>First let's define the interface for our <code>Button</code></p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">interface</span> ButtonProps {
  valid: <span class="hljs-built_in">Boolean</span>;
}
</code></pre>
<p>It takes a single <code>valid</code> boolean property. </p>
<p>The component itself looks like this : </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> Button: React.FunctionComponent&lt;ButtonProps&gt; = <span class="hljs-function">(<span class="hljs-params">{
  valid,
  children,
}</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;button
      disabled={!valid}
    &gt;
      {valid ? &lt;Valid /&gt; : &lt;Invalid /&gt;}
      <span class="hljs-comment">// Rendering the children allows us to use it like</span>
     <span class="hljs-comment">// a normal Button</span>
      {children}
    &lt;/button&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Button;
</code></pre>
<p>Perfect ! The component renders correctly depending on the value of <code>valid</code> : </p>
<center>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634151221117/OthVKVmSU.png" alt="Button Examples" />
</center>

<p>Lovely ! However we probably want for something to happen when we click on the button, and that's where we run into some trouble. For example, let's try to trigger a simple alert on click : </p>
<pre><code class="lang-typescript">
      &lt;Button
        onClick={<span class="hljs-function">() =&gt;</span> {
          alert(<span class="hljs-string">"hello"</span>);
        }}
        valid={valid}
      &gt;
        Valid button
      &lt;/Button&gt;
</code></pre>
<p>If you try to use your Button that way, typescript will complain: </p>
<pre><code class="lang-bash">
Type <span class="hljs-string">'{ children: string; onClick: () =&gt; void; valid: boolean; }'</span> is not assignable to <span class="hljs-built_in">type</span> <span class="hljs-string">'IntrinsicAttributes &amp; ButtonProps &amp; { children?: ReactNode; }'</span>.  
Property <span class="hljs-string">'onClick'</span> does not exist on <span class="hljs-built_in">type</span> <span class="hljs-string">'IntrinsicAttributes &amp; ButtonProps &amp; { children?: ReactNode; }'</span>.  TS2322
</code></pre>
<p>And indeed, while the <code>onClick</code> property is part of a <code>button</code> interface, our custom <code>Button</code> only accepts the <code>valid</code> props ! </p>
<p>Let's try to fix that. </p>
<hr />
<h2 id="heading-typing-the-component">Typing the component</h2>
<p>First it's important to know that working with React type declarations can be a little tricky. Like most libraries, React delegates its types to the <a target="_blank" href="https://github.com/DefinitelyTyped/DefinitelyTyped">Definetly Type Repository</a>.  If you head over to the <a target="_blank" href="https://github.com/DefinitelyTyped/DefinitelyTyped/blob/78120d1d515a1cd7bcd3145424b1a17be15310a9/types/babel-plugin-react-html-attrs/index.d.ts#L1742">React section</a>,  you'll find a <em>lot</em> of similar interfaces.  </p>
<p>Take <code>React.HTMLAttributes&lt;T&gt;</code> for instance. It is a generic interface that accepts all kind of <code>HtmlElement</code> types.  Let's try it with <code>HTMLButtonElement</code>. It seems to be what we're looking for, and if we use it, Typescript will stop complaining. </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">interface</span> ButtonProps <span class="hljs-keyword">extends</span> React.HTMLAttributes&lt;HTMLButtonElement&gt; {
  valid: <span class="hljs-built_in">Boolean</span>;
}
</code></pre>
<p>Now that works like a charm, so what am I going on about ?  Well, say we want to set the type of our Button ...</p>
<pre><code class="lang-typescript">
      &lt;Button
        onClick={<span class="hljs-function">() =&gt;</span> {
          alert(<span class="hljs-string">"hello"</span>);
        }}
        valid={valid}
        <span class="hljs-keyword">type</span>=<span class="hljs-string">"submit"</span>
      &gt;
        Valid button
      &lt;/Button&gt;
</code></pre>
<p>We get a compilation error ! </p>
<pre><code class="lang-bash">
Type <span class="hljs-string">'{ children: string; onClick: () =&gt; void; type: string; valid: boolean; }'</span> is not assignable to <span class="hljs-built_in">type</span> <span class="hljs-string">'IntrinsicAttributes &amp; ButtonProps &amp; { children?: ReactNode; }'</span>.

Property <span class="hljs-string">'type'</span> does not exist on <span class="hljs-built_in">type</span> <span class="hljs-string">'IntrinsicAttributes &amp; ButtonProps &amp; { children?: ReactNode; }'</span>.  TS2322
</code></pre>
<p>And indeed, taking a closer look to <a target="_blank" href="https://github.com/DefinitelyTyped/DefinitelyTyped/blob/78120d1d515a1cd7bcd3145424b1a17be15310a9/types/babel-plugin-react-html-attrs/index.d.ts#L1742">React.HTMLAttribute</a> it doesn't actually define any html attribute specific to the <code>HtmlElement</code> type it receives:</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">interface</span> HTMLAttributes&lt;T&gt; <span class="hljs-keyword">extends</span> AriaAttributes, DOMAttributes&lt;T&gt; {
<span class="hljs-comment">// &lt;T&gt; is not used here</span>
}
</code></pre>
<p>The <code>DOMAttributes</code> that it extends only deals with event handlers. This is is just one example, but there a many others, and since I am a great guy, I'm going to save you some time and give you the correct interface to use. The one that actually does what we want is <code>React.ComponentProps</code>. Note that we pass it a string and not a type. </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">interface</span> ButtonProps <span class="hljs-keyword">extends</span> React.ComponentProps&lt;"button"&gt; {
  valid: <span class="hljs-built_in">Boolean</span>;
}
</code></pre>
<p>No more compilation error ! Our <code>Button</code> component now has the exact same interface as a normal <code>button</code>, with the additional <code>valid</code> property. </p>
<p>Done ? Almost but we still have a little problem left to solve.</p>
<hr />
<h2 id="heading-passing-the-props-down-to-the-dom">Passing the props down to the DOM</h2>
<p>Indeed, if we click on the button nothing happens. It should display an alert, but it doesn't, something is missing.</p>
<p>The problem is, while we have the correct interface, we need to pass the props down to the actual <code>&lt;button&gt;</code> element. Now, we could set the onClick handler explicitely by adding the props to our component. However,  we want our <code>Button</code> to be able to handle any props that a <code>&lt;button&gt;</code> might receive. Doing that for every possible HTML attribute would be hard to read and way too time consuming. </p>
<p>That's where the spread operator comes in handy ! Have look : </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> Button: React.FunctionComponent&lt;ButtonProps&gt; = <span class="hljs-function">(<span class="hljs-params">{
  valid,
  children,
  ...buttonProps
}</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;button
      {...buttonProps}
      disabled={!valid}
    &gt;
      {valid ? &lt;Valid /&gt; : &lt;Invalid /&gt;}
      {children}
    &lt;/button&gt;
  );
};
</code></pre>
<p>Since our component is typed correctly, Typescript can infer that <code>ButtonProps</code> only contains HTML attributes, and we can pass it directly to the actual <code>DOM</code> element. </p>
<p>And now our <code>Button</code> finally works as expected :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634153017028/FZfDlDREM.png" alt="Screenshot from 2021-10-13 21-22-43.png" /></p>
<p>Not only that but we can pass it any props as you would a <code>&lt;button&gt;</code>. <code>type</code>, <code>name</code>.. you name it ! </p>
<p>It's magic ! </p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://media.giphy.com/media/Nj5sTh6kdoKfm/giphy.gif">https://media.giphy.com/media/Nj5sTh6kdoKfm/giphy.gif</a></div>
<hr />
<h2 id="heading-conclusion">Conclusion</h2>
<p>That's all for today ! Hope your found this article useful, more will come soon ! </p>
<hr />
<h2 id="heading-references">References</h2>
<ol>
<li><a target="_blank" href="https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_usecase">React Typescript CheatSheet</a></li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Building An acquisition Funnel in React with Xstate - Part 2]]></title><description><![CDATA[This is part 2 of a two parts article. If you haven't done it yet, checkout part one ! 
In part one, we used a strong programming pattern to model our funnel: a state machine. We also built a FunnelProvider that provides us with a flexible api to syn...]]></description><link>https://dev.indooroutdoor.io/building-an-acquisition-funnel-in-react-with-xstate-part-2</link><guid isPermaLink="true">https://dev.indooroutdoor.io/building-an-acquisition-funnel-in-react-with-xstate-part-2</guid><category><![CDATA[React]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Frontend Development]]></category><dc:creator><![CDATA[Jb Rocher]]></dc:creator><pubDate>Mon, 11 Oct 2021 16:26:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1633779780811/5q4Kzs2ac.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This is part 2 of a two parts article. If you haven't done it yet, checkout <a target="_blank" href="https://dev.indooroutdoor.io/building-an-acquisition-funnel-in-react-with-xstate">part one</a> !</em> </p>
<p>In part one, we used a strong programming pattern to model our funnel: a state machine. We also built a <code>FunnelProvider</code> that provides us with a flexible api to synchronize it with React. </p>
<p>Today we'll implement the actual steps of the funnel and see how we can update our state machine to store the data entered by the customer at each step. </p>
<p>As always the code we'll write in this article is fully available <a target="_blank" href="https://github.com/jbrocher/funnel-state-machine-part-2">here</a>. However if you feel like coding along you can start from where we left off by cloning the repository from <a target="_blank" href="https://github.com/jbrocher/funnel-state-machine">part 1</a></p>
<hr />
<h1 id="state-machine-theory">State machine Theory</h1>
<p>As we did last time, let's start with a bit of state machine theory ! If you're in a hurry, and you just want to see how it's done, <a class="post-section-overview" href="#completing-the-funnel">skip to the implementation</a>. However understanding these concepts is a great step towards mastering this pattern and being able to apply it to more complex use cases. </p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://giphy.com/gifs/idKeY3nvmdIsM">https://giphy.com/gifs/idKeY3nvmdIsM</a></div>
<h2 id="moore-and-mealy-machines">Moore and Mealy machines</h2>
<p>We already know that a Finite State Machine (FSM) is defined by a set of States and Transitions, and can only be in one state at a time. But if we look at the <a target="_blank" href="https://en.wikipedia.org/wiki/Finite-state_machine#Mathematical_model">Mathematical Definiton</a>, there is one aspect we haven't talked about yet : the output function. </p>
<p>So what is the output function ? </p>
<p>Well, for our machine to be useful, it needs to yield an output. If we stick to maths, this output must be part of a finite output alphabet, usually a bunch of 0s and 1s if we're talking electronics. However, since we're using a programming language, this could be anything really. And that's where the output function comes in : It derives an output from the current state of the machine. </p>
<p>There are basically two ways of computing this output, which in turn define two kind of machines : the Moore Machine and the Mealy Machine.</p>
<h3 id="our-current-model-the-moore-machine">Our current model: the Moore machine</h3>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Moore_machine">The Moore Machine</a> is the most straightforward type of finite state machine. Its current output is completely defined by its current state. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633858619129/Sf9qGt8QS.png" alt="the Moore Machine" /></p>
<p>If you remember, this is the model we decided to use for our funnel. Indeed until now we were only concerned with building a machine able to display the correct succession of steps. A Moore machine was perfect for that because we simply needed to match each step of the funnel with a state of the machine.</p>
<p>However for ou funnel to be useful, we  also need to collect data from our customers, which is part of the output of the machine. And this is were it gets tricky: there is an infinite amount of slightly different data that the customer might input, but our finite state machine is well... finite. </p>
<p><em>Actually if we added a maximum length to our input, there would be a finite amount of permutation. But this number would be huge and could not be realistically represented by a state machine !</em></p>
<p>So a Moore machine won't cut it, let's see how we can solve this problem ! </p>
<h3 id="maybe-a-mealy-machine-will-do-the-trick">Maybe a Mealy machine will do the trick ?</h3>
<p>Last time we also briefly mentioned the  <a target="_blank" href="https://en.wikipedia.org/wiki/Mealy_machine">Mealy machine</a> . It is quite similar to a Moore machine in that it's also defined by a set of states and transitions. However the output function is a bit different: the current output of a Mealy Machine depends both on the state and the input that triggers the transition. This greatly increases the number of possible outputs for the same amount of states, sometimes to the detriment of readability. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633858908584/b_nIhb2fM.png" alt="mealy-machine.png" /></p>
<p>However, the amount of possible different outputs is still finite. In fact there is a compete equivalence between the Moore machines and the Mealy machines : It's possible to convert a Moore machine (and vice versa) by following the right algorithm</p>
<p>This means that a Mealy machine can't help us solves the problem, but it is worth mentioning as it's still a really useful model. Now let see what else is left ...</p>
<h2 id="state-charts-to-the-rescue">State Charts to the rescue !</h2>
<p>To solve our problem, we'll need to use the big gun : The <strong>Statechart</strong>. </p>
<p>So what is a <strong>statechart</strong> ? Well essentially it is a finite state machine with extended capabilities. Again it is defined by a set of states and transitions, but it also provides extra features : </p>
<ul>
<li>Hierarchical states: states that are also state machines ! </li>
<li>Guarded transitions: transitions that can be taken only if a condition is met</li>
<li>Delayed transitions: transitions that are delayed (duh)</li>
<li>Actions: Side Effect that happen before, after or during a transition</li>
</ul>
<p>Wait side effects ? Well that's exactly what we're looking for ! If each time we transition out of a step, we're able to store the user data somewhere as a side effect, our problem is solved !</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://giphy.com/gifs/theoffice-episode-13-the-office-tv-Ztzt8zhmmpVPUiSNMX">https://giphy.com/gifs/theoffice-episode-13-the-office-tv-Ztzt8zhmmpVPUiSNMX</a></div>
<h2 id="our-machine">Our machine</h2>
<p>So it's settled, let's up our game, and convert our Moore Machine to a State Chart. We'll see where the user data is stored in the next section. For now we only need to decide which actions to add, and where.</p>
<div class="hn-embed-widget" id="funnel-state-chart"></div><p>And the good news is: it's super easy ! We define <code>exit</code> actions on each state of the funnel. No matter with which transition we exit the state, we want to update the user data with the input from the step we're exiting. So we define three actions : </p>
<ul>
<li><code>setActivity</code> for setting the activity selected in step 1</li>
<li><code>addAttendee</code> for adding a new attendee each time we exit step 2</li>
<li><code>setAdditionalInformation</code> for setting the additional information entered in the optional step</li>
</ul>
<p>We could also handle POSTing the data to an API by taking when reaching the final state. </p>
<hr />
<h1 id="completing-the-funnel">Completing the Funnel</h1>
<p>Now let's update our State machine implementation and apply the actions defined on our chart. I'd like to point out that thanks to our <code>FunnelProvider</code> we only need to update <code>state-machine.ts</code>, and the provider will take care of the rest ! That's what I love with the compound component pattern: it is super flexible ! </p>
<h2 id="adding-context-and-actions-to-the-state-machine">Adding context and actions to the state machine</h2>
<h3 id="putting-things-into-context">Putting things into context</h3>
<p>The <em>ouput</em> of our state machine, which is the customer data, we'll be stored into the <a target="_blank" href="https://xstate.js.org/docs/guides/context.html">Xstate Context</a>. This is exactly what we need as it was designed to hold quantitative data.</p>
<p>First let's defined its type in types.ts</p>
<pre><code class="lang-typescript">#types.ts

# It is always good practice to use constant
# instead <span class="hljs-keyword">of</span> raw strings
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> ESCAPE_GAME = <span class="hljs-string">"escape-game"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> BOWLING = <span class="hljs-string">"bowling"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> Attendee {
  name: <span class="hljs-built_in">string</span>;
  surname: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> Activity = <span class="hljs-keyword">typeof</span> ESCAPE_GAME | <span class="hljs-keyword">typeof</span> BOWLING;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> FunnelData {
  activity?: Activity;
  attendees: Attendee[];
  additional_information?: <span class="hljs-built_in">string</span>;
  payment?: <span class="hljs-built_in">number</span>;
}
</code></pre>
<p>And add it to our state machine in <code>state-machine.ts</code></p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> stateMachine = Machine&lt;FunnelData, FunnelEvent&gt;(
  {
    id: <span class="hljs-string">"funnel-state-machine"</span>,
    initial: <span class="hljs-string">"activity"</span>,
    context: {
      activity: <span class="hljs-literal">undefined</span>,
      attendees: [],
      additional_information: <span class="hljs-literal">undefined</span>,
      payment: <span class="hljs-literal">undefined</span>,
    },
    states: {
      activity: {
        on: {
          SELECT_ACTIVITY: <span class="hljs-string">"register_attendee"</span>,
        },
      },
      register_attendee: {
        on: {
          ADD_ATTENDEE: <span class="hljs-string">"register_attendee"</span>,
          ADD_INFO: <span class="hljs-string">"additional_information"</span>,
          SUBMIT_ATTENDEE: <span class="hljs-string">"payment"</span>,
        },
      },
      additional_information: {
        on: {
          SUBMIT_ADDITIONNAL_INFORMATION: <span class="hljs-string">"payment"</span>,
        },
      },
      payment: {
        <span class="hljs-keyword">type</span>: <span class="hljs-string">"final"</span>,
      },
    },
  }
);
</code></pre>
<p>Notice that we pass our <code>FunnelData</code> interface to the <code>Machine</code> constructor, which allows  Xstate to type our machine correctly. </p>
<h3 id="adding-actions">Adding actions</h3>
<p>Now let's add the exit actions that we defined earlier. Xstate follows the state chart principles and allows to define actions that can be executed when entering or exiting a state node, or during a specific transition. </p>
<p>To modify the context we need to use <a target="_blank" href="https://xstate.js.org/docs/guides/context.html#assign-action">assign actions</a>, which let us modify the context. </p>
<p>First let's define the <code>setActivity</code> action</p>
<pre><code class="lang-typescript"># state-machine.ts

<span class="hljs-keyword">const</span> setActvity = <span class="hljs-function">(<span class="hljs-params">context: FunnelData, event: ActivityEvent</span>) =&gt;</span> {

  <span class="hljs-keyword">return</span> {
    ...context,
    activity: event.data,
  };
};
</code></pre>
<p>Now that looks great, but if we use it as is in the machine, Typescript will raise an error, because there is no guarantee that this action will always be called with the <code>ActivityEvent</code>. </p>
<p>That means we need to narrow the type of the event before using it : </p>
<pre><code class="lang-typescript">
#state-machine.ts

<span class="hljs-keyword">const</span> setActvity = <span class="hljs-function">(<span class="hljs-params">context: FunnelData, event: FunnelEvent</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (event.type !== SELECT_ACTIVITY) {
    <span class="hljs-keyword">return</span> context;
  }
  <span class="hljs-keyword">return</span> {
    ...context,
    activity: event.data,
  };
};
</code></pre>
<p>Those of you familiar with  <a target="_blank" href="https://redux.js.org/">Redux</a> will find some similarities with  a reducer. </p>
<p>Now let's finish creating the actions : </p>
<pre><code class="lang-typescript">#state-machine.ts 

<span class="hljs-keyword">const</span> addAttendee = <span class="hljs-function">(<span class="hljs-params">context: FunnelData, event: FunnelEvent</span>) =&gt;</span> {
  <span class="hljs-keyword">switch</span> (event.type) {
    <span class="hljs-keyword">case</span> ADD_ATTENDEE:
      <span class="hljs-keyword">return</span> {
        context,
        attendees: context.attendees.concat(event.data),
      };
    <span class="hljs-keyword">case</span> SUBMIT_ATTENDEE:
      <span class="hljs-keyword">return</span> {
        context,
        attendees: context.attendees.concat(event.data),
      };
    <span class="hljs-keyword">case</span> ADD_INFO:
      <span class="hljs-keyword">return</span> {
        context,
        attendees: context.attendees.concat(event.data),
      };
    <span class="hljs-keyword">default</span>:
      <span class="hljs-keyword">return</span> context;
  }
};

<span class="hljs-keyword">const</span> setAddtionalInformation = <span class="hljs-function">(<span class="hljs-params">context: FunnelData, event: FunnelEvent</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (event.type !== SUBMIT_ADDITIONNAL_INFORMATION) {
    <span class="hljs-keyword">return</span> context;
  }
  <span class="hljs-keyword">return</span> {
    ...context,
    additional_information: event.data,
  };
};
</code></pre>
<p>And now let's add theses actions to our machine. In each state node we define the corresponding exit actions</p>
<pre><code class="lang-typescript">
#state-machine.ts 

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> stateMachine = Machine&lt;FunnelData, FunnelEvent&gt;(
  {
    id: <span class="hljs-string">"funnel-state-machine"</span>,
    initial: <span class="hljs-string">"activity"</span>,
    context: {
      activity: <span class="hljs-literal">undefined</span>,
      attendees: [],
      additional_information: <span class="hljs-literal">undefined</span>,
      payment: <span class="hljs-literal">undefined</span>,
    },
    states: {
      activity: {
        on: {
          SELECT_ACTIVITY: <span class="hljs-string">"register_attendee"</span>,
        },
        exit: [<span class="hljs-string">"setActivity"</span>],
      },
      register_attendee: {
        on: {
          ADD_ATTENDEE: <span class="hljs-string">"register_attendee"</span>,
          ADD_INFO: <span class="hljs-string">"additional_information"</span>,
          SUBMIT_ATTENDEE: <span class="hljs-string">"payment"</span>,
        },
        exit: [<span class="hljs-string">"addattendee"</span>],
      },
      additional_information: {
        on: {
          SUBMIT_ADDITIONNAL_INFORMATION: <span class="hljs-string">"payment"</span>,
        },
        exit: [<span class="hljs-string">"setAdditionalInformation"</span>],
      },
      payment: {
        <span class="hljs-keyword">type</span>: <span class="hljs-string">"final"</span>,
      },
    },
  },
  {
    actions: {
      setActivity: assign(setActvity),
      addattendee: assign(addAttendee),
      setAdditionalInformation: assign(setAddtionalInformation),
    },
  }
);
</code></pre>
<p>you'll notice that the actual implementation with the call to assign is defined in a second parameter to the <code>Machine</code> constructor. This is because Xstate is designed to produce fully <code>serializable</code> state chart definitions,  keeping them agnostic from the actual implementation. </p>
<p>And we're already done with the machine ! Again, thanks to our <code>FunnelProvider</code> we know that this <strong>will</strong> work as designed. The only thing left to do is to implement the steps, and use our render prop pattern to send the data along with the event type ! </p>
<h2 id="steps">Steps</h2>
<p>The steps we build here will be designed to be <em>completely</em> unaware of the funnel itself. Each step will only have one responsibility : render a form and call the callback we pass it as props on submission. This <a target="_blank" href="https://en.wikipedia.org/wiki/Separation_of_concerns">Separation of Concern</a> makes our project more stable, and our component more reusable. </p>
<h3 id="adding-the-steps-to-the-funnel">Adding the steps to the funnel</h3>
<p>Even though we haven't actually implemented the steps yet, we'll start by adding them to the funnel. That way we can clearly see what props we need to pass to our components ! </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> FunnelProvider <span class="hljs-keyword">from</span> <span class="hljs-string">"./acquisition-funnel/FunnelProvider"</span>;
<span class="hljs-keyword">import</span> StateRenderer <span class="hljs-keyword">from</span> <span class="hljs-string">"./acquisition-funnel/StateRenderer"</span>;
<span class="hljs-keyword">import</span> ActivityStep <span class="hljs-keyword">from</span> <span class="hljs-string">"./acquisition-funnel/steps/ActivityStep"</span>;
<span class="hljs-keyword">import</span> AttendeeStep <span class="hljs-keyword">from</span> <span class="hljs-string">"./acquisition-funnel/steps/AttendeeStep"</span>;
<span class="hljs-keyword">import</span> AdditionnalInformationStep <span class="hljs-keyword">from</span> <span class="hljs-string">"./acquisition-funnel/steps/AdditionalInfomationStep"</span>;
<span class="hljs-keyword">import</span> { stateMachine } <span class="hljs-keyword">from</span> <span class="hljs-string">"./acquisition-funnel/state-machine"</span>;
<span class="hljs-keyword">import</span> {
  SELECT_ACTIVITY,
  SUBMIT_ADDITIONNAL_INFORMATION,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"./acquisition-funnel/types"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"App"</span>&gt;
      &lt;FunnelProvider stateMachine={stateMachine}&gt;
        &lt;StateRenderer state=<span class="hljs-string">"activity"</span>&gt;
          {<span class="hljs-function">(<span class="hljs-params">send</span>) =&gt;</span> (
            &lt;ActivityStep
              onSubmit={<span class="hljs-function">(<span class="hljs-params">activity</span>) =&gt;</span> {
                send({
                  <span class="hljs-keyword">type</span>: SELECT_ACTIVITY,
                  data: activity,
                });
              }}
            /&gt;
          )}
        &lt;/StateRenderer&gt;
        &lt;StateRenderer state=<span class="hljs-string">"register_attendee"</span>&gt;
          {<span class="hljs-function">(<span class="hljs-params">send</span>) =&gt;</span> (
            &lt;AttendeeStep
              addAttendee={<span class="hljs-function">(<span class="hljs-params">attendee</span>) =&gt;</span>
                send({ <span class="hljs-keyword">type</span>: <span class="hljs-string">"ADD_ATTENDEE"</span>, data: attendee })
              }
              addInfo={<span class="hljs-function">(<span class="hljs-params">attendee</span>) =&gt;</span> send({ <span class="hljs-keyword">type</span>: <span class="hljs-string">"ADD_INFO"</span>, data: attendee })}
              proceedToPayment={<span class="hljs-function">(<span class="hljs-params">attendee</span>) =&gt;</span>
                send({ <span class="hljs-keyword">type</span>: <span class="hljs-string">"SUBMIT_ATTENDEE"</span>, data: attendee })
              }
            /&gt;
          )}
        &lt;/StateRenderer&gt;
        &lt;StateRenderer state=<span class="hljs-string">"additional_information"</span>&gt;
          {<span class="hljs-function">(<span class="hljs-params">send</span>) =&gt;</span> (
            &lt;AdditionnalInformationStep
              onSubmit={<span class="hljs-function">(<span class="hljs-params">info</span>) =&gt;</span>
                send({
                  <span class="hljs-keyword">type</span>: SUBMIT_ADDITIONNAL_INFORMATION,
                  data: info,
                })
              }
            /&gt;
          )}
        &lt;/StateRenderer&gt;
        &lt;StateRenderer state=<span class="hljs-string">"payment"</span>&gt;
          {<span class="hljs-function">() =&gt;</span> {
            <span class="hljs-keyword">return</span> &lt;h2&gt;payment&lt;/h2&gt;;
          }}
        &lt;/StateRenderer&gt;
      &lt;/FunnelProvider&gt;
    &lt;/div&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>What is important to note here is that we never pass the <code>send</code> function directly to the steps themselves. Instead, to keep things separate,  we define the appropriate callbacks in order to have a clean API for each of our components. </p>
<h3 id="activity-step-and-additional-information-step">Activity Step and Additional Information Step</h3>
<p>Finally let's build the steps ! When we're done with this part the funnel will be fully functional (well except for the payment part, but we'll leave that for another day 😉)</p>
<p>To make our life easier, we'll use <a target="_blank" href="https://formik.org/docs/api/formik">Formik</a> to handles everything form related. If you haven't heard of it yet, it's an awesome library designed to make form building absolutely painless !  </p>
<p>Let's start with the activity step : </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { Activity, ESCAPE_GAME, BOWLING } <span class="hljs-keyword">from</span> <span class="hljs-string">"../types"</span>;
<span class="hljs-keyword">import</span> { Form, Field, ErrorMessage, Formik } <span class="hljs-keyword">from</span> <span class="hljs-string">"formik"</span>;

<span class="hljs-keyword">interface</span> ActivityStepProps {
  onSubmit: <span class="hljs-function">(<span class="hljs-params">activity: Activity | <span class="hljs-string">""</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
}

<span class="hljs-keyword">interface</span> FormValues {
  activity: Activity | <span class="hljs-string">""</span>;
}

<span class="hljs-keyword">const</span> ActivityStep: React.FunctionComponent&lt;ActivityStepProps&gt; = <span class="hljs-function">(<span class="hljs-params">{
  onSubmit,
}</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;Formik
      onSubmit={<span class="hljs-function">(<span class="hljs-params">values: FormValues</span>) =&gt;</span> onSubmit(values.activity)}
      initialValues={{ activity: <span class="hljs-string">""</span> }}
    &gt;
      &lt;Form&gt;
        &lt;h2&gt;Pick an activity &lt;/h2&gt;
        &lt;ErrorMessage name=<span class="hljs-string">"activity"</span> /&gt;
        &lt;label&gt;
          &lt;Field name=<span class="hljs-string">"activity"</span> <span class="hljs-keyword">type</span>=<span class="hljs-string">"radio"</span> value={ESCAPE_GAME} /&gt;
          Escape Game
        &lt;/label&gt;
        &lt;label&gt;
          &lt;Field name=<span class="hljs-string">"activity"</span> <span class="hljs-keyword">type</span>=<span class="hljs-string">"radio"</span> value={BOWLING} /&gt;
          Bowling
        &lt;/label&gt;
        &lt;button <span class="hljs-keyword">type</span>=<span class="hljs-string">"submit"</span>&gt; Next &lt;/button&gt;
      &lt;/Form&gt;
    &lt;/Formik&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ActivityStep;
</code></pre>
<p>And  the <code>AddtionalInformationStep</code> : </p>
<pre><code class="lang-typescript">
# AdditionalInformationStep.ts
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { Form, Field, ErrorMessage, Formik } <span class="hljs-keyword">from</span> <span class="hljs-string">"formik"</span>;

<span class="hljs-keyword">interface</span> AdditionalInformationStepProps {
  onSubmit: <span class="hljs-function">(<span class="hljs-params">activity: <span class="hljs-built_in">string</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
}

<span class="hljs-keyword">interface</span> FormValues {
  additionalInformation: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">const</span> AdditionalInformationStep: React.FunctionComponent&lt;AdditionalInformationStepProps&gt; =
  <span class="hljs-function">(<span class="hljs-params">{ onSubmit }</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> (
      &lt;Formik
        onSubmit={<span class="hljs-function">(<span class="hljs-params">values: FormValues</span>) =&gt;</span>
          onSubmit(values.additionalInformation)
        }
        initialValues={{ additionalInformation: <span class="hljs-string">""</span> }}
      &gt;
        &lt;Form&gt;
          &lt;h2&gt;Enter additional information&lt;/h2&gt;
          &lt;ErrorMessage name=<span class="hljs-string">"additionalInformation"</span> /&gt;
          &lt;label&gt;
            &lt;Field name=<span class="hljs-string">"additionalInformation"</span> /&gt;
          &lt;/label&gt;
          &lt;button <span class="hljs-keyword">type</span>=<span class="hljs-string">"submit"</span>&gt; Proceed to Payment &lt;/button&gt;
        &lt;/Form&gt;
      &lt;/Formik&gt;
    );
  };

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> AdditionalInformationStep;
</code></pre>
<p>Notice how we don't have to think about the funnel at all ! We juste need to use our callback functions.</p>
<h3 id="attendee-step">Attendee Step</h3>
<p>This step is a tad more complex as we need to manage 3 different buttons. As such we won't use the form submission directly. Instead we'll create our own event Handler to call the corresponding callback with the form values depending on the button. </p>
<p>That's how it looks : </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { Attendee } <span class="hljs-keyword">from</span> <span class="hljs-string">"../types"</span>;
<span class="hljs-keyword">import</span> { Field, ErrorMessage, Formik } <span class="hljs-keyword">from</span> <span class="hljs-string">"formik"</span>;

<span class="hljs-keyword">interface</span> AttendeeStepProps {
  addAttendee: <span class="hljs-function">(<span class="hljs-params">attendee: Attendee</span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  proceedToPayment: <span class="hljs-function">(<span class="hljs-params">attendee: Attendee</span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  addInfo: <span class="hljs-function">(<span class="hljs-params">attendee: Attendee</span>) =&gt;</span> <span class="hljs-built_in">void</span>;
}

<span class="hljs-keyword">const</span> AttendeeStep: React.FunctionComponent&lt;AttendeeStepProps&gt; = <span class="hljs-function">(<span class="hljs-params">{
  addAttendee,
  proceedToPayment,
  addInfo,
}</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;Formik
      onSubmit={<span class="hljs-function">(<span class="hljs-params">values</span>) =&gt;</span> {
        proceedToPayment(values);
      }}
      initialValues={{
        name: <span class="hljs-string">""</span>,
        surname: <span class="hljs-string">""</span>,
      }}
    &gt;
      {<span class="hljs-function">(<span class="hljs-params">{ values }</span>) =&gt;</span> (
        &lt;div&gt;
          &lt;h2&gt;Pick an activity &lt;/h2&gt;
          &lt;ErrorMessage name=<span class="hljs-string">"activity"</span> /&gt;
          &lt;label&gt;
            &lt;Field name=<span class="hljs-string">"name"</span> /&gt;
            Name
          &lt;/label&gt;
          &lt;label&gt;
            &lt;Field name=<span class="hljs-string">"surname"</span> /&gt;
            Surname
          &lt;/label&gt;
          &lt;div&gt;
            &lt;button
              onClick={<span class="hljs-function">() =&gt;</span> {
                addAttendee(values);
              }}
            &gt;
              Submit and add another
            &lt;/button&gt;
            &lt;button
              onClick={<span class="hljs-function">() =&gt;</span> {
                proceedToPayment(values);
              }}
            &gt;
              Proceed to payment
            &lt;/button&gt;
            &lt;button
              onClick={<span class="hljs-function">() =&gt;</span> {
                addInfo(values);
              }}
            &gt;
              Enter additional information
            &lt;/button&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      )}
    &lt;/Formik&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> AttendeeStep;
</code></pre>
<p>That's it for the attendee registration step ! </p>
<h2 id="all-done">All Done ✅</h2>
<p>And we're finished ! If you made it here congrats 🥳 Your funnel should be fully functional, and it should look something like this : </p>
<p><em>In this gif I display the context content to make it clearer what is happening behind the scenes</em></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633856858631/pGOB8oXLM.gif" alt="state-machine-2.gif" /></p>
<p>I deployed this demo to Github Pages, you can try it out <a target="_blank" href="https://jbrocher.github.io/hashnode-xstate-part-2-demo/">here</a> ! </p>
<hr />
<h1 id="conclusion">Conclusion</h1>
<p>This concludes our two parts article on how to build an acquisition funnel using a state machine ! I hope you enjoyed it, you should now be well equipped the next time you're asked to build on of those ! </p>
<p>This was my first experience with writing, and I must say I really loved it ! I intend to continue and I welcome any feedback you may have. See you next week for another article  !</p>
<hr />
<h2 id="references">References</h2>
<h3 id="libraries">Libraries</h3>
<ol>
<li><a target="_blank" href="https://www.typescriptlang.org/">Typescript</a></li>
<li><a target="_blank" href="https://xstate.js.org/docs/">Xstate</a></li>
</ol>
<h3 id="state-machines">State machines</h3>
<ol>
<li><a target="_blank" href="https://en.wikipedia.org/wiki/Finite-state_machine">Finite State Machines</a></li>
<li><a target="_blank" href="https://en.wikipedia.org/wiki/Moore_machine">Moore Machine</a></li>
<li><a target="_blank" href="https://www.sciencedirect.com/science/article/pii/0167642387900359/pdf?crasolve=1&amp;iv=da0df9ab046a47c50fc09796188d3871&amp;token=64653761346535313734616363653232316234623135386434633864303936626561323363306361353031633232643564656132383636636562633766626466363436326337666464356434363930623865326134373a373466626265366461653265663965653265656535633234&amp;text=fd021417ae6930a33c67892c4cffc7c4d201f5a3ec4dd83c3b037684ea9fce1e4abcb8f78b5c6afe3c46aac0badd814e5e708b6a449675a5111beaaa89450adf8a40eb3fa21c5cebde2e568d72d9379548b8587469d59261435363d9a1a71f4baa7d9dd42bf391169cd66c4a823bb966f470faf82b91dcf431ac88b74a22ad1a31e4c1dabef0e6386e24b048ba23990effb1db125b1dd0de3a1b8356df1b0fc3d504589c53cfc898872f10ac1ecadac6b497ffebf4cbe994bedad04dfa0b8872893129c0ae67590ed01885b1d537a56f5eed3a4b53ed72dbdf8f63c44d3a6337b7f34f70e179f28ef1a5d528b6410b534fb869bfae79455110cb031c47468f3b905b43a23202f02f0d63944e4d59ee4ff3985ffdc17ad0273794b16557fea875c09154493946d7c5419a54dedc4ebf66&amp;original=3f">State Charts</a></li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Building an acquisition Funnel in React with Xstate - Part 1]]></title><description><![CDATA[Building (and maintaining) an acquisition funnel is something that most development teams will be asked to do at some point. This is no small task as this funnel will often be the  main entry point to the company products or services. This means that...]]></description><link>https://dev.indooroutdoor.io/building-an-acquisition-funnel-in-react-with-xstate</link><guid isPermaLink="true">https://dev.indooroutdoor.io/building-an-acquisition-funnel-in-react-with-xstate</guid><category><![CDATA[React]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[spa]]></category><category><![CDATA[forms]]></category><dc:creator><![CDATA[Jb Rocher]]></dc:creator><pubDate>Sun, 03 Oct 2021 10:55:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1633858974903/KOwSY4-dy.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Building (and maintaining) an acquisition funnel is something that most development teams will be asked to do at some point. This is no small task as this funnel will often be the  main entry point to the company products or services. This means that such a funnel needs to be completely reliable to avoid losing customers to bugs, and fully flexible to allow the marketing team to optimize the conversion rate. </p>
<p>In this two-part article we'll see how to accomplish that easily by using a strong programming pattern: a Finite State Machine. We'll be using React and Xstate for the implementation, but the principles can be applied to any stack really. </p>
<p>Today in part 1, we'll see how to implement the state machine that powers the funnel, and building the actual steps and handling user data will be covered in <a target="_blank" href="https://dev.indooroutdoor.io/building-an-acquisition-funnel-in-react-with-xstate-part-2">part 2</a>. </p>
<p>You can find the code <a target="_blank" href="You can find the full implementation [here](https://github.com/jbrocher/funnel-state-machine">in this repository</a>. I encourage you to clone the project and play with it !</p>
<hr />
<h1 id="use-case">Use Case</h1>
<p>As always when it comes to programming, the best way to learn something is to get our hands dirty. </p>
<p>Let's say we work for a team building company, and the marketing team wants us to build an acquisition funnel that allows our customers to choose an activity and register a bunch of people. </p>
<p>They need us to implement the following funnel :</p>
<ul>
<li>Step 1: A pick list to choose a type of activity ;</li>
<li>Step 2: A form to register a person. The customer must be able to go through this step any number of time. We must be able to go straight to step 4 from this step.</li>
<li>Step 3 (optional): A form to give additional information ; </li>
<li>Step 4: payment: 💰</li>
</ul>
<p>In part 1 we'll try to implement the different steps of the tunnel without actually submitting anything. We'll see how we handle that in <a target="_blank" href="https://dev.indooroutdoor.io/building-an-acquisition-funnel-in-react-with-xstate-part-2">part 2</a> :D</p>
<hr />
<h1 id="a-word-on-finite-state-machines">A word on finite state machines</h1>
<p><em>Note: Although I recommend reading it, this section isn't strictly necessary to be able to implement our solution. So if you'd like to jump into the action skip to the <a class="post-section-overview" href="#building-the-funnel">Implementation</a></em></p>
<p>Before we get down to it, I'd like to justify our modeling choice: the finite state machine.  I'll go into more detail in a separate article, but for now let's just get a quick overview of this pattern.</p>
<h2 id="what-is-a-finite-state-machine">What is a finite state machine ?</h2>
<p>Let's take a shortcut and quote the <a target="_blank" href="https://en.wikipedia.org/wiki/Finite-state_machine">Wikipedia article</a> : </p>
<blockquote>
<p>A finite-state machine (FSM) or finite-state automaton (FSA, plural: automata), finite automaton, or simply a state machine, is a mathematical model of computation. It is an abstract machine that can be in exactly one of a finite number of states at any given time.The FSM can change from one state to another in response to some inputs; the change from one state to another is called a transition.</p>
</blockquote>
<p>Basically it is a robust, readable and deterministic way of modeling process. For example here how we coud model a traffic light using a State machine:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633256062967/1KjplJ7Yf.png" alt="Traffic light.png" /></p>
<p>Easy ! </p>
<h2 id="why-use-a-state-machine">Why use a state machine</h2>
<p>The important bit in the definition above is that the machine is <strong>guaranteed</strong> to be in only one state at a time. This guarantee is a tremendous step forward in solving our use case. </p>
<p>It's also really easy to apply this model to a funnel, by simply mapping each step to a state, because it does exactly what we want: Displaying a single step at any given time.</p>
<p>Furthermore, the state of our funnel will be decided by the sequence of events triggered by the users, and will be fully deterministic.</p>
<p>This means that if we draw the correct state diagram, and feed it to a component able to execute a state machine, we can be certain that the funnel will behave as we want it to.</p>
<h2 id="different-types-of-state-machine">Different types of state machine</h2>
<p>There are two different types of finite state machine : </p>
<ul>
<li>The  <a target="_blank" href="https://en.wikipedia.org/wiki/Moore_machine">Moore machine</a>: Where the current output of the machine directly matches the state.</li>
<li>The <a target="_blank" href="https://en.wikipedia.org/wiki/Mealy_machine">Mealy machine</a>: Where the output depends both on the current state and the last input.</li>
</ul>
<p>You choose the type to use depending on your use case. A Mealy machine allows to reduce the number of state for the same number of different outputs, while a Moore machine is often more straight forward and easy to read. </p>
<p>Apart from the <em>finite</em> states machine there are other kinds of machine : </p>
<ul>
<li>The turing machine: We'll need a separate article for that one ;)</li>
<li><a target="_blank" href="https://xstate.js.org/docs/guides/introduction-to-state-machines-and-statecharts/">StateChart</a>: This exploit the programming language features (In this case javascript): to go beyond the capabilities of a "normal" finite state machine. This will come handy in <a target="_blank" href="https://dev.indooroutdoor.io/building-an-acquisition-funnel-in-react-with-xstate-part-2">Part 2</a> when we need to handle user input :) </li>
</ul>
<p>For now, as our only concern is the sequence of steps of the funnel, a Moore Machine will suffice !</p>
<h2 id="modeling-the-funnel">Modeling the funnel</h2>
<p>Going back to our requirements, modeling our acquisition funnel is now completely straightforward : </p>
<div class="hn-embed-widget" id="state-machine-part-1"></div><p><em>This player is generated using the <a target="_blank" href="https://xstate.js.org/viz">xstate vizualizer</a> from the machine implementation found in this article</em></p>
<hr />
<h1 id="building-the-funnel">Building the funnel</h1>
<p>Now that we're confident in the reliability of our model, let's implement it! </p>
<h2 id="setting-up-the-project">Setting up the project</h2>
<ul>
<li>Install yarn</li>
<li>Create the app:  <code>yarn create react-app --template typescript</code></li>
<li>Install dependencies : <code>yarn add xstate</code></li>
<li>Create a directory to store our funnel and the components : <code>mkdir src/acquisition-funnel</code></li>
</ul>
<h2 id="writing-the-machine">Writing the machine</h2>
<p>Let's start by translating our state chart into code. Funny thing though: The diagram above was actually generated <em>from the code</em> by the xstate vizualizer. Automatic documentation ! Yay ! </p>
<p>First let's create <code>src/acquisition-funnel/types.ts</code> where we'll the events types : </p>
<pre><code class="lang-typescript">
# types.ts
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> SELECT_ACTIVITY = <span class="hljs-string">"SELECT_ACTIVITY"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> SUBMIT_ATTENDEE = <span class="hljs-string">"SUBMIT_ATTENDEE"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> ADD_ATTENDEE = <span class="hljs-string">"ADD_ATTENDEE"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> ADD_INFO = <span class="hljs-string">"ADD_INFO"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> SUBMIT_ADDITIONNAL_INFORMATION = <span class="hljs-string">"SUBMIT_ADDITIONNAL_INFORMATION"</span>;
</code></pre>
<p>It is always a good idea to store constants into variable. </p>
<p>Now let's get to the machine itself. Create a file called <code>state-machine.ts</code> in <code>src/acquisition-funnel</code>, and add the code below to it. </p>
<pre><code class="lang-typescript">
# state-machine.ts

<span class="hljs-keyword">import</span> { Machine } <span class="hljs-keyword">from</span> <span class="hljs-string">"xstate"</span>;
<span class="hljs-keyword">import</span> {
  SELECT_ACTIVITY,
  SUBMIT_ATTENDEE,
  ADD_ATTENDEE,
  ADD_INFO,
  SUBMIT_ADDITIONNAL_INFORMATION,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"./types"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> stateMachine = Machine({
  id: <span class="hljs-string">"funnel-state-machine"</span>,
  initial: <span class="hljs-string">"activity"</span>,
  states: {
    activity: {
      on: {
        [SELECT_ACTIVITY]: <span class="hljs-string">"register_attendee"</span>,
      },
    },
    register_attendee: {
      on: {
        [ADD_ATTENDEE]: <span class="hljs-string">"register_attendee"</span>,
        [ADD_INFO]: <span class="hljs-string">"additional_information"</span>,
        [SUBMIT_ATTENDEE]: <span class="hljs-string">"payment"</span>,
      },
    },
    additional_information: {
      on: {
        [SUBMIT_ADDITIONNAL_INFORMATION]: <span class="hljs-string">"payment"</span>,
      },
    },
    payment: {
      <span class="hljs-keyword">type</span>: <span class="hljs-string">"final"</span>,
    },
  },
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> stateMachine;
</code></pre>
<p>As you can see each state of our diagram is listed, and the available transition for each state are described by the <code>on</code> attribute. </p>
<h2 id="building-the-funnelprovider">Building the <code>FunnelProvider</code></h2>
<p>Now that our state machine is ready, we need to design a component that handles rendering the  step components associated with each state.  In order to do that we'll use the <a target="_blank" href="https://kentcdodds.com/blog/compound-components-with-react-hooks">Compound component</a> pattern. </p>
<p>The <code>FunnelProvider</code> will hold the current state in a context, making it available to the children <code>State</code> components. </p>
<p>Each state component will only render if if the current state match, getting the rendering and the state machine in sync  !</p>
<p>First add a file called <code>FunnelProvider.ts</code> to <code>src/acquisition-funnel</code>, and add the following code to it : </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">import</span> React, { useContext } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { StateMachine, State } <span class="hljs-keyword">from</span> <span class="hljs-string">"xstate"</span>;
<span class="hljs-keyword">import</span> { useMachine } <span class="hljs-keyword">from</span> <span class="hljs-string">"@xstate/react"</span>;

<span class="hljs-comment">// We use a generic type to be able to handle</span>
<span class="hljs-comment">// any shape of context with type checking</span>
<span class="hljs-keyword">interface</span> FunnelProviderProps&lt;TContext&gt; {
  stateMachine: StateMachine&lt;TContext, <span class="hljs-built_in">any</span>, <span class="hljs-built_in">any</span>&gt;;
  children: React.ReactNode;
}

<span class="hljs-keyword">interface</span> FunnelContextValue {
  currentState: State&lt;<span class="hljs-built_in">any</span>&gt;;
  send: <span class="hljs-function">(<span class="hljs-params">state: <span class="hljs-built_in">string</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
}

<span class="hljs-keyword">const</span> FunnelContext = React.createContext({} <span class="hljs-keyword">as</span> FunnelContextValue);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">FunnelProvider</span>&lt;<span class="hljs-title">TContext</span>&gt;(<span class="hljs-params">{
  stateMachine,
  children,
}: FunnelProviderProps&lt;TContext&gt;</span>): <span class="hljs-title">React</span>.<span class="hljs-title">ReactElement</span> </span>{
  <span class="hljs-keyword">const</span> [current, send] = useMachine(stateMachine);
  <span class="hljs-keyword">return</span> (
    &lt;FunnelContext.Provider value={{ currentState: current, send }}&gt;
      {children}
    &lt;/FunnelContext.Provider&gt;
  );
}

<span class="hljs-comment">// This is a common patter to avoid import </span>
<span class="hljs-comment">// the constext in every consumer</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useFunnel = <span class="hljs-function">() =&gt;</span> useContext(FunnelContext);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> FunnelProvider;
</code></pre>
<p>Then create a <code>StateRenderer.tsx</code> file in <code>src/acquisition-funnel</code>  and add the following code to id : </p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { useFunnel } <span class="hljs-keyword">from</span> <span class="hljs-string">"./FunnelProvider"</span>;

<span class="hljs-keyword">interface</span> StateProps {
  state: <span class="hljs-built_in">string</span>;
  children: <span class="hljs-function">(<span class="hljs-params">send: <span class="hljs-built_in">any</span></span>) =&gt;</span> React.ReactNode;
}

<span class="hljs-keyword">const</span> StateRenderer: React.FunctionComponent&lt;StateProps&gt; = <span class="hljs-function">(<span class="hljs-params">{
  state,
  children,
}</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { currentState, send } = useFunnel();

  <span class="hljs-keyword">return</span> currentState.matches(state) ? (
    &lt;div&gt;{children(send)}&lt;/div&gt;
  ) : (
    &lt;div&gt;&lt;/div&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> StateRenderer;
</code></pre>
<p>Here we use the <a target="_blank" href="https://reactjs.org/docs/render-props.html">Render props</a> pattern to allow passing the send property to the the compontent render into State. We'll se why it's useful in the next step</p>
<h3 id="putting-it-all-together">Putting it all together</h3>
<p>Now that we've got our state-machine and our compound component <code>FunnelProvider</code> and <code>StateRenderer</code>, all that's left to do is choosing what to render. </p>
<p>Add the following code to <code>App.tsx</code> : </p>
<pre><code class="lang-typescript">
# App.tsx

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> FunnelProvider <span class="hljs-keyword">from</span> <span class="hljs-string">"./acquisition-funnel/FunnelProvider"</span>;
<span class="hljs-keyword">import</span> StateRenderer <span class="hljs-keyword">from</span> <span class="hljs-string">"./acquisition-funnel/StateRenderer"</span>;
<span class="hljs-keyword">import</span> RegistrationStep <span class="hljs-keyword">from</span> <span class="hljs-string">"./acquisition-funnel/RegistrationStep"</span>;
<span class="hljs-keyword">import</span> { stateMachine } <span class="hljs-keyword">from</span> <span class="hljs-string">"./acquisition-funnel/state-machine"</span>;
<span class="hljs-keyword">import</span> {
  SELECT_ACTIVITY,
  SUBMIT_ATTENDEE,
  ADD_ATTENDEE,
  ADD_INFO,
  SUBMIT_ADDITIONNAL_INFORMATION,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"./acquisition-funnel/types"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"App"</span>&gt;
      &lt;FunnelProvider stateMachine={stateMachine}&gt;
        &lt;StateRenderer state=<span class="hljs-string">"activity"</span>&gt;
          {<span class="hljs-function">(<span class="hljs-params">send</span>) =&gt;</span> {
            <span class="hljs-keyword">return</span> (
              &lt;div&gt;
                &lt;h2&gt;Activity Step&lt;/h2&gt;
                &lt;button onClick={<span class="hljs-function">() =&gt;</span> send(SELECT_ACTIVITY)}&gt;next&lt;/button&gt;
              &lt;/div&gt;
            );
          }}
        &lt;/StateRenderer&gt;
        &lt;StateRenderer state=<span class="hljs-string">"register_attendee"</span>&gt;
          {<span class="hljs-function">(<span class="hljs-params">send</span>) =&gt;</span> (
            &lt;RegistrationStep
              add_participant={<span class="hljs-function">() =&gt;</span> send(ADD_ATTENDEE)}
              additional_information={<span class="hljs-function">() =&gt;</span> send(ADD_INFO)}
              proceed_to_payment={<span class="hljs-function">() =&gt;</span> send(SUBMIT_ATTENDEE)}
            /&gt;
          )}
        &lt;/StateRenderer&gt;
        &lt;StateRenderer state=<span class="hljs-string">"additional_information"</span>&gt;
          {<span class="hljs-function">(<span class="hljs-params">send</span>) =&gt;</span> {
            <span class="hljs-keyword">return</span> (
              &lt;div&gt;
                &lt;h2&gt;Additional information&lt;/h2&gt;
                &lt;button onClick={<span class="hljs-function">() =&gt;</span> send(SUBMIT_ADDITIONNAL_INFORMATION)}&gt;
                  next
                &lt;/button&gt;
              &lt;/div&gt;
            );
          }}
        &lt;/StateRenderer&gt;
        &lt;StateRenderer state=<span class="hljs-string">"payment"</span>&gt;
          {<span class="hljs-function">() =&gt;</span> {
            <span class="hljs-keyword">return</span> &lt;h2&gt;payment&lt;/h2&gt;;
          }}
        &lt;/StateRenderer&gt;
      &lt;/FunnelProvider&gt;
    &lt;/div&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Thanks to all the work we've done this step is really easy: We simply use the StateRenderer inside our provider to render the active step. The StateRenderer passes the necessary callbacks to move into the funnel using our render prop. </p>
<p>The Registration Step is a tad more complex because I wanted us to be able to see the loop in the registration step. That's why it's defined in its own component : </p>
<pre><code class="lang-Typescript">
<span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">interface</span> RegistrationProps {
  add_participant: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
  additional_information: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
  proceed_to_payment: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">void</span>;
}
<span class="hljs-keyword">const</span> RegistrationStep: React.FunctionComponent&lt;RegistrationProps&gt; = <span class="hljs-function">(<span class="hljs-params">{
  add_participant,
  additional_information,
  proceed_to_payment,
}</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> [counter, setCounter] = useState(<span class="hljs-number">1</span>);

  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;h2&gt;Register participant <span class="hljs-built_in">number</span> {counter}&lt;/h2&gt;
      &lt;button
        onClick={<span class="hljs-function">() =&gt;</span> {
          setCounter(<span class="hljs-function">(<span class="hljs-params">counter</span>) =&gt;</span> counter + <span class="hljs-number">1</span>);
          add_participant();
        }}
      &gt;
        Continue registering
      &lt;/button&gt;
      &lt;button onClick={additional_information}&gt;
        Add additional information
      &lt;/button&gt;
      &lt;button onClick={proceed_to_payment}&gt;Proceed to Payment&lt;/button&gt;
    &lt;/div&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> RegistrationStep;
</code></pre>
<p>We simply increment a counter each time we call <code>add_participant</code></p>
<hr />
<p>And we're done ! The funnel we've built is completely deterministic and meets the requirements. If we need to change the flow, all we have to do is update <code>state-machine.tx</code>. Isn't that great ? </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633871996762/pc-zlKobi.gif" alt="xstate-part-1.gif" /></p>
<p><a target="_blank" href="https://jbrocher.github.io/hashnode-xstate-part-1-demo/">Try it out ! </a></p>
<h1 id="conclusion">Conclusion</h1>
<p>That's it for today ! Hope you enjoyed part 1, we've accomplished a lot already ! 
Of course we can't really submit anything yet, head over to <a target="_blank" href="https://dev.indooroutdoor.io/building-an-acquisition-funnel-in-react-with-xstate-part-2">part 2</a> to discover how we handle that ! </p>
]]></content:encoded></item></channel></rss>