<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mathieu Kerjouan</title>
    <description>The latest articles on DEV Community by Mathieu Kerjouan (@niamtokik).</description>
    <link>https://dev.to/niamtokik</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1302415%2Ff02d4ee9-14d9-45ec-966a-6bb53e0e3240.jpeg</url>
      <title>DEV Community: Mathieu Kerjouan</title>
      <link>https://dev.to/niamtokik</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/niamtokik"/>
    <language>en</language>
    <item>
      <title>Sandbox#1: Flutter Application Design First Steps</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Fri, 12 Jun 2026 09:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/sandbox1-flutter-application-design-first-steps-35ge</link>
      <guid>https://dev.to/niamtokik/sandbox1-flutter-application-design-first-steps-35ge</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Epic things start with small humble steps. Pay respect to your beginnings. And if you're just starting out, know that it's OK to be sucky. To be small. To be messy and chaotic. Just make sure to never ever stop dreaming.&lt;/p&gt;

&lt;p&gt;-- Vishen Lakhiani&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let start another chapter of this journey with Dart by creating a mobile application with &lt;a href="https://flutter.dev/" rel="noopener noreferrer"&gt;Flutter&lt;/a&gt;. For this post, a really simple application will be created called &lt;code&gt;sandbox&lt;/code&gt;. Instead of adding some interactive part, like sending/receiving data from a backend, let simply design an application based on a wireframe.&lt;/p&gt;

&lt;h1&gt;
  
  
  Bootstrapping
&lt;/h1&gt;

&lt;p&gt;As usual, I'm using &lt;code&gt;asdf&lt;/code&gt; to install Flutter on my laptop but it can also be installed manually by following the &lt;a href="https://docs.flutter.dev/install/quick" rel="noopener noreferrer"&gt;Flutter Quick Install documentation&lt;/a&gt;. This new project will be called &lt;code&gt;sandbox&lt;/code&gt;, a good name for a draft.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;flutter create sandbox
&lt;span class="go"&gt;Creating project sandbox...
Resolving dependencies in `sandbox`... 
Downloading packages... 
Got dependencies in `sandbox`.
Wrote 131 files.

All done!
You can find general documentation for Flutter at: https://docs.flutter.dev/
Detailed API documentation is available at: https://api.flutter.dev/
If you prefer video documentation, consider: https://www.youtube.com/c/flutterdev

In order to run your application, type:

&lt;/span&gt;&lt;span class="gp"&gt;  $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;sandbox
&lt;span class="gp"&gt;  $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;flutter run
&lt;span class="go"&gt;
Your application code is in sandbox/lib/main.dart.

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;sandbox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Wireframe and Prototype
&lt;/h1&gt;

&lt;p&gt;My career is from network and system administration, backend and distributed development, when it comes to design something, I'm thinking like a technician/engineer: I don't care about the look, it should work. In a previous publication, rock-paper-scissors data structure and primitive have been designed, why not starting with a kind of interface for this game?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1f69igd0hrdsbrxifzzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1f69igd0hrdsbrxifzzo.png" alt="Prototype made with Figma" width="435" height="846"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Creating a prototype or a wireframe with Figma is painful. Really. I still don't believe such kind of "application" can be so successful. Anyway, after a long fight, and without any idea how to expert a layer correctly, I finally decided to do a screenshot of the page. Yeah. Ridiculous. If you are a developer at Figma, are you really using your software? I mean, exporting a selected area can be done quite easily with &lt;a href="https://app.diagrams.net/" rel="noopener noreferrer"&gt;draw.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Anyway, the goal here is to reproduce this with Flutter. No need for interactive feature, just a raw prototype.&lt;/p&gt;

&lt;h1&gt;
  
  
  First Version
&lt;/h1&gt;

&lt;p&gt;Someone said "start humble", this is the first version, with lot of duplicated code an crappy colors, but it should give the big picture of the application.&lt;/p&gt;

&lt;p&gt;The top bar will be used for the menu, to switch to another mode or game for example. It contains the title of the current page. At this time, it will do nothing.&lt;/p&gt;

&lt;p&gt;Then comes the opponent container. This part will be empty at first, but in the future, it could be an animated area, where the final shape of the opponent will be loaded.&lt;/p&gt;

&lt;p&gt;The third container will contain the final result, when both players selected the shape, and the result should be displayed. If this application is used also by someone else (remotely), one player can see "loss", while the other one will see "win". If the shapes are the same, both players will see "draw".&lt;/p&gt;

&lt;p&gt;The fourth container is where the player will select the action. Three shapes are available, rock, paper and scissors. When tapping on one of them, it will send it to a remote server (if playing against someone else), or simply update the state of the application (if playing against the machine).&lt;/p&gt;

&lt;p&gt;Finally, the last container is the bottom bar. It will be removed in the second version because I think an user can click by mistake on one of the button instead of selecting one shape. Let begins the implementation. Nothing really fancy at first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/material.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting part is present in &lt;code&gt;MyApp()&lt;/code&gt; class where we are defining the different layer of the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MaterialApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;debugShowCheckedModeBanner:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;home:&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;appBar:&lt;/span&gt; &lt;span class="n"&gt;AppBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;leading:&lt;/span&gt; &lt;span class="n"&gt;IconButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
          &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Jan Ken Pon"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;[&lt;/span&gt;
            &lt;span class="n"&gt;Expanded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;InkWell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                  &lt;span class="nl"&gt;onTap:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
                  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Ink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;width:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;grey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"opponent choice"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                  &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Expanded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;InkWell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                  &lt;span class="nl"&gt;onTap:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
                  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Ink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;width:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;amber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                  &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Expanded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                  &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;[&lt;/span&gt;
                    &lt;span class="n"&gt;Expanded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                      &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;InkWell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nl"&gt;onTap:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
                        &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Ink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                          &lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;width:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"rock"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                        &lt;span class="p"&gt;),&lt;/span&gt;
                      &lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="n"&gt;Expanded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                      &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;InkWell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nl"&gt;onTap:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
                        &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Ink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                          &lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;width:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"paper"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                        &lt;span class="p"&gt;),&lt;/span&gt;
                      &lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="n"&gt;Expanded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                      &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;InkWell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nl"&gt;onTap:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
                        &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Ink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                          &lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;width:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"scissors"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                        &lt;span class="p"&gt;),&lt;/span&gt;
                      &lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="p"&gt;),&lt;/span&gt;
                  &lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nl"&gt;bottomNavigationBar:&lt;/span&gt; &lt;span class="n"&gt;BottomNavigationBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;items:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;BottomNavigationBarItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;star&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"Home"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;BottomNavigationBarItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;star&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"Jan Ken Pon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I know, this is a disgusting piece of code. The level of indentation is insane and the duplicated code is crazy, but at least, it works. What kind of classes I used here?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://api.flutter.dev/flutter/material/Scaffold-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Scaffold&lt;/code&gt;&lt;/a&gt; class is used to configure the visual layout. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://api.flutter.dev/flutter/material/AppBar-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;AppBar()&lt;/code&gt;&lt;/a&gt; class is instantiated in the &lt;code&gt;Scaffold&lt;/code&gt; object (&lt;a href="https://api.flutter.dev/flutter/material/Scaffold/appBar.html" rel="noopener noreferrer"&gt;&lt;code&gt;appBar&lt;/code&gt;&lt;/a&gt; attribute) and configure the top bar of the application. The menu button on the left currently don't work. The title is a simple &lt;code&gt;Text()&lt;/code&gt; object;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;AppBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;leading:&lt;/span&gt; &lt;span class="n"&gt;IconButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.flutter.dev/flutter/material/BottomNavigationBar-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;BottomNavigationBar&lt;/code&gt;&lt;/a&gt; is also instantiated in &lt;code&gt;Scaffold&lt;/code&gt; in the &lt;a href="https://api.flutter.dev/flutter/material/Scaffold/bottomNavigationBar.html" rel="noopener noreferrer"&gt;&lt;code&gt;bottomNavigationBar&lt;/code&gt;&lt;/a&gt; attribute. This object is also made of a list of Widgets defined in the &lt;code&gt;items&lt;/code&gt; attribute. At least 2 items must be created, in our case, two &lt;a href="https://api.flutter.dev/flutter/widgets/BottomNavigationBarItem-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;BottomNavigationBarItem&lt;/code&gt;&lt;/a&gt; are created;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;BottomNavigationBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;items:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
   &lt;span class="n"&gt;BottomNavigationBarItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="c1"&gt;// ...&lt;/span&gt;
   &lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="n"&gt;BottomNavigationBarItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="c1"&gt;// ...&lt;/span&gt;
   &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.flutter.dev/flutter/widgets/Column-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Column&lt;/code&gt;&lt;/a&gt; class is used in the &lt;a href="https://api.flutter.dev/flutter/material/Scaffold/body.html" rel="noopener noreferrer"&gt;&lt;code&gt;body&lt;/code&gt;&lt;/a&gt; attribute of the &lt;code&gt;Scaffold&lt;/code&gt; object. This is where the user will be able to see the actions and play with the application. A list of &lt;code&gt;Widget&lt;/code&gt; can be provided using the &lt;code&gt;children&lt;/code&gt;&amp;nbsp;attributes, each one will divide the available space. So, in our case, we need 3 "lines", one for the opponent, one for the result and the last one for the user selection;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.flutter.dev/flutter/widgets/Container-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Expanded&lt;/code&gt;&lt;/a&gt; class is used on every "line" to use all the space available from each line;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Expanded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.flutter.dev/flutter/widgets/Center-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Center&lt;/code&gt;&lt;/a&gt; is then used to vertically and horizontally center all elements;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;InkWell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.flutter.dev/flutter/material/InkWell-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;InkWell&lt;/code&gt;&lt;/a&gt; classes are then used to define a place that can react to user touch, a bit like a button, but with more flexibility;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;InkWell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;onTap:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Ink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.flutter.dev/flutter/material/Ink-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Ink&lt;/code&gt;&lt;/a&gt; classes are finally used to set the &lt;a href="https://api.flutter.dev/flutter/material/Ink/height.html" rel="noopener noreferrer"&gt;&lt;code&gt;height&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://api.flutter.dev/flutter/material/Ink/width.html" rel="noopener noreferrer"&gt;&lt;code&gt;width&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://api.flutter.dev/flutter/material/MaterialApp/color.html" rel="noopener noreferrer"&gt;&lt;code&gt;color&lt;/code&gt;&lt;/a&gt; of each active elements;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Ink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;width:&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;infinity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;amber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://api.flutter.dev/flutter/widgets/Row-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Row&lt;/code&gt;&lt;/a&gt; is also used for the shapes user layer, where the user can select &lt;code&gt;rock&lt;/code&gt;, &lt;code&gt;paper&lt;/code&gt; or &lt;code&gt;scissors&lt;/code&gt;. The goal of this class is to split the available space in rows.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This application can be compiled and installed on the Android emulator. Here a screenshot of this first application draft from Android (API 36.0).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3p82ep5tl05fdfv2xhe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3p82ep5tl05fdfv2xhe.png" alt="Design First Version" width="800" height="1778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The colors have been set simply to see where the important information will be displayed. The same colors will stay there until we have something functional.&lt;/p&gt;

&lt;p&gt;With this first version, we also have an idea of the essential bricks we can reuse in the future. It means the next iteration will remove some part of this design, create reusable functions and likely add a bit of interactivity.&lt;/p&gt;

&lt;p&gt;Have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@tetrakiss?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Arseny Togulev&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/white-robot-MECKPoKJYjM?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>android</category>
      <category>mobile</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Encoding and Decoding JSON in Dart</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Thu, 11 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/encoding-and-decoding-json-in-dart-b8m</link>
      <guid>https://dev.to/niamtokik/encoding-and-decoding-json-in-dart-b8m</guid>
      <description>&lt;p&gt;Is it really necessary to introduce &lt;a href="https://json.org/" rel="noopener noreferrer"&gt;JSON&lt;/a&gt; in 2026? Any developers had to deal with JSON at least one time in his career. Anyway, JSON (JavaScript Object Notation) is an open standard format designed for web development usage. In fine, it became one of the most used data format.&lt;/p&gt;

&lt;p&gt;Before working on more challengin but interesting serializer like &lt;a href="https://cbor.io/" rel="noopener noreferrer"&gt;CBOR&lt;/a&gt; or &lt;a href="https://protobuf.dev/" rel="noopener noreferrer"&gt;Protocol Buffer&lt;/a&gt;, let take a moment to learn how use JSON in Dart.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When one needs to encode or decode JSON, two ways existing, using the &lt;a href="https://api.dart.dev/dart-convert/JsonCodec-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonCodec()&lt;/code&gt;&lt;/a&gt; class or use the specific class for encoding (&lt;a href="https://api.dart.dev/dart-convert/JsonEncoder-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonEncoder()&lt;/code&gt;&lt;/a&gt;) or for decoding (&lt;a href="https://api.dart.dev/dart-convert/JsonDecoder-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonDecoder()&lt;/code&gt;&lt;/a&gt;). In both case, the result will be the same.&lt;/p&gt;

&lt;h1&gt;
  
  
  Encoding
&lt;/h1&gt;

&lt;p&gt;JSON encoder is directly integrated in the Dart SDK, and can be included in your code by simply importing &lt;a href="https://api.dart.dev/dart-convert/" rel="noopener noreferrer"&gt;&lt;code&gt;dart:convert&lt;/code&gt;&lt;/a&gt;&amp;nbsp;package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let create a first function called &lt;code&gt;encode()&lt;/code&gt; to encode any kind of object using the &lt;a href="https://api.dart.dev/dart-convert/JsonCodec/encode.html" rel="noopener noreferrer"&gt;&lt;code&gt;json.encode()&lt;/code&gt;&lt;/a&gt; interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-- &lt;/span&gt;&lt;span class="si"&gt;${label}&lt;/span&gt;&lt;span class="s"&gt;:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;encoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let create another function called &lt;code&gt;encode2()&lt;/code&gt;. This one will instantiate an object from the &lt;a href="https://api.dart.dev/dart-convert/JsonEncoder-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonEncoder()&lt;/code&gt;&lt;/a&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonEncoder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-- &lt;/span&gt;&lt;span class="si"&gt;${label}&lt;/span&gt;&lt;span class="s"&gt;:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;encoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, we have two ways to encode our objects, the main entry-point is still missing though. Let fix that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"== encode with JsonCodec"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"integer(1)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"string(test)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"list()"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"list(1,2,3)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"map()"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"map(a, 1)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"float(0.001)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"bool(true)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"bool(false)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"encode with JsonEncoder"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"integer(1)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"string(test)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"list()"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"list(1,2,3)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"map()"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"map(a, 1)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"float(0.001)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"bool(true)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;encode2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"bool(false)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The time has come to execute this program and see the result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run bin/encode.dart
&lt;span class="go"&gt;== encode with JsonCodec
-- integer(1):
1

-- string(test):
"test"

-- list():
[]

-- list(1,2,3):
[1,2,3]

-- map():
{}

-- map(a, 1):
{"a":1}

-- float(0.001):
0.001

-- bool(true):
true

-- bool(false):
false

encode with JsonEncoder
-- integer(1):
1

-- string(test):
"test"

-- list():
[]

-- list(1,2,3):
[1,2,3]

-- map():
{}

-- map(a, 1):
{"a":1}

-- float(0.001):
0.001

-- bool(true):
true

-- bool(false):
false
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks okay, the main Dart types are supported, but what will happen if we give an unsupported type to the encoder?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code is simple, we pass a raw &lt;code&gt;Object()&lt;/code&gt;  to &lt;a href="https://api.dart.dev/dart-convert/JsonCodec/encode.html" rel="noopener noreferrer"&gt;&lt;code&gt;json.encode()&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart bin/encode_exception.dart
&lt;span class="go"&gt;Uncaught Error, error: Error: Converting object to an encodable object failed: Instance of 'Object'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doh, it produces an error! In fact, this is normal, the JSON encoder does not know how to deal with such objects. JSON only support a subset of all Dart type, so, in this case, we need to find a way to convert (serialize) similar objects to something compatible with JSON. In most of the case, it will be converted as &lt;code&gt;String()&lt;/code&gt;, perhaps using Base64 or something similar.&lt;/p&gt;

&lt;p&gt;To illustrate that, let create a new class called &lt;code&gt;Tuple&lt;/code&gt;, it will contain two public attributes, a &lt;code&gt;left&lt;/code&gt; and a &lt;code&gt;right&lt;/code&gt; one. Both are &lt;a href="https://api.dart.dev/dart-core/String-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;String()&lt;/code&gt;&lt;/a&gt;. To represent this kind of object in JSON we can easily use a Map containing at least two fields, one for the left and one of the right. Adding a method called &lt;code&gt;toJson()&lt;/code&gt; here will help us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tuple&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"_object"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Tuple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"left"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"right"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JSON encoder needs a way to know how to convert this new object, it can be done by creating a simple function like the following one. The idea is to check the type of the object passed as argument and to return another object compatible with the JSON format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;toEncodable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;toEncodable()&lt;/code&gt; function can be passed to the &lt;a href="https://api.dart.dev/dart-convert/JsonCodec/JsonCodec.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonCodec&lt;/code&gt;&lt;/a&gt; constructor via the &lt;a href="https://api.dart.dev/dart-convert/JsonEncoder/JsonEncoder.html" rel="noopener noreferrer"&gt;&lt;code&gt;toEncodable&lt;/code&gt;&lt;/a&gt; attribute. The rest is pure testing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonCodec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;toEncodable:&lt;/span&gt; &lt;span class="n"&gt;toEncodable&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;encoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Left1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Right1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let execute the code and check the result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart bin/encode_encable.dart
&lt;span class="go"&gt;[1,"test","Instance of 'Object'",{"_object":"Tuple","left":"Left1","right":"Right1"}]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our object is correctly converted to a JSON map containing a left and right field. Regarding the raw object, we have now a string containing &lt;code&gt;"Instance of 'Object'"&lt;/code&gt;, that's the direct result of the &lt;code&gt;toString()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Last trick, it is also possible to encode a JSON using indentation via the &lt;a href="https://api.dart.dev/dart-convert/JsonEncoder/JsonEncoder.withIndent.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonEncoder.withIndent()&lt;/code&gt;&lt;/a&gt; constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JsonEncoder&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withIndent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let execute our code, the output should be a JSON with indentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart bin/encode_indent.dart
&lt;span class="go"&gt;{
 "test": [
  1,
  2,
  3
 ]
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's right! It works! Next step, decoding JSON.&lt;/p&gt;

&lt;h1&gt;
  
  
  Decoding
&lt;/h1&gt;

&lt;p&gt;Decoding a JSON String is similar than encoding Dart objects, one can use &lt;a href="https://api.dart.dev/dart-convert/JsonCodec-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonCodec&lt;/code&gt;&lt;/a&gt; or create a &lt;a href="https://api.dart.dev/dart-convert/JsonDecoder-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonDecoder&lt;/code&gt;&lt;/a&gt; object, the result will be the same.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s"&gt;'{"test": 1}'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;JsonDecoder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s"&gt;'{"test": 1}'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let execute this code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run bin/decode.dart
&lt;span class="go"&gt;{test: 1}
{test: 1}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without surprise, it returns a &lt;a href="https://api.dart.dev/dart-core/Map-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Map()&lt;/code&gt;&lt;/a&gt;, but, what if we want to return a specific Object instead? Let take our &lt;code&gt;Tuple&lt;/code&gt; class back from the Encoding section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tuple&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"_object"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Tuple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"left"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"right"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://api.dart.dev/dart-convert/JsonDecoder-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;JsonDecoder&lt;/code&gt;&lt;/a&gt; class can accept a &lt;a href="https://api.dart.dev/dart-convert/JsonDecoder/JsonDecoder.html" rel="noopener noreferrer"&gt;&lt;code&gt;reviver()&lt;/code&gt;&lt;/a&gt; function passed as parameter and can be used for this kind of situation. When some data looks like it's a specific object, the &lt;code&gt;reviver()&lt;/code&gt; function will be able to return the correct instantiated object. Here an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="kt"&gt;Function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;reviver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--&amp;gt; key: &lt;/span&gt;&lt;span class="si"&gt;$key&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--&amp;gt; value: &lt;/span&gt;&lt;span class="si"&gt;$value&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"_object"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"tuple"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; 
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"right"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The snippet above will check each value returned by the decoder, and if one of these values are matching a specific kind of pattern (in our case if the value is a Map containing a field &lt;code&gt;object&lt;/code&gt; containing the string &lt;code&gt;tuple&lt;/code&gt;), then it will create the object. Let check that with a new main entry-point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;toEncode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; 
      &lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;decoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reviver&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;toEncode&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"==&amp;gt; final: &lt;/span&gt;&lt;span class="si"&gt;$x&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can execute the code and check the output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart bin/decode_reviver.dart
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: data
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: 2
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: _object
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: tuple
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: left
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: left
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: right
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: right
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: tuple
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: &lt;span class="o"&gt;{&lt;/span&gt;_object: tuple, left: left, right: right&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: &lt;span class="o"&gt;{&lt;/span&gt;data: 2, tuple: Instance of &lt;span class="s1"&gt;'Tuple'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: 0
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;data: 2, tuple: Instance of &lt;span class="s1"&gt;'Tuple'&lt;/span&gt;&lt;span class="o"&gt;}}&lt;/span&gt;
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: 1
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: &lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;data: 2, tuple: Instance of &lt;span class="s1"&gt;'Tuple'&lt;/span&gt;&lt;span class="o"&gt;}}]&lt;/span&gt;
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;key: null
&lt;span class="gp"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;value: &lt;span class="o"&gt;{&lt;/span&gt;1: &lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;data: 2, tuple: Instance of &lt;span class="s1"&gt;'Tuple'&lt;/span&gt;&lt;span class="o"&gt;}}]}&lt;/span&gt;
&lt;span class="gp"&gt;==&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;final: &lt;span class="o"&gt;{&lt;/span&gt;1: &lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;data: 2, tuple: Instance of &lt;span class="s1"&gt;'Tuple'&lt;/span&gt;&lt;span class="o"&gt;}}]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the returned value by the decoder includes an instance of a &lt;code&gt;Tuple&lt;/code&gt; class. The code is still a bit messy, not enough check, but the idea is there.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This post was a small one, JSON is a simple format, and the interfaces used to encode/decode it are relatively easy to use, at least, for simple use cases. JSON can also be specified using schema or used as serializer, but it will be seen in another publication. As usual, for the ones would like to dive deeper in the subject, here few interesting links for you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dart.dev/learn/tutorial/data-and-json" rel="noopener noreferrer"&gt;Official JSON Dart Tutorial&lt;/a&gt;, where you can see more complex examples;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://api.dart.dev/dart-convert/JsonCodec-class.html" rel="noopener noreferrer"&gt;JsonCodec class&lt;/a&gt;, where you will find the complete documentation API for the JsonCodec class;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://api.dart.dev/dart-convert/JsonDecoder-class.html" rel="noopener noreferrer"&gt;JsonDecoder class&lt;/a&gt;, where you will learn how to use the JsonDecoder class;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://api.dart.dev/dart-convert/JsonEncoder-class.html" rel="noopener noreferrer"&gt;JsonEncoder class&lt;/a&gt;, where the full description of the JsonEncoder class can be found;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/dart-lang/sdk/blob/fc3da898ea1664b8a8633f6ec03d2c2290bb3d01/sdk/lib/convert/json.dart" rel="noopener noreferrer"&gt;&lt;code&gt;dart:convert&lt;/code&gt; JSON source code&lt;/a&gt;, where you will find the full documentation of the &lt;code&gt;convert&lt;/code&gt; package, containing also the classes for Base64 or UTF8/Unicode;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/avwerosuoghene/passing-data-from-dart-to-json-for-backend-integration-5bn2a"&gt;Passing Data from Dart to JSON for Backend Integration&lt;/a&gt; by &lt;a class="mentioned-user" href="https://dev.to/avwerosuoghene"&gt;@avwerosuoghene&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@codewithif/mastering-json-serialization-in-dart-flutter-from-basics-to-complex-models-4c764400c2d6" rel="noopener noreferrer"&gt;Mastering JSON Models in Dart &amp;amp; Flutter: From Basics to Complex Models&lt;/a&gt; by &lt;a href="https://medium.com/@codewithif" rel="noopener noreferrer"&gt;Iftekhar Ahmed&lt;/a&gt; on Medium&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@vikranthsalian/how-to-parse-json-in-dart-flutter-60c17143198e" rel="noopener noreferrer"&gt;How to Parse JSON in Dart/Flutter&lt;/a&gt; by &lt;a href="https://medium.com/@vikranthsalian" rel="noopener noreferrer"&gt;Vikranh Salian&lt;/a&gt; on Medium&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@bhavesh.sachala/mastering-json-encoding-and-decoding-in-flutter-dart-a-comprehensive-guide-for-api-integration-b5153aa6f1bc" rel="noopener noreferrer"&gt;Mastering JSON Encoding and Decoding in Flutter/Dart: A Comprehensive Guide for API Integration&lt;/a&gt; by &lt;a href="https://medium.com/@bhavesh.sachala" rel="noopener noreferrer"&gt;Bhaveshh Sachala&lt;/a&gt; on Medium &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@dieter_muenchen?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Dieter K&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-black-and-white-photo-of-a-row-of-sinks-M4G5BSUo1p4?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>json</category>
      <category>dart</category>
      <category>serialization</category>
      <category>serializer</category>
    </item>
    <item>
      <title>Standalone HTTP Server with Relic in Dart</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Wed, 10 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/standalone-http-server-with-relic-in-dart-5gf8</link>
      <guid>https://dev.to/niamtokik/standalone-http-server-with-relic-in-dart-5gf8</guid>
      <description>&lt;p&gt;Instead of creating another classic web server displaying some static value, we will create today a local cache management over HTTP using Dart and Relic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart create &lt;span class="nt"&gt;-t&lt;/span&gt; console-full cache_relic
&lt;span class="go"&gt;Creating cache_relic using template console-full...

  .gitignore
  analysis_options.yaml
  CHANGELOG.md
  pubspec.yaml
  README.md
  bin/cache_relic.dart
  lib/cache_relic.dart
  test/cache_relic_test.dart

Running pub get...                     0.5s
  Resolving dependencies...
  Downloading packages...
  Changed 48 dependencies!

Created project cache_relic in cache_relic! In order to get started, run the following commands:

  cd cache_relic
  dart run

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;cache_relic
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart pub add relic
&lt;span class="go"&gt;Resolving dependencies... 
Downloading packages... 
+ relic 1.2.0
+ relic_core 1.2.0
+ relic_io 1.2.0
Changed 3 dependencies!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Cache Server
&lt;/h1&gt;

&lt;p&gt;How to store in-memory data in Dart and how to do it correctly? What kind of solution do we have to "share" a reference to an object containing data? Let review the solution I would have used on &lt;a href="https://www.erlang.org/" rel="noopener noreferrer"&gt;Erlang&lt;/a&gt;/&lt;a href="https://elixir-lang.org/" rel="noopener noreferrer"&gt;Elixir&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Creating a &lt;a href="https://www.erlang.org/doc/apps/stdlib/gen_server.html" rel="noopener noreferrer"&gt;&lt;code&gt;gen_server&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://www.erlang.org/doc/apps/stdlib/gen_statem.html" rel="noopener noreferrer"&gt;&lt;code&gt;gen_statem&lt;/code&gt;&lt;/a&gt; process using a &lt;a href="https://www.erlang.org/doc/apps/stdlib/maps.html" rel="noopener noreferrer"&gt;&lt;code&gt;map&lt;/code&gt;&lt;/a&gt; as main state with at least 3 interfaces, one to push a value, one to get a value and then one to delete a value. One session would be directly associated with a process, under a &lt;a href="https://www.erlang.org/doc/apps/stdlib/supervisor.html" rel="noopener noreferrer"&gt;&lt;code&gt;supervisor&lt;/code&gt;&lt;/a&gt; using a &lt;code&gt;one_for_one&lt;/code&gt; or &lt;code&gt;simple_one_for_one&lt;/code&gt; strategy;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Creating a public or protected &lt;a href="https://www.erlang.org/doc/apps/stdlib/ets.html" rel="noopener noreferrer"&gt;ETS table&lt;/a&gt; managed by a process following the same principle than the previous point (in short, replacing the &lt;a href="https://www.erlang.org/doc/apps/stdlib/maps.html" rel="noopener noreferrer"&gt;&lt;code&gt;map&lt;/code&gt;&lt;/a&gt; with the ETS). Instead of using Erlang messages via &lt;a href="https://www.erlang.org/doc/apps/stdlib/gen_server.html" rel="noopener noreferrer"&gt;&lt;code&gt;gen_server&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://www.erlang.org/doc/apps/stdlib/gen_statem.html" rel="noopener noreferrer"&gt;&lt;code&gt;gen_statem&lt;/code&gt;&lt;/a&gt;, here we could use the &lt;a href="https://www.erlang.org/doc/apps/stdlib/ets.html" rel="noopener noreferrer"&gt;&lt;code&gt;ets&lt;/code&gt;&lt;/a&gt; interface instead (faster).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dart is a more complex language, you have in this situation many choices available, and each one should probably tested and profiled before being used. Let try to list them one by one.&lt;/p&gt;

&lt;p&gt;Why a cache server? Well, to be, a cache system is the smallest piece of software one can found everywhere. There is a reason why &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;&lt;code&gt;redis&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://www.memcached.org/" rel="noopener noreferrer"&gt;&lt;code&gt;memcached&lt;/code&gt;&lt;/a&gt; or many other projects like that are used by everybody: developers need a way to store data quick. It could be for a session, for temporary data or simply to avoid annoying the main core database. A cache service is easy to create (key/value store), and can become rapidly something cool to hack. In fact, if you look a bit closely, any web server or database can be seen as a kind of "infinite" cache server, right?&lt;/p&gt;

&lt;p&gt;Oh, by the way, all &lt;a href="https://connect.ed-diamond.com/auteur/kerjouan-mathieu" rel="noopener noreferrer"&gt;articles published in the French Linux Magazine&lt;/a&gt; regarding Erlang were created around a cache system service. Feel free to check them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Global Variables
&lt;/h2&gt;

&lt;p&gt;A global variable containing an Object like a &lt;a href="https://api.dart.dev/dart-core/Map-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Map()&lt;/code&gt;&lt;/a&gt; containing &lt;code&gt;Session()&lt;/code&gt; objects. Using global variables are not a good idea, even in Dart, but it's a simple answer and it should work. The big risk here would be the race conditions and other weird behaviors in case of concurrent calls. Here some list to see what we can do with this method. Here a list of few discussion from the web when it comes to use a global variable in Dart: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@navinprince.thegr8gossiper/how-to-store-global-variables-in-flutter-the-best-approaches-explained-cde516badf84" rel="noopener noreferrer"&gt;How to Store Global Variables in Flutter: The Best Approaches Explained&lt;/a&gt; at Medium;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.reddit.com/r/FlutterDev/comments/1c1gqrx/do_you_use_global_variables_in_your_app_design/" rel="noopener noreferrer"&gt;Do you use global variables in your app design?&lt;/a&gt; from a reddit thread.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, I would like to avoid using a global variable (for example in module entry-point), I think it could lead to more issues. It could be a good idea to do a quick test though...&lt;/p&gt;

&lt;h2&gt;
  
  
  Singleton pattern
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;In object-oriented programming, the singleton pattern is a software design pattern that restricts the instantiation of a class to a singular instance [...] Singletons are often preferred to global variables because they do not pollute the global namespace (or their containing namespace).&lt;/p&gt;

&lt;p&gt;-- &lt;a href="https://en.wikipedia.org/wiki/Singleton_pattern" rel="noopener noreferrer"&gt;Singleton pattern&lt;/a&gt; on Wikipedia&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Object Oriented Programming Design Patterns, a complex topic where programming patterns are standardized and used as common bricks to create something more complex. It was always a pain to work with that to be...&lt;/p&gt;

&lt;p&gt;Anyway, let talk a bit about the &lt;a href="https://en.wikipedia.org/wiki/Singleton_pattern" rel="noopener noreferrer"&gt;singleton pattern&lt;/a&gt;. When an object is created and instantiated from a specific class , a part of the memory is allocated for it. If you are recreating another object with the same class, another part of the memory will be allocated, both objects living in their own space. A singleton is a pattern that will eventually (on demand for example) return an object already instantiated instead of created a new one. To do that in Dart, we also need to talk about the &lt;a href="https://en.wikipedia.org/wiki/Factory_method_pattern" rel="noopener noreferrer"&gt;factory method pattern&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In object-oriented programming, the factory method pattern is a design pattern that uses factory methods to deal with the problem of creating objects without having to specify their exact classes. Rather than by calling a constructor, this is accomplished by invoking a factory method to create an object.&lt;/p&gt;

&lt;p&gt;-- &lt;a href="https://en.wikipedia.org/wiki/Factory_method_pattern" rel="noopener noreferrer"&gt;Factory method pattern&lt;/a&gt; on Wikipedia&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The idea here is to allow a method or a function to create different objects from different classes. Those object can be stored in another object in Map for example or any other data-structure. This kind of behavior can be achieved by using the &lt;code&gt;factory&lt;/code&gt; keyword.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A factory is a constructor prefaced by the built-in identifier (17.38) factory.&lt;/p&gt;

&lt;p&gt;-- &lt;a href="https://storage.googleapis.com/dart-specification/DartLangSpecDraft.pdf" rel="noopener noreferrer"&gt;Dart Language Specification&lt;/a&gt;, Chapter 10, Section 7.2, page 51&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thanks for the information. It does not give us more information about how &lt;code&gt;factory&lt;/code&gt;&amp;nbsp;is being used though.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Instance creation expressions generally produce instances and invoke constructors to initialize them. The exception is that a factory constructor invocation works like a regular function call. It may of course evaluate an instance creation expression and thus produce a fresh instance, but no fresh instances are created as a direct consequence of the factory constructor invocation.&lt;/p&gt;

&lt;p&gt;-- &lt;a href="https://storage.googleapis.com/dart-specification/DartLangSpecDraft.pdf" rel="noopener noreferrer"&gt;Dart Language Specification&lt;/a&gt;, Chapter 17, Section 13, page 135&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a bit better. Indeed, the &lt;code&gt;factory&lt;/code&gt; function looks like a regular function, and produce fresh instances or create new ones.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A factory constructor can be declared in an abstract class and used safely, as it will either produce a valid instance or throw&lt;/p&gt;

&lt;p&gt;-- &lt;a href="https://storage.googleapis.com/dart-specification/DartLangSpecDraft.pdf" rel="noopener noreferrer"&gt;Dart Language Specification&lt;/a&gt;, Chapter 17, Section 13.2, page 137&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's part of the specification is also interesting, it means a factory can be part of an abstract class, then, the factory can be used from it to produce objects from a subclass.&lt;/p&gt;

&lt;p&gt;The official documentation is listing few examples about &lt;a href="https://dart.dev/language/constructors#factory-constructors" rel="noopener noreferrer"&gt;factory constructors&lt;/a&gt;, and they give an idea when it could be used.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In object-oriented programming, the factory method pattern is a design pattern that uses factory methods to deal with the problem of creating objects without having to specify their exact classes. Rather than by calling a constructor, this is accomplished by invoking a factory method to create an object. &lt;/p&gt;

&lt;p&gt;-- &lt;a href="https://en.wikipedia.org/wiki/Factory_method_pattern" rel="noopener noreferrer"&gt;Factory method pattern&lt;/a&gt; on Wikipedia&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Perhaps having other point of view could help to understand how this thing is working. Here a list of resources from the web:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dart.dev/language/constructors#factory-constructors" rel="noopener noreferrer"&gt;Factory constructors&lt;/a&gt; from the official Dart documentation;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/devdammak/mastering-the-singleton-pattern-in-dart-4gin"&gt;Mastering the Singleton Pattern in Dart&lt;/a&gt; by &lt;a class="mentioned-user" href="https://dev.to/devdammak"&gt;@devdammak&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@swe.jamirulinfo/singleton-is-a-design-pattern-in-dart-98dd947c6dd1" rel="noopener noreferrer"&gt;Singleton is a Design Pattern in Dart&lt;/a&gt; by &lt;a href="https://medium.com/@swe.jamirulinfo" rel="noopener noreferrer"&gt;Jamirul islam&lt;/a&gt; on Medium&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@hpatilabhi10/mastering-the-singleton-design-pattern-in-dart-40adb7dd87ec" rel="noopener noreferrer"&gt;https://medium.com/@hpatilabhi10/mastering-the-singleton-design-pattern-in-dart-40adb7dd87ec&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/blackcoda/dart-singleton-demystified-pdl"&gt;Singleton Pattern in Dart&lt;/a&gt; by &lt;a class="mentioned-user" href="https://dev.to/blackcoda"&gt;@blackcoda&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@shemsedinrobsen/understanding-factory-constructors-in-dart-and-flutter-a-comparison-with-static-methods-d03b471a7944" rel="noopener noreferrer"&gt;Understanding Factory Constructors in Dart and Flutter: A Comparison with Static Methods&lt;/a&gt; by &lt;a href="https://medium.com/@shemsedinrobsen" rel="noopener noreferrer"&gt;Shemsedin&lt;/a&gt; on Medium&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/12649573/how-do-you-build-a-singleton-in-dart" rel="noopener noreferrer"&gt;How do you build a Singleton in Dart?&lt;/a&gt; on StackOverflow&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using the &lt;code&gt;factory&lt;/code&gt; keyword here, is it really for creating a singleton? Let create a class called &lt;code&gt;Singleton&lt;/code&gt; to understand this concept.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Singleton&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"singleton"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;factory&lt;/span&gt; &lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;decrement&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_counter&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_counter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_counter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"counter (&lt;/span&gt;&lt;span class="si"&gt;${name}&lt;/span&gt;&lt;span class="s"&gt;): &lt;/span&gt;&lt;span class="si"&gt;${counter}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;_name&lt;/code&gt; and &lt;code&gt;_counter&lt;/code&gt; attributes are private. They will be used to identify the singleton, to see if the instance is really the same. We can also add a random value instead of the fixed one for the name, but it would be too overkill for a simple test.&lt;/p&gt;

&lt;p&gt;The constructor &lt;code&gt;Singleton()&lt;/code&gt; simply returns an instantiated &lt;code&gt;Singleton&lt;/code&gt; object and do no more. Then the &lt;code&gt;create()&lt;/code&gt; method is marked as &lt;code&gt;factory&lt;/code&gt;, so, in theory, it should also return the same instance of a &lt;code&gt;Singleton&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;Then, &lt;code&gt;increment()&lt;/code&gt;, &lt;code&gt;decrement()&lt;/code&gt; and &lt;code&gt;reset()&lt;/code&gt; methods are simply there to play with the &lt;code&gt;_counter&lt;/code&gt; private attribute to see if when we change one instance, the other variables/functions pointing out this instance will change as well.&lt;/p&gt;

&lt;p&gt;Finally, 2 getters are created to easily retrieve the values of the private attributes. The last method called &lt;code&gt;toString()&lt;/code&gt; is a simple boilerplate to convert the &lt;code&gt;Singleton&lt;/code&gt; as &lt;code&gt;String&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Great, now, let creates an instance of a &lt;code&gt;Singleton&lt;/code&gt; inside a synchronous functions called &lt;code&gt;f()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Singleton&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"f() name: &lt;/span&gt;&lt;span class="si"&gt;${v.name}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"f() counter: &lt;/span&gt;&lt;span class="si"&gt;${v.counter}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let do the same but for an asynchronous function call this time, using a &lt;code&gt;Future&lt;/code&gt; and being delayed of 1 second. This function will be called &lt;code&gt;a()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Future&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delayed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;seconds:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;Singleton&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a() name: &lt;/span&gt;&lt;span class="si"&gt;${v.name}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a() counter: &lt;/span&gt;&lt;span class="si"&gt;${v.counter}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decrement&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An helper function to inspect the content of the &lt;code&gt;Singleton&lt;/code&gt; object can also be good. It could be created directly as a method inside it, or, like in this case, as a function called &lt;code&gt;inspect()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Singleton&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"counter (&lt;/span&gt;&lt;span class="si"&gt;${label}&lt;/span&gt;&lt;span class="s"&gt;): &lt;/span&gt;&lt;span class="si"&gt;${s.counter}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name (&lt;/span&gt;&lt;span class="si"&gt;${label}&lt;/span&gt;&lt;span class="s"&gt;): &lt;/span&gt;&lt;span class="si"&gt;${s.name}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our code looks good, but the entry-point is still missing, let create our &lt;code&gt;main()&lt;/code&gt; function. This one will contain most of the interesting stuff, and will try to instantiate many &lt;code&gt;Singleton()&lt;/code&gt; objects to see if the values similar or different.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// create a first instance&lt;/span&gt;
  &lt;span class="n"&gt;Singleton&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"v1"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// try to create a second instance&lt;/span&gt;
  &lt;span class="c1"&gt;// and increment the counter&lt;/span&gt;
  &lt;span class="n"&gt;Singleton&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"v2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// check both instance (should return the same&lt;/span&gt;
  &lt;span class="c1"&gt;// values)&lt;/span&gt;
  &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"v1"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"v2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// reset the singleton&lt;/span&gt;
  &lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"v1"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"v2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// asynchronous function call&lt;/span&gt;
  &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// create a third instance&lt;/span&gt;
  &lt;span class="n"&gt;Singleton&lt;/span&gt; &lt;span class="n"&gt;v3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Singleton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"v3"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"v2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"v1"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// synchronous function call&lt;/span&gt;
  &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code can be invoked, and here the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run bin/singleton.dart
&lt;span class="go"&gt;counter (v1): 1
name (v1): singleton
counter (v2): 1
name (v2): singleton
counter (v1): 2
name (v1): singleton
counter (v2): 2
name (v2): singleton
counter (v1): 0
name (v1): singleton
counter (v2): 0
name (v2): singleton
counter (v3): 0
name (v3): singleton
counter (v2): 0
name (v2): singleton
counter (v1): 0
name (v1): singleton
f() name: singleton
f() counter: 0
a() name: singleton
a() counter: 1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yeah, it seems the singleton pattern is working there. Even when we try to instantiate a new &lt;code&gt;Singleton()&lt;/code&gt; object during the execution of the program, the values returned seems similar. I assume then the returned object is always the same. Anyway, I still don't understand everything, it's probably due to the syntax:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;when using the constructor, the same instance is returned, does it means when any kind of method is marked with a &lt;code&gt;factory&lt;/code&gt; keyword, it will only act as a factory?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;what about race condition and asynchronous calls? Is it really safe to have a singleton in this kind of context? It looks like a global variable to me, so, I guess the same issues will be present.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Multiton Pattern
&lt;/h2&gt;

&lt;p&gt;Another concept pattern exists, called  &lt;a href="https://en.wikipedia.org/wiki/Multiton_pattern" rel="noopener noreferrer"&gt;multiton pattern&lt;/a&gt;, permits to manage more than one object. In fact, it seems this is the example made by the &lt;a href="https://dart.dev/language/constructors#factory-constructors" rel="noopener noreferrer"&gt;Dart documentation&lt;/a&gt; when talking about factories, at least, if the wikipedia can be trusted:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In software engineering, the multiton pattern is a design pattern which generalizes the singleton pattern. Whereas the singleton allows only one instance of a class to be created, the multiton pattern allows for the controlled creation of multiple instances, which it manages through the use of a map.&lt;/p&gt;

&lt;p&gt;-- &lt;a href="https://en.wikipedia.org/wiki/Multiton_pattern" rel="noopener noreferrer"&gt;Multiton pattern&lt;/a&gt; on Wikipedia&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, what's the idea there? Well, instead of creating one instance of one object, and reusing every time, the multiton pattern deals with many objects stored somewhere, usually a &lt;a href="https://api.dart.dev/dart-core/Map-class.html" rel="noopener noreferrer"&gt;Map&lt;/a&gt; like in the example. Let recreate it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Multiton&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_id&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"multiton"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Multiton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;Multiton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;{};&lt;/span&gt;

  &lt;span class="kd"&gt;factory&lt;/span&gt; &lt;span class="n"&gt;Multiton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putIfAbsent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Multiton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Multiton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_counter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Multiton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"==&amp;gt; remove &lt;/span&gt;&lt;span class="si"&gt;${id}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"label: &lt;/span&gt;&lt;span class="si"&gt;${label}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"multiton_length: &lt;/span&gt;&lt;span class="si"&gt;${length()}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"object_hashCode: &lt;/span&gt;&lt;span class="si"&gt;${hashCode}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  object_id: &lt;/span&gt;&lt;span class="si"&gt;${_id}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  object_counter: &lt;/span&gt;&lt;span class="si"&gt;${_counter}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  object_name: &lt;/span&gt;&lt;span class="si"&gt;${_name}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay, it looks like a bit more complex than the singleton. The most complex part is about the &lt;code&gt;_cache&lt;/code&gt; attribute, it will store the &lt;code&gt;Multiton&lt;/code&gt; instance using a key as &lt;code&gt;String&lt;/code&gt; when a new &lt;code&gt;Multiton&lt;/code&gt; is created. &lt;/p&gt;

&lt;p&gt;the &lt;code&gt;inspect()&lt;/code&gt; method has been added directly inside the class to help to see what's happening. Note: only a &lt;code&gt;Multiton&lt;/code&gt; instance can execute it, it's not possible to call it via the class itself like &lt;code&gt;Multiton.inspect()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let create then &lt;code&gt;main()&lt;/code&gt; entry-point now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// create a first multiton instance&lt;/span&gt;
  &lt;span class="c1"&gt;// with the id "test" and increment&lt;/span&gt;
  &lt;span class="c1"&gt;// its counter&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;m1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Multiton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;m1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"m1"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;m1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// create a second multiton instance&lt;/span&gt;
  &lt;span class="c1"&gt;// using the same id "test", this should&lt;/span&gt;
  &lt;span class="c1"&gt;// be the same instance than m1.&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;m2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Multiton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;m2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"m2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// create a third multiton instance&lt;/span&gt;
  &lt;span class="c1"&gt;// with "test2" as id. It should be&lt;/span&gt;
  &lt;span class="c1"&gt;// a new instance.&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;m3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Multiton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;m3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"m3"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;m3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;m3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;m3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;m1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"m1"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;m2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"m2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;m3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"m3"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// let remove the multiton using&lt;/span&gt;
  &lt;span class="c1"&gt;// "test" as id and recreate a new&lt;/span&gt;
  &lt;span class="c1"&gt;// one. the counter should be reset&lt;/span&gt;
  &lt;span class="c1"&gt;// to 0&lt;/span&gt;
  &lt;span class="n"&gt;Multiton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;m4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Multiton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;m4&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"m4"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// let remove it again&lt;/span&gt;
  &lt;span class="c1"&gt;// but because of garbage collector,&lt;/span&gt;
  &lt;span class="c1"&gt;// the value is still there.&lt;/span&gt;
  &lt;span class="n"&gt;Multiton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;m4&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"m4"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// let remove the last id "test2"&lt;/span&gt;
  &lt;span class="c1"&gt;// and print the length of the multiton&lt;/span&gt;
  &lt;span class="c1"&gt;// it should be equal to 0&lt;/span&gt;
  &lt;span class="n"&gt;Multiton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Multiton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To help me (and the readers), some comments have been added. Dealing with multiple instance of objects can be challenging, and I did not even add the asynchronous part. Anyway, the idea there is to instantiate &lt;code&gt;Multiton()&lt;/code&gt; objects in different manners. We can also play a bit with the garbage collector.&lt;/p&gt;

&lt;p&gt;Indeed, the 2 last &lt;code&gt;inspect()&lt;/code&gt; call are interesting. When we remove the key &lt;code&gt;"test"&lt;/code&gt; from the &lt;code&gt;_cache&lt;/code&gt;, we still have one instance of this object alive, because we are reusing the &lt;code&gt;m4&lt;/code&gt; variable. When we remove all keys, the &lt;code&gt;_cache&lt;/code&gt; length drop to &lt;code&gt;0&lt;/code&gt; and in theory, because no more variables are being used, all instances have been removed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;--
label: m1
multiton_length: 1
object_hashCode: 779530408
  object_id: test
  object_counter: 0
  object_name: multiton
--
label: m2
multiton_length: 1
object_hashCode: 779530408
  object_id: test
  object_counter: 1
  object_name: multiton
--
label: m3
multiton_length: 2
object_hashCode: 938446565
  object_id: test2
  object_counter: 0
  object_name: multiton
--
label: m1
multiton_length: 2
object_hashCode: 779530408
  object_id: test
  object_counter: 1
  object_name: multiton
--
label: m2
multiton_length: 2
object_hashCode: 779530408
  object_id: test
  object_counter: 1
  object_name: multiton
--
label: m3
multiton_length: 2
object_hashCode: 938446565
  object_id: test2
  object_counter: 3
  object_name: multiton
&lt;/span&gt;&lt;span class="gp"&gt;==&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;remove &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="go"&gt;--
label: m4
multiton_length: 2
object_hashCode: 702292414
  object_id: test
  object_counter: 0
  object_name: multiton
&lt;/span&gt;&lt;span class="gp"&gt;==&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;remove &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="go"&gt;--
label: m4
multiton_length: 1
object_hashCode: 702292414
  object_id: test
  object_counter: 0
  object_name: multiton
&lt;/span&gt;&lt;span class="gp"&gt;==&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;remove test2
&lt;span class="go"&gt;0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick summary, I find the interface and all the syntactic sugar highly confusing here. It's really like black magic under steroids, the code is working, but you don't really know why and you are a bit afraid of the answer.&lt;/p&gt;

&lt;p&gt;Unfortunately, the cache service will use the multiton patterns to store each sessions. When an user will store some value, the session requested will also have access to the cache store and offers getters/setters to modify it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observer pattern (Observables)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;In software design and software engineering, the observer pattern is a software design pattern in which an object, called the subject (also known as event source or event stream), maintains a list of its dependents, called observers (also known as event sinks), and automatically notifies them of any state changes, typically by calling one of their methods.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Observer_pattern" rel="noopener noreferrer"&gt;Observer pattern&lt;/a&gt; on Wikipedia&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While I was looking for an answer, some people pointed another solution called the observer pattern. Unfortunately, the package called &lt;a href="https://pub.dev/documentation/observable/latest/observable/" rel="noopener noreferrer"&gt;&lt;code&gt;observable&lt;/code&gt;&lt;/a&gt; I wanted to use is not maintained anymore. An alternative called &lt;a href="https://pub.dev/packages/simple_observable" rel="noopener noreferrer"&gt;&lt;code&gt;simple_observable&lt;/code&gt;&lt;/a&gt; though. It will be the subject for another publication. Here some interesting link from the web about observer pattern in Dart:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/scottt2/design-patterns-in-dart/blob/master/observer/README.md" rel="noopener noreferrer"&gt;Observer Pattern&lt;/a&gt; by &lt;a href="https://github.com/scottt2" rel="noopener noreferrer"&gt;Tyler Scott&lt;/a&gt; on Github&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://blog.devblocks.tech/design-pattern-trong-dart-observer-pattern/" rel="noopener noreferrer"&gt;Design Pattern in Dart: Observer Pattern&lt;/a&gt; by &lt;a href="https://blog.devblocks.tech/user/nguyennhutthong.devgmail-com/" rel="noopener noreferrer"&gt;Lotus Blog&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/20186198/implement-an-observer-pattern-in-dart" rel="noopener noreferrer"&gt;Implement an Observer pattern in Dart&lt;/a&gt; on StackOverflow&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@gauravswarankar/understanding-flutter-observables-cb0277f85174" rel="noopener noreferrer"&gt;Understanding Flutter Observables&lt;/a&gt; by &lt;a href="https://medium.com/@gauravswarankar" rel="noopener noreferrer"&gt;Gaurav Swarankar&lt;/a&gt; on Medium&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.christianfindlay.com/blog/flutter-rx-signals-riverpod-rxdart" rel="noopener noreferrer"&gt;Reactive Programming in Flutter: Understanding the Power of Observables and Computed Values with Signals, Riverpod and RxDart&lt;/a&gt; by &lt;a href="https://www.christianfindlay.com/" rel="noopener noreferrer"&gt;Christan Findlay&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@yadunandan.xyz/how-to-write-your-own-observables-in-dart-15a2c9d36be5" rel="noopener noreferrer"&gt;How to write your own observables in dart.&lt;/a&gt; by &lt;a href="https://medium.com/@yadunandan.xyz" rel="noopener noreferrer"&gt;yadunandan&lt;/a&gt; on Medium&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@mo7amedaliebaid/why-the-observer-pattern-is-essential-for-flutter-developers-214ce2f5e0d5" rel="noopener noreferrer"&gt;Why the Observer Pattern is Essential for Flutter Developers?&lt;/a&gt; by &lt;a href="https://medium.com/@mo7amedaliebaid" rel="noopener noreferrer"&gt;Moamedaliebaid&lt;/a&gt; on Medium&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Common Functions
&lt;/h1&gt;

&lt;p&gt;The cache service will use Base64Url specified in &lt;a href="https://www.rfc-editor.org/info/rfc4648/#section-5" rel="noopener noreferrer"&gt;RFC4648&lt;/a&gt;. What's the difference with Base64? Well, this format is URL-friendly, and replace the &lt;code&gt;+&lt;/code&gt; plus) and the &lt;code&gt;/&lt;/code&gt; (slash) respectively by &lt;code&gt;-&lt;/code&gt; (dash) and &lt;code&gt;_&lt;/code&gt; (underscore). It means base64url can easily be passed in a path. Let create the file &lt;code&gt;lib/base64url.dart&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bloody Eyes Alert&lt;/strong&gt;: the implementation used here is disgustingly slow and crappy on purpose, I wanted to test the &lt;a href="https://api.dart.dev/dart-core/RegExp-class.html" rel="noopener noreferrer"&gt;RegExp&lt;/a&gt; interface in Dart...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;toBase64Url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replaceAllMapped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sx"&gt;r'\+'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Match&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;'-'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replaceAllMapped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sx"&gt;r'\/'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Match&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;'_'&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;toBase64Url()&lt;/code&gt; function simple replace &lt;code&gt;+&lt;/code&gt; and &lt;code&gt;/&lt;/code&gt; by &lt;code&gt;-&lt;/code&gt; and &lt;code&gt;_&lt;/code&gt; with the help of &lt;a href="https://api.dart.dev/stable/3.11.5/dart-core/String/replaceAllMapped.html" rel="noopener noreferrer"&gt;&lt;code&gt;String.replaceAllMapped()&lt;/code&gt;&lt;/a&gt; method. A new &lt;a href="https://api.dart.dev/dart-core/RegExp-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;RegExp()&lt;/code&gt;&lt;/a&gt; is used there to replace all characters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;toBase64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replaceAllMapped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sx"&gt;r'\-'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Match&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;'+'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replaceAllMapped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sx"&gt;r'\_'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Match&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;'/'&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;toBase64()&lt;/code&gt;&amp;nbsp;function is doing the opposite of the previous function (and uses the same structure).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;isBase64Url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;b64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toBase64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b64&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, &lt;code&gt;isBase64Url()&lt;/code&gt; checks if a Base64 string is valid or not. Again, dirty code where the Base64Url is converted to &lt;a href="https://api.dart.dev/dart-convert/Base64Codec-class.html" rel="noopener noreferrer"&gt;Base64&lt;/a&gt; and then &lt;a href="https://api.dart.dev/dart-convert/Base64Decoder-class.html" rel="noopener noreferrer"&gt;decoded&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Is there a better method? Yes. In fact, doing my own implementation of Base64Url could have been done, it would have been faster, but would also mean more work. The goal is not to have production ready tool, but to learn the most from Dart.&lt;/p&gt;

&lt;h1&gt;
  
  
  Store
&lt;/h1&gt;

&lt;p&gt;The &lt;code&gt;Store&lt;/code&gt; class will be one of the most important, it will contain the key/value defined by the clients. The definition should be flexible to accept any kind of data. The checks will be done before inserting data (in the handlers or middlewares).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CacheStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;{};&lt;/span&gt;

  &lt;span class="n"&gt;CacheStore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;containsKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_store&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_store&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This class definition is simple, a &lt;code&gt;_store&lt;/code&gt; attribute will contain the keys/values, and the &lt;code&gt;get()&lt;/code&gt;, &lt;code&gt;delete()&lt;/code&gt;, &lt;code&gt;put()&lt;/code&gt; and &lt;code&gt;exists()&lt;/code&gt; methods the interfaces to interact with it. The test suite can be seen below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;store_test&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'store'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'create a new store'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CacheStore&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;isA&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CacheStore&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;());&lt;/span&gt;  
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'insert and deleting keys'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;CacheStore&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CacheStore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

      &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

      &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let execute them to be sure it works correclty.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="go"&gt;00:00 +2: All tests passed!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks good to me. Go to the next stack.&lt;/p&gt;

&lt;h1&gt;
  
  
  Session
&lt;/h1&gt;

&lt;p&gt;The &lt;code&gt;Session&lt;/code&gt; class is our multiton, it will be in charge to manage the session by their id. Each session got one &lt;code&gt;Store()&lt;/code&gt; object. This module is from &lt;code&gt;lib/session.dart&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:core'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:math'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'cache_store.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'base64url.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CacheSession&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;CacheStore&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CacheStore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_sessions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;{};&lt;/span&gt;

  &lt;span class="kd"&gt;factory&lt;/span&gt; &lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_sessions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putIfAbsent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_sessions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;containsKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_sessions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most important part of the code is from the constructor &lt;code&gt;CacheSession&lt;/code&gt; marked with the &lt;code&gt;factory&lt;/code&gt; keyword. Every time a new &lt;code&gt;Session()&lt;/code&gt; object is created, it will check if a similar object is already present using the session id. The &lt;code&gt;exists()&lt;/code&gt; function can check if an existing object is associated with a key and the &lt;code&gt;delete()&lt;/code&gt; method can remove an instantiated object from the session store if needed. This multiton implementation is similar to the one previously implemented at the beginning of this publication. Then, we need a way to check a session id.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;checkSessionId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;RegExp&lt;/span&gt; &lt;span class="n"&gt;regexp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sx"&gt;r'^([a-zA-Z0-9_-])+$'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;regexp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;firstMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A session id is a base64url like String, with - for the moment - a fixed length of 32 characters. It should be enough to avoid collision. The &lt;code&gt;checkSessionId()&lt;/code&gt; function is dealing with that, but how to generate a random session id?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;generateSessionId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(;&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;nextInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base64url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, the &lt;code&gt;generateSessionId()&lt;/code&gt; function will generate a random session id as Base64url String using &lt;code&gt;Random.secure()&lt;/code&gt; object from &lt;code&gt;dart:math&lt;/code&gt; package. The returned String is cryptographically secure (at least, if one can trust the Dart API documentation).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;base64url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;b64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;toBase64Url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b64&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last function called &lt;code&gt;base64url()&lt;/code&gt; converts a list of integers to a base64url &lt;code&gt;String&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;session_test&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'session'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'create a new session store'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;isA&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;());&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'dealing with sessions'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isA&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CacheStore&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;());&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isA&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CacheStore&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;());&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test2"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test2"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

      &lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

      &lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test2"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="go"&gt;00:00 +4: All tests passed!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Relic Handlers
&lt;/h1&gt;

&lt;p&gt;The previous parts were talking about the logic of the application, the next parts will talk about the integration of this logic with &lt;a href="https://pub.dev/packages/relic" rel="noopener noreferrer"&gt;Relic&lt;/a&gt;, a pure Dart HTTP server.&lt;/p&gt;

&lt;p&gt;A relic server is using &lt;a href="https://docs.dartrelic.dev/reference/handlers" rel="noopener noreferrer"&gt;handlers&lt;/a&gt; to deal with &lt;a href="https://pub.dev/documentation/relic/latest/relic/Request-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Request()&lt;/code&gt;&lt;/a&gt; objects. A request is made when a client is requesting data from a server. A &lt;code&gt;Request()&lt;/code&gt; object got some really interesting attributes and methods, mostly used to route it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/Message/body.html" rel="noopener noreferrer"&gt;&lt;code&gt;body&lt;/code&gt;&lt;/a&gt; property, contains the body of the request as &lt;code&gt;Stream&lt;/code&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/Request/connectionInfo.html" rel="noopener noreferrer"&gt;&lt;code&gt;connectionInfo&lt;/code&gt;&lt;/a&gt; property, contains a &lt;a href="https://pub.dev/documentation/relic/latest/relic/Request/connectionInfo.html" rel="noopener noreferrer"&gt;&lt;code&gt;ConnectionInfo&lt;/code&gt;&lt;/a&gt; object which store the client IP address and TCP port information;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/Message/headers.html" rel="noopener noreferrer"&gt;&lt;code&gt;header&lt;/code&gt;&lt;/a&gt; property, contains the HTTP &lt;a href="https://pub.dev/documentation/relic/latest/relic/Headers-class.html" rel="noopener noreferrer"&gt;Headers&lt;/a&gt; of the client;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/Request/method.html" rel="noopener noreferrer"&gt;&lt;code&gt;method&lt;/code&gt;&lt;/a&gt; property, containing the HTTP method used by the client like GET or POST;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/QueryParametersRequestEx/queryParameters.html" rel="noopener noreferrer"&gt;&lt;code&gt;queryParameters&lt;/code&gt;&lt;/a&gt; property, contains the queries passed by the client;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/RoutingRequestEx/rawPathParameters.html" rel="noopener noreferrer"&gt;&lt;code&gt;rawPathParameters&lt;/code&gt;&lt;/a&gt; property contains the parameters parsed from the path;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/Request/url.html" rel="noopener noreferrer"&gt;&lt;code&gt;url&lt;/code&gt;&lt;/a&gt; property contains the full url used by the client;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/Message/readAsString.html" rel="noopener noreferrer"&gt;&lt;code&gt;read&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://pub.dev/documentation/relic/latest/relic/Message/read.html" rel="noopener noreferrer"&gt;&lt;code&gt;readAsString&lt;/code&gt;&lt;/a&gt; methods are used to extract the body from the request.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After applying the logic on a &lt;a href="https://pub.dev/documentation/relic/latest/relic/Request-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Request()&lt;/code&gt;&lt;/a&gt; object, an &lt;a href="https://pub.dev/documentation/relic/latest/relic/HandlerObject-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Handler&lt;/code&gt;&lt;/a&gt; should return a &lt;a href="https://pub.dev/documentation/relic/latest/relic/Response-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;Response()&lt;/code&gt;&lt;/a&gt; object. This response will help to return a specific message to the client when using one of those &lt;a href="https://pub.dev/documentation/relic/latest/relic/Response-class.html#constructors" rel="noopener noreferrer"&gt;constructors&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/Response/Response.ok.html" rel="noopener noreferrer"&gt;&lt;code&gt;Response.ok()&lt;/code&gt;&lt;/a&gt; to return a 200 OK HTTP response;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/Response/Response.notFound.html" rel="noopener noreferrer"&gt;&lt;code&gt;Response.notFound()&lt;/code&gt;&lt;/a&gt; to return a 404 NOT FOUND HTTP Response;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/Response/Response.internalServerError.html" rel="noopener noreferrer"&gt;&lt;code&gt;Response.internalServerError()&lt;/code&gt;&lt;/a&gt; to return a 500 Internal Server Error HTTP Response.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More of them are present in the documentation, don't hesitate to use the one required for your needs.&lt;/p&gt;

&lt;p&gt;Anyway, come back to the cache application; those handlers are currently stored in &lt;code&gt;lib/handlers.dart&lt;/code&gt; but a better convention would probably to store them by features. Here the header.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:math'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:relic/relic.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'store.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'session.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first step for a client is to ask for a session, on the path &lt;code&gt;/sessions&lt;/code&gt;. A session id is generated (base64url &lt;code&gt;String&lt;/code&gt;) and returned to the client only if the server can do it. This feature is defined by the &lt;code&gt;newSessionHandler()&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="nf"&gt;newSessionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generateSessionId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A client can also check if one of its session is existing (or not). In this case, it will check the path &lt;code&gt;/sessions/:session&lt;/code&gt; where &lt;code&gt;:session&lt;/code&gt; is a session id. If the session exists, the server returns a 200 OK, if not, a 404 NOT FOUND. The &lt;code&gt;getSessionHandler()&lt;/code&gt; function is taking care of this handler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="nf"&gt;getSessionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rawPathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;#session&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;notFound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A client can also remove a session by using the &lt;code&gt;/session/:session&lt;/code&gt; path with the help of the &lt;code&gt;DELETE&lt;/code&gt; HTTP method. This feature is implemented in the &lt;code&gt;deleteSessionHandler()&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="nf"&gt;deleteSessionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rawPathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;#session&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;notFound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'not found'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A client with a valid session id can see the number of keys set in the cache by using the &lt;code&gt;/sessions/:session/keys&lt;/code&gt;. This is currently not implemented, but it will be managed by the &lt;code&gt;getCacheLengthHandler()&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getCacheLengthHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rawPathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;#session&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;CacheSession&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A client with a valid session id can retrieve a key from a cache with the path &lt;code&gt;/cache/:session/:key&lt;/code&gt; where &lt;code&gt;:key&lt;/code&gt; is a String used as key in the cache store. This feature is implemented in the &lt;code&gt;getCacheHandler()&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getCacheHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rawPathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;#session&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;CacheSession&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rawPathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;#key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;notFound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"not found"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A client with a valid session id can also create a new key with a value. In this case, the client will use the &lt;code&gt;/cache/:session/:key&lt;/code&gt; with the HTTP POST method. The body of the request will be the value associated with the key (then the data stored in the cache). This feature is implemented in &lt;code&gt;postCacheHandler()&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;postCacheHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rawPathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;#session&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;CacheSession&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rawPathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;#key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readAsString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;maxLength:&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, a client with a valid session can also delete a key/value using the &lt;code&gt;/cache/:session/:key&lt;/code&gt; path and with the &lt;code&gt;DELETE&lt;/code&gt; HTTP method. The &lt;code&gt;deleteCacheHandler()&lt;/code&gt; function was created to implement this behavior.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;deleteCacheHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rawPathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;#session&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;CacheSession&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rawPathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;#key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I still don't really know how to deal with the tests though, so, it will be done later. For now, the handlers created there are pretty simple and should not do lot of mess.&lt;/p&gt;

&lt;h1&gt;
  
  
  Relic Middleware
&lt;/h1&gt;

&lt;p&gt;A Relic &lt;a href="https://docs.dartrelic.dev/reference/middleware" rel="noopener noreferrer"&gt;Middleware&lt;/a&gt; can be seen as a pipeline. It will got a request as input, modify it and returns it as input, before any handlers. A feature called &lt;a href="https://docs.dartrelic.dev/reference/pipeline" rel="noopener noreferrer"&gt;Pipeline&lt;/a&gt; was previously available from Relic, but it is now deprecated.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://pub.dev/documentation/relic/latest/relic/Middleware.html" rel="noopener noreferrer"&gt;Middleware&lt;/a&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/MiddlewareObject-class.html" rel="noopener noreferrer"&gt;https://pub.dev/documentation/relic/latest/relic/MiddlewareObject-class.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our case, the middlewares are currently all stored in &lt;code&gt;lib/middlewares.dart&lt;/code&gt; module; we don't have a lot of them, so, it should be okay for the moment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:relic/relic.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'store.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'session.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of the middleware, called &lt;code&gt;checkSessionId()&lt;/code&gt; will be in charge to check if a session id exists or not. If it does not exist, a 404 not found is returned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// check if the session id has been defined in the&lt;/span&gt;
&lt;span class="c1"&gt;/// URL path.&lt;/span&gt;
&lt;span class="n"&gt;Middleware&lt;/span&gt; &lt;span class="nf"&gt;checkSessionId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Handler&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rawPathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;#session&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;badRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'sessionId not defined'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I think I made some duplicated code. Anyway, not really important for the moment, it will also be another good reason to test the Dart analyzer tool, and delete dead code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// check if the session id exists in the sessions&lt;/span&gt;
&lt;span class="c1"&gt;/// store.&lt;/span&gt;
&lt;span class="n"&gt;Middleware&lt;/span&gt; &lt;span class="nf"&gt;checkSessionExists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Handler&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rawPathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;#session&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;CacheSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;badRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'sessionId "&lt;/span&gt;&lt;span class="si"&gt;${sessionId}&lt;/span&gt;&lt;span class="s"&gt;" does not exist'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another middleware can be used to check if a key is present in the cache. Like the previous definition, it will return a not found if it's not the case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// check if the key has been defined in the URL path&lt;/span&gt;
&lt;span class="n"&gt;Middleware&lt;/span&gt; &lt;span class="nf"&gt;checkKeyExists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Handler&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rawPathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;#key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;badRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'key not defined'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3 middlewares have been created, more of them can be added, but let stay with that for now, if you want to have some ideas for the next ones to implement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;a middleware to check the &lt;code&gt;:session&lt;/code&gt; id, it must be a valid base64url;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a middleware to check the &lt;code&gt;:key&lt;/code&gt;, it must be a valid base64url String and limited to 64 characters;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a middleware to check the size of the data sent by the client, it should be limited (like 65KB).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Relic Routing
&lt;/h1&gt;

&lt;p&gt;Great, we know how to create &lt;a href="https://pub.dev/documentation/relic/latest/relic/Handler.html" rel="noopener noreferrer"&gt;Handlers&lt;/a&gt; and &lt;a href="https://pub.dev/documentation/relic/latest/relic/Middleware.html" rel="noopener noreferrer"&gt;Middlewares&lt;/a&gt;, the last step is to assemble them with the help of a routers inside a &lt;a href="https://pub.dev/documentation/relic/latest/relic/RelicApp-class.html" rel="noopener noreferrer"&gt;RelicApp()&lt;/a&gt; object. This time, the code is stored in &lt;code&gt;lib/app.dart&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:relic/relic.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'handlers.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'middlewares.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this application, a function called &lt;code&gt;app()&lt;/code&gt; has been created. This one is returned a &lt;a href="https://pub.dev/documentation/relic/latest/relic/RelicApp-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;RelicApp()&lt;/code&gt;&lt;/a&gt; object, but before returning it, the routes can be configured with the help of the followings methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/Router/use.html" rel="noopener noreferrer"&gt;&lt;code&gt;use()&lt;/code&gt;&lt;/a&gt; method adds a new middleware based on path pattern;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/RouteEx/get.html" rel="noopener noreferrer"&gt;&lt;code&gt;get()&lt;/code&gt;&lt;/a&gt; method is used to deal with GET HTTP method;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/RouteEx/post.html" rel="noopener noreferrer"&gt;&lt;code&gt;post()&lt;/code&gt;&lt;/a&gt; method is used to route the POST HTTP method;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/RouteEx/delete.html" rel="noopener noreferrer"&gt;&lt;code&gt;delete()&lt;/code&gt;&lt;/a&gt; method is used to deal with the DELETE HTTP method;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/relic/Router/fallback.html" rel="noopener noreferrer"&gt;&lt;code&gt;fallback()&lt;/code&gt;&lt;/a&gt; method is the last route called if no others matched a pattern.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, all those methods are following the same structure, the first argument is used to create a path pattern and the second argument is there to add an handler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;RelicApp&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;RelicApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;// sessions API&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/sessions'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newSessionHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/sessions/:session'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkSessionId&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/sessions/:session'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkSessionExists&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/sessions/:session'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deleteSessionHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/sessions/:session'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getSessionHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// cache API&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/cache/:session/:key'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkSessionId&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/cache/:session/:key'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkSessionExists&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/cache/:session/:key'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkKeyExists&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/cache/:session'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getCacheLengthHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/cache/:session/:key'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getCacheHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/cache/:session/:key'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;postCacheHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/cache/:session/:key'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deleteCacheHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// wildcard&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;respondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;notFound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"not found"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, all the routes and their handlers have been defined. The application is practically ready to be started.&lt;/p&gt;

&lt;h1&gt;
  
  
  Application Entry Point
&lt;/h1&gt;

&lt;p&gt;The last brick to this application is the entry-point. This module is stored in &lt;code&gt;bin/cache_relic.dart&lt;/code&gt;. The code is straightforward: start the &lt;a href="https://pub.dev/documentation/relic/latest/relic/RelicApp-class.html" rel="noopener noreferrer"&gt;&lt;code&gt;RelicApp&lt;/code&gt;&lt;/a&gt; listener by executing the &lt;a href="https://pub.dev/documentation/relic/latest/relic/RelicAppIOServeEx/serve.html" rel="noopener noreferrer"&gt;&lt;code&gt;serve()&lt;/code&gt;&lt;/a&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:relic/relic.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:cache_relic/app.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;serve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voilà! It's done! The application should work now.&lt;/p&gt;

&lt;h1&gt;
  
  
  Testing
&lt;/h1&gt;

&lt;p&gt;All bricks are ready, the logic has been created, the middlewares and the handlers have been added in the router and the main entry-point can be executed. Let start the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run
&lt;span class="go"&gt;Building package executable... 
Built cache_relic:cache_relic.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can verify if the application is listening correctly on the port TCP/8080 with the help of &lt;a href=""&gt;&lt;code&gt;ss&lt;/code&gt;&lt;/a&gt;&lt;a href="https://manpages.debian.org/trixie/iproute2/ss.8.en.html" rel="noopener noreferrer"&gt;https://manpages.debian.org/trixie/iproute2/ss.8.en.html&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ss &lt;span class="nt"&gt;-nlpt&lt;/span&gt; sport &lt;span class="o"&gt;=&lt;/span&gt; 8080
&lt;span class="go"&gt;State               Recv-Q              Send-Q                            Local Address:Port                             Peer Address:Port              Process                                                    
LISTEN              0                   128                                   127.0.0.1:8080                                  0.0.0.0:*                  users:(("dart:cache_reli",pid=4137059,fd=9))
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service is listening. First step to use it, generate a new session from &lt;code&gt;/sessions&lt;/code&gt; end-point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl localhost:8080/sessions
&lt;span class="go"&gt;xYgo--V1c8Bmu8Gp-y1WeQ==
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The session id is &lt;code&gt;xYgo--V1c8Bmu8Gp-y1WeQ==&lt;/code&gt;, let use it to store our first data into the cache with the help of &lt;code&gt;/cache/xYgo--V1c8Bmu8Gp-y1WeQ==/${key}&lt;/code&gt; end-point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-XPOST&lt;/span&gt; &lt;span class="nt"&gt;-dtest&lt;/span&gt; localhost:8080/cache/xYgo--V1c8Bmu8Gp-y1WeQ&lt;span class="o"&gt;==&lt;/span&gt;/test
&lt;span class="go"&gt;ok
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks good, but can we be sure it has been correctly stored? We can check that with the &lt;code&gt;/cache/xYgo--V1c8Bmu8Gp-y1WeQ==/${key}&lt;/code&gt; end-point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-XGET&lt;/span&gt; localhost:8080/cache/xYgo--V1c8Bmu8Gp-y1WeQ&lt;span class="o"&gt;==&lt;/span&gt;/test
&lt;span class="go"&gt;test
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice! It seems our data has been correctly stored! Let delete it now, still with the &lt;code&gt;/cache/xYgo--V1c8Bmu8Gp-y1WeQ==/${key}&lt;/code&gt; end-point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-XDELETE&lt;/span&gt; localhost:8080/cache/xYgo--V1c8Bmu8Gp-y1WeQ&lt;span class="o"&gt;==&lt;/span&gt;/test
&lt;span class="go"&gt;ok
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The end-point returns &lt;code&gt;ok&lt;/code&gt;, but let check if it's really the case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-XGET&lt;/span&gt; localhost:8080/cache/xYgo--V1c8Bmu8Gp-y1WeQ&lt;span class="o"&gt;==&lt;/span&gt;/test
&lt;span class="go"&gt;not found
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's good, the data has been correctly removed from the cache store. Let check now the other features we added, especially on the session side. One feature is to check if a session exists, it has been added on the &lt;code&gt;/sessions/xYgo--V1c8Bmu8Gp-y1WeQ==&lt;/code&gt; end-point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-XGET&lt;/span&gt; localhost:8080/sessions/xYgo--V1c8Bmu8Gp-y1WeQ&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="go"&gt;Note: Unnecessary use of -X or --request, GET is already inferred.
* Host localhost:8080 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* HTTPS-RR: -
*   Trying [::1]:8080...
* connect to ::1 port 8080 from ::1 port 57168 failed: Connection refused
*   Trying 127.0.0.1:8080...
* Established connection to localhost (127.0.0.1 port 8080) from 127.0.0.1 port 49010 
* using HTTP/1.x
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;GET /sessions/xYgo--V1c8Bmu8Gp-y1WeQ&lt;span class="o"&gt;==&lt;/span&gt; HTTP/1.1
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Host: localhost:8080
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;User-Agent: curl/8.20.0
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Accept: &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="go"&gt;* Request completely sent off
&amp;lt; HTTP/1.1 200 OK
&lt;/span&gt;&lt;span class="gp"&gt;&amp;lt; content-type: text/plain;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;utf-8
&lt;span class="go"&gt;&amp;lt; date: Sun, 07 Jun 2026 16:20:40 GMT
&amp;lt; content-length: 0
&amp;lt; 
&lt;/span&gt;&lt;span class="gp"&gt;* Connection #&lt;/span&gt;0 to host localhost:8080 left intact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returns a 200 HTTP Code, it means it exists. Let delete the session now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-XDELETE&lt;/span&gt; localhost:8080/sessions/xYgo--V1c8Bmu8Gp-y1WeQ&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="go"&gt;xYgo--V1c8Bmu8Gp-y1WeQ==
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The session id is returned to the client, and the server looks happy. Let check if the session has been correctly removed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-XGET&lt;/span&gt; localhost:8080/sessions/xYgo--V1c8Bmu8Gp-y1WeQ&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="go"&gt;sessionId "xYgo--V1c8Bmu8Gp-y1WeQ==" does not exist
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;xYgo--V1c8Bmu8Gp-y1WeQ==&lt;/code&gt; session does not exist anymore. I think the application is working has expected.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;The implementation is working correctly, but the code looks dirty, using for example something like a factory to deal with the instantiated objects does not seem to be the right way. In fact, using a factory like that can probably lead to race conditions and other issues. In fact, the factories in Dart are more black magic stuff, even in the language specifications, it's kinda hard to understand how they are working or how they have been implemented.&lt;/p&gt;

&lt;p&gt;Furthermore, because this "project" was a draft to test factories in Dart, no tests have been created (really bad practices), but it was also not designed to be used in production! So, be careful if you want to use it for one of your project, and please review it.&lt;/p&gt;

&lt;p&gt;Anyway, here a list of cool features to add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add tests for middlewares&lt;/li&gt;
&lt;li&gt;Add tests for handlers&lt;/li&gt;
&lt;li&gt;Add integration tests&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Global:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the session ttl can be set from the CLI (e.g. &lt;code&gt;--session.ttl=60&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;the session limit size can be set from the CLI (e.g. &lt;code&gt;--session.size.max=123&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;the default key size can be changed from the CLI (e.g. &lt;code&gt;--cache.key.size.max=123&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;the default value size can be changed from the CLI (e.g. &lt;code&gt;--cache.value.size.max=123&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;those parameters could be stored in another immutable factory.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Session:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only a limited amount of session can be created, if full, the API returns not more sessions can't be created (session cache full);&lt;/li&gt;
&lt;li&gt;A session has a limited period of validity (60s), after this period, it is automatically removed, except if the client ask for more time;&lt;/li&gt;
&lt;li&gt;A session is limited in size (bytes);&lt;/li&gt;
&lt;li&gt;A session can be exported as JSON.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Cache:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A store key can be copied to another existing session;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;x-cache-session-ttl&lt;/code&gt; can be added on all request, it will contain the remaining times before the session will be removed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As usual, if you want to know a bit more, dig a bit deeper into this topic, here few resources that could probably help you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/packages/relic" rel="noopener noreferrer"&gt;Relic package&lt;/a&gt; on pub.dev;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/relic/latest/" rel="noopener noreferrer"&gt;Relic API Documentation&lt;/a&gt;, where you will find the full documentation of the API;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.dartrelic.dev/" rel="noopener noreferrer"&gt;Official Relic Website&lt;/a&gt;, where you will find the official documentation and lot of examples;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/serverpod/relic" rel="noopener noreferrer"&gt;Relic Repository&lt;/a&gt; at Github, where you will find the full relic implementation source code, you should check the examples and the test suite;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/packages/in_memory_store" rel="noopener noreferrer"&gt;&lt;code&gt;in_memory_store&lt;/code&gt; package&lt;/a&gt;, this package would have been a great alternative to store data in memory, adding and testing it in the future could be great.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repository containing the source code can be found in &lt;a href="https://github.com/niamtokik/cache_relic" rel="noopener noreferrer"&gt;&lt;code&gt;niamtokik/cache_relic&lt;/code&gt;&lt;/a&gt; repository on Github.&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@daneelzam?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Daniil Zameshaev&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-table-topped-with-lots-of-brown-vases-pTxV4nn6S6A?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>relic</category>
      <category>dart</category>
      <category>cache</category>
      <category>http</category>
    </item>
    <item>
      <title>Interactive Shell with Dart</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Tue, 09 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/interactive-shell-with-dart-2dal</link>
      <guid>https://dev.to/niamtokik/interactive-shell-with-dart-2dal</guid>
      <description>&lt;p&gt;Any modern languages should have its REPL, Erlang, Elixir, Haskell, Clojure, Ocaml, Python... All of them got one shell-like feature to interact with the application in conception phase. That's kinda mandatory to me, it permits to test functions before adding them in the final commit, it can be used to draft quick ideas and so on. When I started learning Dart, I was a bit sad it was not integrated by default... but someone decided to fix that by creating the &lt;a href="https://pub.dev/packages/interactive" rel="noopener noreferrer"&gt;&lt;code&gt;interactive&lt;/code&gt;&lt;/a&gt; package! Let create a new project called &lt;code&gt;myrepl&lt;/code&gt; just to check that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart create myrepl
&lt;span class="go"&gt;Creating myrepl using template console...

  .gitignore
  analysis_options.yaml
  CHANGELOG.md
  pubspec.yaml
  README.md
  bin/myrepl.dart
  lib/myrepl.dart
  test/myrepl_test.dart

Running pub get...                     0.3s
  Resolving dependencies...
  Downloading packages...
  Changed 48 dependencies!

Created project myrepl in myrepl! In order to get started, run the following commands:

  cd myrepl
  dart run

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;myrepl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As usual, the new dependency can be added using &lt;code&gt;dart pub add&lt;/code&gt;. &lt;code&gt;interactive&lt;/code&gt; depends on 12 dependencies, so, it could be better to add it only in the &lt;code&gt;dev&lt;/code&gt; environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart pub add interactive
&lt;span class="go"&gt;Resolving dependencies... 
Downloading packages... 
&amp;lt; _fe_analyzer_shared 85.0.0 (was 100.0.0) (100.0.0 available)
&amp;lt; analyzer 7.7.1 (was 13.0.0) (13.0.0 available)
+ cli_repl 0.2.3
&amp;lt; coverage 1.6.4 (was 1.15.0) (1.15.0 available)
+ interactive 1.4.1
+ js 0.6.7 (0.7.2 available)
&amp;lt; matcher 0.12.17 (was 0.12.20) (0.12.20 available)
&amp;lt; test 1.26.3 (was 1.31.1) (1.31.1 available)
&amp;lt; test_api 0.7.7 (was 0.7.12) (0.7.12 available)
&amp;lt; test_core 0.6.12 (was 0.6.18) (0.6.18 available)
&amp;lt; vm_service 11.10.0 (was 15.2.0) (15.2.0 available)
These packages are no longer being depended on:
- cli_config 0.2.0
Changed 12 dependencies!
9 packages have newer versions incompatible with dependency constraints.
Try `dart pub outdated` for more information
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, &lt;code&gt;interactive&lt;/code&gt; package must be compiled and activated on the system. It will be installed locally in &lt;code&gt;${HOME}/.pub-cache/bin&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart pub global activate interactive
&lt;span class="go"&gt;Package interactive is currently active at version 1.4.1.
Downloading packages... . 
  _fe_analyzer_shared 85.0.0 (100.0.0 available)
  analyzer 7.7.1 (13.0.0 available)
  js 0.6.7 (0.7.2 available)
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;meta 1.18.3 &lt;span class="o"&gt;(&lt;/span&gt;was 1.18.2&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;  vm_service 11.10.0 (15.2.0 available)
Building package executables... (2.5s)
Built interactive:interactive.
Installed executable interactive.
&lt;/span&gt;&lt;span class="gp"&gt;Warning: Pub installs executables into $&lt;/span&gt;HOME/.pub-cache/bin, which is not on your path.
&lt;span class="go"&gt;You can fix that by adding this to your shell's config file (.bashrc, .bash_profile, .zshrc etc.):

&lt;/span&gt;&lt;span class="gp"&gt;  export PATH="$&lt;/span&gt;PATH&lt;span class="s2"&gt;":"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.pub-cache/bin&lt;span class="s2"&gt;"
&lt;/span&gt;&lt;span class="go"&gt;
Activated interactive 1.4.1.

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;file &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/.pub-cache/bin/interactive 
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;HOME&lt;span class="o"&gt;}&lt;/span&gt;/.pub-cache/bin/interactive: a sh script, ASCII text executable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;${HOME}/.pub-cache/bin/interactive&lt;/code&gt; file is a simple shell script using the dart VM to execute the &lt;code&gt;interactive&lt;/code&gt; package. The command executed looks like the snippet below. It means this code could be executed anywhere on your system if the &lt;code&gt;interactive&lt;/code&gt; package is installed (it will produce some errors outside a Dart project directory).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;dart pub -v global run interactive:interactive "$&lt;/span&gt;@&lt;span class="s2"&gt;"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anyway, let export the Dart &lt;code&gt;bin&lt;/code&gt; directory inside our &lt;code&gt;PATH&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.pub-cache/bin"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Let execute the &lt;code&gt;interactive&lt;/code&gt; command (it should be present in our &lt;code&gt;PATH&lt;/code&gt; now).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;interactive
&lt;span class="go"&gt;Run: /bin/dart [--enable-vm-service=34273, file:///bin/interactive.dart-3.11.5.snapshot, --vm-service-was-enabled]
Workspace: /tmp/dart_interactive_workspace_2026-06-04T163359990891
The Dart VM service is listening on http://127.0.0.1:34273/HveG-LmYIiQ=/
The Dart DevTools debugger and profiler is available at: http://127.0.0.1:34273/HveG-LmYIiQ=/devtools/?uri=ws://127.0.0.1:34273/HveG-LmYIiQ=/ws
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command invoke a shell with a &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; prompt. What will happen if we create some Dart code?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; 1+1
&lt;span class="go"&gt;2

&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; class Meh &lt;span class="o"&gt;{&lt;/span&gt; String _meh &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; Meh&lt;span class="o"&gt;(&lt;/span&gt;String meh&lt;span class="o"&gt;)&lt;/span&gt; : this._meh &lt;span class="o"&gt;=&lt;/span&gt; meh&lt;span class="p"&gt;;&lt;/span&gt; String toString&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;_meh&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; Meh&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;test!

&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; import &lt;span class="s1"&gt;'dart:async'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; Future&amp;lt;int&amp;gt; t&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="go"&gt;[WARNING 2026-06-04 20:25:05.859183] Error: Hot reload failed, maybe because code has syntax error?

&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; Future&amp;lt;int&amp;gt; t&lt;span class="o"&gt;()&lt;/span&gt; async &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; t&lt;span class="o"&gt;()&lt;/span&gt;.then&lt;span class="o"&gt;((&lt;/span&gt;x&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; print&lt;span class="o"&gt;(&lt;/span&gt;x&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="gp"&gt;Instance of 'Future&amp;lt;void&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;&amp;gt;&amp;gt; 1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works! &lt;code&gt;interactive&lt;/code&gt; gives us a way to dynamically create Dart code, like any modern language! That's very cool! It's even possible to execute command from the system itself by prefixing the line to execute with &lt;code&gt;!&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="go"&gt;lib
pubspec.lock
pubspec.yaml

&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;
&lt;span class="go"&gt;/tmp/dart_interactive_workspace_2026-06-04T201405364736
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This is a great project, and it could be even better to see it be added in the SDK! At this time, it works, but it feels like it's a bit unstable and not finished yet. The shell is working but can be slow sometimes. The fact the &lt;code&gt;devtools&lt;/code&gt; are already started by default is also another nice feature. What's missing to me? Here a quick list of cool stuff to add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;adding a successful command line index counter, every time a command is being executed, the counter is increased;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;removing the "..." for the multi line support, it can be annoying when doing a copy/paste;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;integrating the &lt;code&gt;devtools&lt;/code&gt; directly into the shell, like &lt;code&gt;@profile&lt;/code&gt; to profile the application or &lt;code&gt;@memory&lt;/code&gt; to see memory usage; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;saved session with state and command history restore;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;debugger and tracer integration;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;documentation and manual integration like &lt;code&gt;help(Object)&lt;/code&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;class, function and command Auto-completion when pressing tab;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;handlers, hooks and other features to make a custom shell.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As usual, if you want to know more about this project, here some links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;a href="https://pub.dev/packages/interactive" rel="noopener noreferrer"&gt;&lt;code&gt;interactive&lt;/code&gt; package&lt;/a&gt; at pub.dev;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;a href="https://github.com/fzyzcjy/dart_interactive" rel="noopener noreferrer"&gt;official &lt;code&gt;interactive&lt;/code&gt; package source code&lt;/a&gt; at Github;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;a href="https://pub.dev/documentation/interactive/latest/" rel="noopener noreferrer"&gt;&lt;code&gt;interactive&lt;/code&gt; API documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Bonus: devtools
&lt;/h1&gt;

&lt;p&gt;While testing &lt;code&gt;interactive&lt;/code&gt; I discovered the Dart &lt;a href="https://dart.dev/tools/dart-devtools" rel="noopener noreferrer"&gt;&lt;code&gt;devtools&lt;/code&gt; suite&lt;/a&gt;. It's a pretty cool way to debug, trace and profile a Dart application, thanks to the Dart virtual machine. This application is working a bit like the different tools we have with the BEAM (e.g. observer, debugger and so on). Here some screenshot from the previous project created.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://127.0.0.1:34273/HveG-LmYIiQ=/devtools/home?compiler=wasm" rel="noopener noreferrer"&gt;http://127.0.0.1:34273/HveG-LmYIiQ=/devtools/home?compiler=wasm&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2i5vup0s59rdgjhr6ivj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2i5vup0s59rdgjhr6ivj.png" alt="Connection Page" width="799" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwhzaq4el6fmhorc8free.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwhzaq4el6fmhorc8free.png" alt="Performance Page" width="799" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frpbnm4lrwzg3wspbjkyy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frpbnm4lrwzg3wspbjkyy.png" alt="CPU Profiler Page" width="799" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipv0v6t5le10usx4bbyc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipv0v6t5le10usx4bbyc.png" alt="Memory Page" width="799" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F00k674eyay48gy1u4scf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F00k674eyay48gy1u4scf.png" alt="Network Page" width="799" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A full post on this feature will be required. For now, it's just a quick overview based on screenshots.&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@giuliamay?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Giulia May&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-close-up-of-a-shell-with-a-white-background-cNtMy74-mnI?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dart</category>
      <category>cli</category>
      <category>repl</category>
      <category>shell</category>
    </item>
    <item>
      <title>Missing Libraries on OpenBSD</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Mon, 08 Jun 2026 10:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/missing-libraries-on-openbsd-2n4p</link>
      <guid>https://dev.to/niamtokik/missing-libraries-on-openbsd-2n4p</guid>
      <description>&lt;p&gt;Sometimes, shit happens, and if you are unlucky, it can be many time in a short period of time! Anyway, following the upgrade of &lt;a href="https://www.openbsd.org/" rel="noopener noreferrer"&gt;OpenBSD&lt;/a&gt; to 7.9 on one of my laptop, I got another issue with missing libraries. I still don't know what happened, but I suspect a problem with &lt;a href="https://man.openbsd.org/pkg_add" rel="noopener noreferrer"&gt;&lt;code&gt;pkg_add&lt;/code&gt;&lt;/a&gt;. While installing a package, this one crashed. Anyway, I will give you a way to fix that. First, let describe the problem.&lt;/p&gt;

&lt;p&gt;I wanted to use &lt;a href="https://openports.pl/path/graphics/ffmpeg" rel="noopener noreferrer"&gt;&lt;code&gt;ffmpeg&lt;/code&gt;&lt;/a&gt; to extract audio from a video file, but when executing it, here the message I got:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ffmpeg
&lt;span class="go"&gt;ld.so: ffmpeg: can't load library 'libavfilter.so'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ah! That's annoying. Where this library come from? Let use &lt;a href="https://man.openbsd.org/pkglocate" rel="noopener noreferrer"&gt;&lt;code&gt;pkglocate&lt;/code&gt;&lt;/a&gt; to have our answer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pkglocate libavfilter.so
&lt;span class="go"&gt;ffmpeg-8.0.1p1v1:graphics/ffmpeg:/usr/local/lib/libavfilter.so.13.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ah! This is even more annoying, this library is coming from &lt;a href="https://openports.pl/path/graphics/ffmpeg" rel="noopener noreferrer"&gt;&lt;code&gt;ffmpeg&lt;/code&gt;&lt;/a&gt; package, but it seems &lt;a href="https://openports.pl/path/graphics/ffmpeg" rel="noopener noreferrer"&gt;&lt;code&gt;ffmpeg&lt;/code&gt;&lt;/a&gt; is installed, right? Let check that with &lt;code&gt;pkg_info&lt;/code&gt; (just to confirm).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pkg_info &lt;span class="nt"&gt;-l&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;ffmpeg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Indeed. The package is correctly installed. Let investigate a bit with &lt;a href="https://man.openbsd.org/kdump" rel="noopener noreferrer"&gt;&lt;code&gt;kdump&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://man.openbsd.org/ktrace" rel="noopener noreferrer"&gt;&lt;code&gt;ktrace&lt;/code&gt;&lt;/a&gt;, the equivalent version of &lt;a href="https://manpages.debian.org/trixie/strace/strace.1.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;strace&lt;/code&gt;&lt;/a&gt; for OpenBSD. This part is simply for fun, because I was already aware of the problem, but it will give you an idea of the procedure I would have use if I was looking for something weird.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ktrace ffmpeg
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kdump
&lt;span class="go"&gt; 75640 ktrace   RET   ktrace 0
&lt;/span&gt;&lt;span class="gp"&gt; 75640 ktrace   CALL  mmap(0,0xd7,0x3&amp;lt;PROT_READ|PROT_WRITE&amp;gt;&lt;/span&gt;,0x1002&amp;lt;MAP_PRIVATE|MAP_ANON&amp;gt;,-1,0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt; 75640 ktrace   RET   mmap 6423225446400/0x5d786123000
 75640 ktrace   CALL  execve(0x73aa771e3d40,0x73aa771e4340,0x73aa771e4350)
 75640 ktrace   NAMI  "/bin/ffmpeg"
 75640 ktrace   RET   execve -1 errno 2 No such file or directory
 75640 ktrace   CALL  execve(0x73aa771e3d40,0x73aa771e4340,0x73aa771e4350)
 75640 ktrace   NAMI  "/bin/ffmpeg"
 75640 ktrace   RET   execve -1 errno 2 No such file or directory
 75640 ktrace   CALL  execve(0x73aa771e3d40,0x73aa771e4340,0x73aa771e4350)
 75640 ktrace   NAMI  "/sbin/ffmpeg"
 75640 ktrace   RET   execve -1 errno 2 No such file or directory
 75640 ktrace   CALL  execve(0x73aa771e3d40,0x73aa771e4340,0x73aa771e4350)
 75640 ktrace   NAMI  "/usr/bin/ffmpeg"
 75640 ktrace   RET   execve -1 errno 2 No such file or directory
 75640 ktrace   CALL  execve(0x73aa771e3d40,0x73aa771e4340,0x73aa771e4350)
 75640 ktrace   NAMI  "/usr/sbin/ffmpeg"
 75640 ktrace   RET   execve -1 errno 2 No such file or directory
 75640 ktrace   CALL  execve(0x73aa771e3d40,0x73aa771e4340,0x73aa771e4350)
 75640 ktrace   NAMI  "/usr/X11R6/bin/ffmpeg"
 75640 ktrace   RET   execve -1 errno 2 No such file or directory
 75640 ktrace   CALL  execve(0x73aa771e3d40,0x73aa771e4340,0x73aa771e4350)
 75640 ktrace   NAMI  "/usr/local/bin/ffmpeg"
 75640 ktrace   ARGS  
        [0] = "ffmpeg"
 75640 ffmpeg   NAMI  "/usr/libexec/ld.so"
 75640 ffmpeg   NAMI  "/var/run/ld.so.hints"
 75640 ffmpeg   RET   open 3
 75640 ffmpeg   CALL  fstat(3,0x7183b3383150)
&lt;/span&gt;&lt;span class="gp"&gt; 75640 ffmpeg   STRU  struct stat { dev=66628, ino=984995, mode=-r--r--r-- , nlink=1, uid=0&amp;lt;"root"&amp;gt;&lt;/span&gt;, &lt;span class="nv"&gt;gid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&amp;lt;&lt;span class="s2"&gt;"wheel"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;, &lt;span class="nv"&gt;rdev&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3943528, &lt;span class="nv"&gt;atime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1780508371&amp;lt;&lt;span class="s2"&gt;"Jun  3 17:39:31 2026"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.216788585, &lt;span class="nv"&gt;mtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1780508368&amp;lt;&lt;span class="s2"&gt;"Jun  3 17:39:28 2026"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.926794253, &lt;span class="nv"&gt;ctime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1780508368&amp;lt;&lt;span class="s2"&gt;"Jun  3 17:39:28 2026"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.926794253, &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;11270, &lt;span class="nv"&gt;blocks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;24, &lt;span class="nv"&gt;blksize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;16384, &lt;span class="nv"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x0, &lt;span class="nv"&gt;gen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x0 &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="go"&gt; 75640 ffmpeg   RET   fstat 0
&lt;/span&gt;&lt;span class="gp"&gt; 75640 ffmpeg   CALL  mmap(0,0x2c06,0x1&amp;lt;PROT_READ&amp;gt;&lt;/span&gt;,0x2&amp;lt;MAP_PRIVATE&amp;gt;,3,0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt; 75640 ffmpeg   RET   mmap 6587275476992/0x5fdb8370000
&lt;/span&gt;&lt;span class="gp"&gt; 75640 ffmpeg   CALL  mmap(0,0x1000,0x3&amp;lt;PROT_READ|PROT_WRITE&amp;gt;&lt;/span&gt;,0x1002&amp;lt;MAP_PRIVATE|MAP_ANON&amp;gt;,-1,0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt; 75640 ffmpeg   RET   mmap 6585773539328/0x5fd5eb14000
 75640 ffmpeg   CALL  mimmutable(0x5fdb8370000,0x2c06)
 75640 ffmpeg   RET   mimmutable 0
 75640 ffmpeg   CALL  close(3)
 75640 ffmpeg   RET   close 0
&lt;/span&gt;&lt;span class="gp"&gt; 75640 ffmpeg   CALL  open(0x5fd5eb14150,0x30000&amp;lt;O_RDONLY|O_CLOEXEC|O_DIRECTORY&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt; 75640 ffmpeg   NAMI  "/usr/lib"
 75640 ffmpeg   RET   open 3
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt; 75640 ffmpeg   GIO   fd 2 wrote 15 bytes
       "ld.so: ffmpeg: "
 75640 ffmpeg   RET   write 15/0xf
 75640 ffmpeg   CALL  write(2,0x5fcc3339190,0x23)
 75640 ffmpeg   GIO   fd 2 wrote 35 bytes
       "can't load library 'libavfilter.so'"
 75640 ffmpeg   RET   write 35/0x23
 75640 ffmpeg   CALL  write(2,0x5fcc3237b0c,0x1)
 75640 ffmpeg   GIO   fd 2 wrote 1 bytes
       "
       "
 75640 ffmpeg   RET   write 1
 75640 ffmpeg   CALL  thrkill(0,SIGKILL,0)
 75640 ffmpeg   PSIG  SIGKILL SIG_DFL
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interesting. It looks like &lt;code&gt;ld.hints&lt;/code&gt; is having only &lt;code&gt;/usr/lib&lt;/code&gt; configured, let check that with &lt;a href="https://man.openbsd.org/ldconfig.8" rel="noopener noreferrer"&gt;&lt;code&gt;ldconfig&lt;/code&gt;&lt;/a&gt; and the &lt;code&gt;-r&lt;/code&gt; flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ldconfig &lt;span class="nt"&gt;-r&lt;/span&gt;
&lt;span class="go"&gt;ldconfig -r                                                               
/var/run/ld.so.hints:                           
        search directories: /usr/lib
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doh. The problem is from &lt;code&gt;/var/run/ld.so.hints&lt;/code&gt;, this file should contain a list of libraries available on the system, here, it returns literally nothing. Let fix that by reusing &lt;a href="https://man.openbsd.org/ldconfig.8" rel="noopener noreferrer"&gt;&lt;code&gt;ldconfig&lt;/code&gt;&lt;/a&gt; but this time to recreate the file correctly with the help of the &lt;code&gt;-m&lt;/code&gt; and &lt;code&gt;-R&lt;/code&gt; flags.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;doas ldconfig &lt;span class="nt"&gt;-mR&lt;/span&gt; /usr/lib /usr/X11R6/lib /usr/local/lib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let check if the &lt;code&gt;ld.so.hints&lt;/code&gt; file now contain more libraries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ldconfig &lt;span class="nt"&gt;-r&lt;/span&gt;
&lt;span class="go"&gt;/var/run/ld.so.hints:
        search directories: /usr/lib
&lt;/span&gt;&lt;span class="gp"&gt;        0:-liberty.12.0 =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/usr/lib/libiberty.so.12.0
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="gp"&gt;        1055:-lhogweed.3.1 =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/usr/local/lib/libhogweed.so.3.1
&lt;span class="gp"&gt;        1056:-latkmm-1.6.10.1 =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/usr/local/lib/libatkmm-1.6.so.10.1
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="gp"&gt;        1168:-lpixman-1.46.4 =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/usr/X11R6/lib/libpixman-1.so.46.4
&lt;span class="gp"&gt;        1169:-lpoppler.101.0 =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/usr/local/lib/libpoppler.so.101.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's way better like that! Okay, let run &lt;a href="https://openports.pl/path/graphics/" rel="noopener noreferrer"&gt;&lt;code&gt;ffmpeg&lt;/code&gt;&lt;/a&gt; another time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ffmpeg &lt;span class="nt"&gt;-version&lt;/span&gt;
&lt;span class="go"&gt;ffmpeg version 8.0.2 Copyright (c) 2000-2025 the FFmpeg developers
built with OpenBSD clang version 19.1.7
configuration: --enable-shared --arch=amd64 --cc=cc --cxx=c++ --enable-debug --disable-stripping --disable-indev=jack --disable-vulkan --enable-fontconfig --enable-frei0r --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libdav1d --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libharfbuzz --enable-libmp3lame --enable-libopus --enable-libspeex --enable-libsvtav1 --enable-libtheora --enable-libv4l2 --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzimg --enable-nonfree --enable-openssl --enable-libvidstab --extra-cflags='-I/usr/local/include -I/usr/X11R6/include' --extra-libs='-L/usr/local/lib -L/usr/X11R6/lib' --extra-ldsoflags= --mandir=/usr/local/man --objcc=/usr/bin/false --optflags='-O2 -pipe -g -Wno-redundant-decls'
libavutil      60.  8.102 / 60.  8.102
libavcodec     62. 11.102 / 62. 11.102
libavformat    62.  3.102 / 62.  3.102
libavdevice    62.  1.102 / 62.  1.102
libavfilter    11.  4.102 / 11.  4.102
libswscale      9.  1.102 /  9.  1.102
libswresample   6.  1.102 /  6.  1.102

Exiting with exit code 0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice! A last check to be sure everything is fine on the system, it's to execute &lt;a href="https://man.openbsd.org/pkg_check.8" rel="noopener noreferrer"&gt;&lt;code&gt;pkg_check&lt;/code&gt;&lt;/a&gt;. It will verify if the packages have been correctly installed and if nothing wrong happened to them. If this issue happens again, it could be due to a misconfiguration, or a bug.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;doas pkg_check                                                                                                   
&lt;span class="go"&gt;Packing-list sanity: ok                                                                                                                                       
Direct dependencies: ok                                                                                                                                       
Reverse dependencies: ok                                                                                                                                      
Files from packages: ok
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything looks good at first glance. Anyway, I think I did something wrong during the last upgrade. That's great! It gives me more opportunities to write publication about &lt;a href="https://www.openbsd.org/" rel="noopener noreferrer"&gt;OpenBSD&lt;/a&gt;! Debugging and fixing problems on OpenBSD is a real pleasure, thanks to the documentation and all the simple Unix-like procedures used everywhere.&lt;/p&gt;

&lt;p&gt;Have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@chuttersnap?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;CHUTTERSNAP&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/cardboard-box-lot-fyaTq-fIlro?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sysadmin</category>
      <category>openbsd</category>
      <category>linker</category>
      <category>library</category>
    </item>
    <item>
      <title>Tests and Coverage in Dart</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Fri, 05 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/tests-and-coverage-in-dart-5ahm</link>
      <guid>https://dev.to/niamtokik/tests-and-coverage-in-dart-5ahm</guid>
      <description>&lt;p&gt;Writing tests is one of the most important and tedious task a developer has to do. A test is more than proving something is working in a project - even if it's main goal, it's also a way to see how an API is working, by offering implicit use cases and representing data using different format (mocking).&lt;/p&gt;

&lt;p&gt;It's usually a good practice to create the test first and then implement the solution. This is the foundation of both &lt;a href="https://en.wikipedia.org/wiki/Test-driven_development" rel="noopener noreferrer"&gt;Test-driven Development&lt;/a&gt; (TDD) and &lt;a href="https://en.wikipedia.org/wiki/Extreme_programming" rel="noopener noreferrer"&gt;Extreme Programming&lt;/a&gt; (XP) methodologies.&lt;/p&gt;

&lt;p&gt;In TDD, the test must be written first, and must fail. During the next iteration, the developer will invest time and effort to ensure the test is passing. In XP, this is practically the same, except this methodology has been created for high speed development (game production), and the tests are also used as source of trust and documentation.&lt;/p&gt;

&lt;p&gt;This post is not really about those methodologies, even if we can talk a bit about them, one will find more accurate resources on Wikipedia or elsewhere than reading this publication. The goal of this article is to summarize the different ways available to test an application in Dart. Dart only, not Flutter, this is a different beast, and a dedicated article will be created only for it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Bootstrapping
&lt;/h1&gt;

&lt;p&gt;As usual, a new dedicated project will be created, let call it... &lt;code&gt;rps&lt;/code&gt; for rock-paper-scissors. Let say it will be a library containing the rules for the &lt;a href="https://en.wikipedia.org/wiki/Rock_paper_scissors" rel="noopener noreferrer"&gt;Jan-ken-pon&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart create rps
&lt;span class="go"&gt;Creating rps using template console...

  .gitignore
  analysis_options.yaml
  CHANGELOG.md
  pubspec.yaml
  README.md
  bin/rps.dart
  lib/rps.dart
  test/rps_test.dart

Running pub get...                     0.3s
  Resolving dependencies...
  Downloading packages...
  Changed 48 dependencies!

Created project rps in rps! In order to get started, run the following commands:

  cd rps
  dart run
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If no template is being used to create this application, one will need to add the &lt;code&gt;test&lt;/code&gt; package as dependencies in &lt;code&gt;pubspec.yaml&lt;/code&gt; file, inside the &lt;code&gt;dev_dependencies&lt;/code&gt; section. See below for an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;dev_dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;lints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^6.0.0&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^1.25.6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://pub.dev/packages/test" rel="noopener noreferrer"&gt;&lt;code&gt;test&lt;/code&gt; package&lt;/a&gt; is the main one used to create test suites in Dart. It is maintained by the Dart team and available by default in the SDK.&lt;/p&gt;

&lt;h1&gt;
  
  
  Rock-Paper-Scissors Rules
&lt;/h1&gt;

&lt;p&gt;The Jan-ken-pon is a simple - and addictive - game. The goal is to win a duel with someone else using one of the 3 movements also called Shapes: Rock (the hand is fully closed), Paper (the hand is fully opened), and Scissors (the hand is half closed and the fingers are doing a scissors).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;Rock&lt;/code&gt; wins over &lt;code&gt;Scissors&lt;/code&gt; (then the Scissors is losing against &lt;code&gt;Rock&lt;/code&gt;);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;Scissors&lt;/code&gt; wins over &lt;code&gt;Paper&lt;/code&gt; (then the Paper is losing against &lt;code&gt;Scissors&lt;/code&gt;);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;Paper&lt;/code&gt; wins over &lt;code&gt;Rock&lt;/code&gt; (then the &lt;code&gt;Rock&lt;/code&gt; is losing against &lt;code&gt;Paper&lt;/code&gt;);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If both players are using the same movement, it's a draw and they must start over until one win.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A quick table to summarize the final results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;a vs b&lt;/th&gt;
&lt;th&gt;rock&lt;/th&gt;
&lt;th&gt;paper&lt;/th&gt;
&lt;th&gt;scissors&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;rock&lt;/td&gt;
&lt;td&gt;draw&lt;/td&gt;
&lt;td&gt;loss&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;win&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;paper&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;win&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;draw&lt;/td&gt;
&lt;td&gt;loss&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;scissors&lt;/td&gt;
&lt;td&gt;loss&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;win&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;draw&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Usually, this game is played anywhere and for any reasons (for example for the one who will eat the last chocolate desert in the restaurant). Let call the act to play with someone else a &lt;code&gt;Party&lt;/code&gt;. This post will also give me the opportunity to talk about design programming and OOP. Yeah. Marvelous. Finally.&lt;/p&gt;

&lt;h1&gt;
  
  
  Rock-Paper-Scissors Shapes
&lt;/h1&gt;

&lt;p&gt;To me, a good programming language should give you an idea of an implementation without a lot of effort. When using Object Oriented Programming paradigm, this is never the case. You don't have one or two obvious solutions, but a bunch of potentials valid solutions. Let try to implement our shapes here (rock, paper and scissors).&lt;/p&gt;

&lt;p&gt;Here 3 of the implementations I can think of when dealing with this project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;(medium complexity) Like any other OOP language, one will create a first abstract class called &lt;code&gt;Shape&lt;/code&gt;, and then will create 3 classes called &lt;code&gt;Rock&lt;/code&gt;, &lt;code&gt;Paper&lt;/code&gt; and &lt;code&gt;Scissors&lt;/code&gt; inheriting from the &lt;code&gt;Shape&lt;/code&gt; class;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;(low complexity) Because Dart is being used here, one can do something a bit different, using an enumerator as class, and extends it with few methods to compare the defined value between them;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;(high complexity) Create a more complex data-structure, based on a ring (linked list) where the first element is linked to the last one, or a directed graph, where the different path can define which shape greater than another one. Those solutions are way more complex, but can give more flexibility and extends the game in the future with new shapes (e.g. &lt;code&gt;Spoke&lt;/code&gt; and &lt;code&gt;Snake&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The second option seems to be the easiest one for this kind of publication, so, let implement it. Let begin with something simple, the representation of the shapes using an emoji (utf8/unicode). A &lt;code&gt;test/&lt;/code&gt; directory should already be present in the project, we can add a new test file called &lt;code&gt;shapes_test.dart&lt;/code&gt; there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// file test/shapes_test.dart&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:test/test.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:rps/shapes.dart'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;shapes&lt;/span&gt; &lt;span class="kd"&gt;show&lt;/span&gt; &lt;span class="n"&gt;versus&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:rps/shapes.dart'&lt;/span&gt; &lt;span class="kd"&gt;show&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;utf8Encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;utf8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;shapeRepresentation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Shapes Representation'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// see https://unicodeplus.com/U+270A&lt;/span&gt;
    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'rock as raised fist unicode'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;utf8Encoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;226&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;156&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;138&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// see https://unicodeplus.com/U+1FAF3&lt;/span&gt;
    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'paper as palm down hand unicode'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;utf8Encoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;paper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;159&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;171&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;179&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// see https://unicodeplus.com/U+270C&lt;/span&gt;
    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'scissors as victory hand unicode'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;utf8Encoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scissors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;226&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;156&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;140&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;shapeRepresentation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's an explanation of this code, a new test case is created with the help of the &lt;a href="https://pub.dev/documentation/test/latest/test/test.html" rel="noopener noreferrer"&gt;&lt;code&gt;test()&lt;/code&gt; function&lt;/a&gt;. The first argument is the description of the test as &lt;code&gt;String&lt;/code&gt;, and the next argument is an anonymous function containing the code to be executed.&lt;/p&gt;

&lt;p&gt;A test case can be added in a test group, in this situation, the &lt;a href="https://pub.dev/documentation/test/latest/test/group.html" rel="noopener noreferrer"&gt;&lt;code&gt;group()&lt;/code&gt;&amp;nbsp;function&lt;/a&gt; can be used. It follows the same principle than the &lt;code&gt;test()&lt;/code&gt; function, the first argument is the description of the group as &lt;code&gt;String&lt;/code&gt; and the second argument will contain the test cases to be executed.&lt;/p&gt;

&lt;p&gt;Then, each test cases is made of functions exposed by the &lt;code&gt;test&lt;/code&gt; module, in our case, the &lt;a href="https://pub.dev/documentation/test/latest/test/expect.html" rel="noopener noreferrer"&gt;&lt;code&gt;expect()&lt;/code&gt;&lt;/a&gt; function is used to assert a certain value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="go"&gt;00:00 +0 -1: loading test/shapes_test.dart [E]                                                          
  Failed to load "test/shapes_test.dart":
  test/shapes_test.dart:13:29: Error: Undefined name 'Shape'.
          utf8Encoder.convert(Shape.rock.toString()),
                              ^^^^^
  test/shapes_test.dart:21:29: Error: Undefined name 'Shape'.
          utf8Encoder.convert(Shape.paper.toString()),
                              ^^^^^
  test/shapes_test.dart:29:29: Error: Undefined name 'Shape'.
          utf8Encoder.convert(Shape.scissors.toString()),
                              ^^^^^
  test/shapes_test.dart:39:14: Error: Undefined name 'Shape'.
&lt;/span&gt;&lt;span class="gp"&gt;        expect(Shape.rock &amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Shape.scissors, equals&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;               ^^^^^
  test/shapes_test.dart:39:27: Error: Undefined name 'Shape'.
&lt;/span&gt;&lt;span class="gp"&gt;        expect(Shape.rock &amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Shape.scissors, equals&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="go"&gt;
00:00 +0 -1: Some tests failed.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tests are failing, this is normal, nothing has been created so far. Let fix that by creating a new file called &lt;code&gt;lib/shapes.dart&lt;/code&gt;, it will contain our first implementation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// file lib/shapes.dart&lt;/span&gt;

&lt;span class="kt"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;           
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;rock:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"✊"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;paper:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"🫳"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;scissors:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="s"&gt;"✌"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let re-invoke &lt;code&gt;dart test&lt;/code&gt; to see if our implementation is working correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;dart&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;
&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;All&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="n"&gt;passed&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works! Now we need something to compare Shapes between them; for doing that, let playing with the default operators offered by dart, mainly used to compare numbers. Why not using them to compare Shapes?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// file test/shapes_test.dart&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;shapeOperators&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Shapes Operators'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'rock operators'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rock&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rock&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rock&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'scissors operators'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scissors&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scissors&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scissors&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'paper operators'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;paper&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;paper&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;paper&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;shapeRepresentation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;shapeOperators&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As previously said, the test cases are grouped together. The code here describes how the Shapes will interact when using &lt;a href="https://api.dart.dev/dart-core/BigInt/operator_greater.html" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/a&gt; (greater than) and &lt;a href="https://api.dart.dev/dart-core/BigInt/operator_less.html" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;&lt;/a&gt; (less than) operators. The code is straightforward and explicit, but one could have done probably more tests with the help of the huge amount of function helpers offered by the &lt;code&gt;test&lt;/code&gt; package, here a short list of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/test/latest/test/isFalse-constant.html" rel="noopener noreferrer"&gt;&lt;code&gt;isFalse&lt;/code&gt;&lt;/a&gt; constant, ensure the final result is returning &lt;code&gt;false&lt;/code&gt; as boolean;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/test/latest/test/isTrue-constant.html" rel="noopener noreferrer"&gt;&lt;code&gt;isTrue&lt;/code&gt;&lt;/a&gt; constant, same than &lt;code&gt;isFalse&lt;/code&gt; except it ensure the returned value is &lt;code&gt;true&lt;/code&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/test/latest/test/isException-constant.html" rel="noopener noreferrer"&gt;&lt;code&gt;isException()&lt;/code&gt;&lt;/a&gt; contant can be used to be sure the function will throw an exception;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/test/latest/test/contains.html" rel="noopener noreferrer"&gt;&lt;code&gt;contains()&lt;/code&gt;&lt;/a&gt; function checks if the returned values contain a specific objects;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/test/latest/test/fail.html" rel="noopener noreferrer"&gt;&lt;code&gt;fail()&lt;/code&gt;&lt;/a&gt; function throws a failure;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/test/latest/test/isA.html" rel="noopener noreferrer"&gt;&lt;code&gt;isA()&lt;/code&gt;&lt;/a&gt; function checks the type of the data returned;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/test/latest/test/same.html" rel="noopener noreferrer"&gt;&lt;code&gt;same()&lt;/code&gt;&lt;/a&gt; function checks if 2 items are identical.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anyway, let implement these new requested features to compare &lt;code&gt;Shapes&lt;/code&gt; using our standard operators. To do that, we will create methods marked with the &lt;code&gt;operator&lt;/code&gt; identifier. From the specification:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Operators are instance methods with special names, except for operator &lt;code&gt;[]&lt;/code&gt; which is an instance getter and operator &lt;code&gt;[]=&lt;/code&gt; which is an instance setter. An operator declaration is identified using the built-in identifier &lt;code&gt;operator&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;-- &lt;a href="https://storage.googleapis.com/dart-specification/DartLangSpecDraft.pdf" rel="noopener noreferrer"&gt;Dart Programming Language Specification&lt;/a&gt;, Chapter 10, Section 2.1, page 37&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In our case, the methods &lt;code&gt;&amp;lt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt; will be created. They must return a boolean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// file lib/shapes.dart&lt;/span&gt;

&lt;span class="kt"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;           
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;rock:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"✊"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;paper:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"🫳"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;scissors:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="s"&gt;"✌"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="kd"&gt;operator&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="kd"&gt;operator&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the implementation is done, let runs the tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;dart&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;
&lt;span class="n"&gt;Building&lt;/span&gt; &lt;span class="n"&gt;package&lt;/span&gt; &lt;span class="n"&gt;executable&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt; 
&lt;span class="n"&gt;Built&lt;/span&gt; &lt;span class="nl"&gt;test:&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;All&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="n"&gt;passed&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, finally, we need an easy way to know if a shape versus another can be defined as a &lt;code&gt;Win&lt;/code&gt;, a &lt;code&gt;Loss&lt;/code&gt; or a &lt;code&gt;Draw&lt;/code&gt;. It can be implemented as an exposed function from the module &lt;code&gt;Shapes&lt;/code&gt; directly, we don't need something smart, just a function doing a comparison with two &lt;code&gt;Shape&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// file test/shapes_test.dart&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:rps/results.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;shapeVersus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shapes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;versus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Shapes Versus'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'rock'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;shapes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;versus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scissors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Win"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;shapes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;versus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Draw"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;shapes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;versus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;paper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Loss"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;New problem. How to represent a &lt;code&gt;Draw&lt;/code&gt;, a &lt;code&gt;Win&lt;/code&gt; and a &lt;code&gt;Loss&lt;/code&gt;? Well, all can be seen as &lt;code&gt;Result&lt;/code&gt;, so, let create at first an abstract class called &lt;code&gt;Result&lt;/code&gt;&amp;nbsp;first. Then, we can create the needed classes. This code will be stored in &lt;code&gt;lib/results.dart&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// file lib/results.dart&lt;/span&gt;

&lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Draw&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Draw"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Win&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Win"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Loss&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Loss"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, let work on the &lt;code&gt;versus()&lt;/code&gt; function, we can implement it in many ways, let implement the easiest one, using the operators previously created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// file lib/shapes.dart&lt;/span&gt;

&lt;span class="n"&gt;Result&lt;/span&gt; &lt;span class="nf"&gt;versus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Shape&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Shape&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Draw&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Win&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Loss&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our implementation is done, let check the tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="go"&gt;00:00 +7: All tests passed!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have a quick and dirty implementation of the different available Shapes in Jan Ken Pon, let have a look on the &lt;code&gt;test&lt;/code&gt; package and all the features it offers us.&lt;/p&gt;

&lt;h1&gt;
  
  
  Test Reports
&lt;/h1&gt;

&lt;p&gt;The &lt;code&gt;test&lt;/code&gt; package offers a way to create test reports using &lt;a href="https://pub.dev/packages/test#selecting-a-test-reporter" rel="noopener noreferrer"&gt;reporters&lt;/a&gt;. The default reporter used is the &lt;code&gt;compact&lt;/code&gt; one, only printing the sum of successful test or printing the errors. Another simple reporter is the &lt;code&gt;expanded&lt;/code&gt; one, it will display more information about the test suite.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--reporter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;expanded
&lt;span class="go"&gt;00:00 +0: loading test/rps_test.dart
00:00 +0: test/shapes_test.dart: Shapes Representation rock as raised fist unicode
00:00 +1: test/shapes_test.dart: Shapes Representation paper as palm down hand unicode
00:00 +2: test/shapes_test.dart: Shapes Representation scissors as victory hand unicode
00:00 +3: test/shapes_test.dart: Shapes Operators rock operators
00:00 +4: test/shapes_test.dart: Shapes Operators scissors operators
00:00 +5: test/shapes_test.dart: Shapes Operators paper operators
00:00 +6: test/shapes_test.dart: Shapes Versus rock
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your project is running on Github and you are using Github Actions, a specific reporter called &lt;code&gt;github&lt;/code&gt; can be used and will output a Github friendly report.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--reporter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;github
&lt;span class="go"&gt;
::group::✅ Passing tests
✅ test/shapes_test.dart: Shapes Representation rock as raised fist unicode
✅ test/shapes_test.dart: Shapes Representation paper as palm down hand unicode
✅ test/shapes_test.dart: Shapes Representation scissors as victory hand unicode
✅ test/shapes_test.dart: Shapes Operators rock operators
✅ test/shapes_test.dart: Shapes Operators scissors operators
✅ test/shapes_test.dart: Shapes Operators paper operators
✅ test/shapes_test.dart: Shapes Versus rock
::endgroup::

🎉 7 tests passed.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, if you are using your own CI/CD pipeline with Jenkins or &lt;a href="https://robotframework.org/" rel="noopener noreferrer"&gt;Robot Framework&lt;/a&gt;, the result of the test can be returned as JSON with the &lt;code&gt;json&lt;/code&gt; reporter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--reporter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;json
&lt;span class="go"&gt;{"protocolVersion":"0.1.1","runnerVersion":"1.31.1","pid":3420883,"type":"start","time":0}
{"suite":{"id":0,"platform":"vm","path":"test/rps_test.dart"},"type":"suite","time":0}
{"test":{"id":1,"name":"loading test/rps_test.dart","suiteID":0,"groupIDs":[],"metadata":{"skip":false,"skipReason":null},"line":null,"column":null,"url":null},"type":"testStart","time":0}
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;{"success":true,"type":"done","time":310}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Test Coverage
&lt;/h1&gt;

&lt;p&gt;Generating a &lt;a href="https://pub.dev/packages/test#collecting-code-coverage" rel="noopener noreferrer"&gt;coverage report&lt;/a&gt; via &lt;code&gt;dart test&lt;/code&gt; requires &lt;a href="https://github.com/linux-test-project/lcov" rel="noopener noreferrer"&gt;LCOV tool&lt;/a&gt; installed on your system. If you are using a Debian-like distribution, the LCOV package should be available.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;lcov
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To generate a new coverage report, simply use &lt;code&gt;--coverage-path&lt;/code&gt; arguments. It will create the directory destination if it does not exist and generate the report.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dart run &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--coverage-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./_coverage/lcov.info
&lt;span class="go"&gt;00:00 +7: All tests passed!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to have an HTML report, the &lt;a href="https://manpages.debian.org/trixie/lcov/genhtml.1.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;genhtml&lt;/code&gt;&lt;/a&gt; command usually present with the LCOV package is needed. It will generate an HTML report in the same directory the &lt;code&gt;lcov.info&lt;/code&gt; file has been created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;genhtml &lt;span class="nt"&gt;-o&lt;/span&gt; _coverage/report ./_coverage/lcov.info 
&lt;span class="go"&gt;Reading tracefile ./_coverage/lcov.info.
Found 3 entries.
Found common filename prefix "/dart/rps"
Generating output.
Processing file lib/rps.dart
  lines=2 hit=0
Processing file lib/results.dart
  lines=3 hit=3
Processing file lib/shapes.dart
  lines=28 hit=18
Overall coverage rate:
  source files: 3
  lines.......: 63.6% (21 of 33 lines)
  functions...: no data found
Message summary:
  no messages were reported
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the final result. See below the 3 levels of coverage from the top layer (a global view of the application) and the 2 other layers. The third one is the coverage from the module directly, indicating the numbers of time a line is called.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjj85sgrqpqqi72wiow30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjj85sgrqpqqi72wiow30.png" alt="Top Level Coverage Report" width="800" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frnaum6dp1k5xr4h8839h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frnaum6dp1k5xr4h8839h.png" alt="Lib Coverage Report" width="800" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgd1r8zcc4upd0dc75ji3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgd1r8zcc4upd0dc75ji3.png" alt="Shapes Module Coverage Report" width="800" height="906"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Testing an application with Dart is an easy task, the tools offered by default are doing what any developer is looking for, unit testing, test coverage and reports. It was a really quick introduction to this complex topic, we still have a lot of things to learn, like mocking techniques, test integration, testing frontend, backend or flutter and much more.&lt;/p&gt;

&lt;p&gt;The skeleton of this project can be found in &lt;a href="https://github.com/niamtokik/rps_dart" rel="noopener noreferrer"&gt;niamtokik/rps_dart&lt;/a&gt; repository on Github. If you are still there and want to know more, here a list of interesting resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dart.dev/tools/testing" rel="noopener noreferrer"&gt;Dart testing guide&lt;/a&gt; where you will find a quick introduction to testing and more resources;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dart.dev/learn/tutorial/testing" rel="noopener noreferrer"&gt;Dart testing tutorial&lt;/a&gt;  where you will find a complete test project example in Dart;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/packages/test" rel="noopener noreferrer"&gt;&lt;code&gt;test&lt;/code&gt; package&lt;/a&gt; on pub.dev;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pub.dev/documentation/test/latest/" rel="noopener noreferrer"&gt;&lt;code&gt;test&lt;/code&gt;&amp;nbsp;package API documentation&lt;/a&gt; where you will find the full documentation of the &lt;code&gt;test&lt;/code&gt; package;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/dart-lang/test/tree/master/pkgs/test" rel="noopener noreferrer"&gt;&lt;code&gt;test&lt;/code&gt; package source code&lt;/a&gt; on Github, where you will find the implementation of the &lt;code&gt;test&lt;/code&gt; package;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@lord.oussama.2020/test-driven-development-tdd-in-flutter-a-practical-guide-with-runnable-examples-ec844761a03c" rel="noopener noreferrer"&gt;Test-Driven Development (TDD) in Flutter: a Practical Guide with Runnable Examples&lt;/a&gt; by &lt;a href="https://medium.com/@lord.oussama.2020" rel="noopener noreferrer"&gt;Oussama DX&lt;/a&gt; on Medium, where you will learn how to test Flutter;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/Yczar/test-driven-dart/blob/master/6_Special_Topics/6.1_TDD_in_Dart.md" rel="noopener noreferrer"&gt;Test-Driven Development (TDD) in Dart&lt;br&gt;
&lt;/a&gt; by &lt;a href="https://github.com/Yczar/" rel="noopener noreferrer"&gt;Ayatomide&lt;/a&gt; on Github, where you will an example of TDD in Dart;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://nooralibutt.com/unit-testing-tdd-in-dart-469aee03aa4b" rel="noopener noreferrer"&gt;Unit Testing (TDD) in Dart: A Beginners Guide Part I&lt;/a&gt; by &lt;a href="https://nooralibutt.com/" rel="noopener noreferrer"&gt;Noor Ali&lt;/a&gt; on Medium where you will learn how to use TDD in Dart;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://clouddevs.com/dart/test-driven-development/" rel="noopener noreferrer"&gt;Test-Driven Development with Dart: Ensuring Code Quality&lt;/a&gt; by &lt;a href="https://clouddevs.com/" rel="noopener noreferrer"&gt;CloudDevs&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@tomcrewceramics?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Tom Crew&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/white-garlic-on-gray-table-yKmBMyxndsI?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dart</category>
      <category>testing</category>
      <category>unit</category>
      <category>coverage</category>
    </item>
    <item>
      <title>Alternative Laptop Manufacturers</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Thu, 04 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/alternative-laptop-manufacturers-2dm0</link>
      <guid>https://dev.to/niamtokik/alternative-laptop-manufacturers-2dm0</guid>
      <description>&lt;p&gt;IBM/Lenovo was my first choice for the last 20 years when it comes to computers and laptops in particular. Unfortunately, the quality of these systems is slowly decreasing over time. Lenovo is doing nasty things with their recent models. Like Apple and other big manufacturers, plenty of their laptops are now sold with soldered RAM, hidden battery and many other displeasing “features”. Well, this is a bit sad, those laptops were great. Is it possible to find a professional alternative to them? A list of requirements before starting digging:&lt;/p&gt;

&lt;p&gt;Multi-core with more than 32GB of RAM. Currently involved in Erlang development on projects consuming a huge amount of RAM and CPU, this is a must have. A system with at least 8 cores and 32GB RAM is the bare minimum in my case. An amd64 architecture is probably better, but any other should do the job as well. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Open-Source Friendly&lt;/strong&gt;. This is one of the first important point, all my laptops must be compatible with at least OpenBSD and recent Linux distributions. It was fun to debug stuff 10 years ago, but my time is now too valuable to lose it by doing hardware/kernel hacking stuff.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easy to repair&lt;/strong&gt;. Fixing an hardware issue on Lenovo laptop was easy. Even too easy. In less than 5 minutes you can replace the RAM, remove a disk, change a fan… My first IBM/Lenovo laptop (a T42p) is still working today, and only few screws need to be removed to replace the most important port of the system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Documented&lt;/strong&gt;. It is a big plus to have a documentation on the hardware and a summary of all references in one place like in a PDF file. Lenovo offers this kind of solution and it saved me many time when looking for spares.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Battle Tested&lt;/strong&gt;. More users means less hacking time. Using obscure laptops from odd manufacturers lead to long hacking session, trying to debug every part of something only one guy lost in China knows. Lenovo is still being used by most engineers and hackers around the world, a good manufacturers should also have this kind of “community”.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hardened&lt;/strong&gt;. This is another important point, but not vital. Most of the Lenovo in my collections were quite hard to break. They have not been approved with military grade, but they are hardened enough to survive most of the common issues of life.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These 5 points will generate a score, my own score, to choice one manufacturers between all of them. By the way, why this article? Well, I’m looking for a new laptop, and I was not able to find a decent list of open-source friendly devices on the web. Let’s get started!&lt;/p&gt;

&lt;h1&gt;
  
  
  Entroware
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Entroware specialises in providing Ubuntu Linux based computing solutions and services for our clients' requirements. We have been manufacturing Ubuntu powered Desktops, Laptops, and Servers since early 2014, using only high quality components, both hardware and software.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.entroware.com" rel="noopener noreferrer"&gt;Entroware&lt;/a&gt; is a company specialized in Linux/Ubuntu based computing and located in United Kingdom. They are creating laptops, desktops, workstations and servers. They give access to &lt;a href="https://docs.entroware.com/" rel="noopener noreferrer"&gt;guides and tutorials&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can follow them on &lt;a href="https://twitter.com/entroware" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt; or &lt;a href="https://www.facebook.com/entroware" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Framework
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;We’re here to remake consumer electronics to respect people and the planet. Unlike most products, ours are open for you to repair, customize, upgrade, and own at the deepest level.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://frame.work" rel="noopener noreferrer"&gt;Framework Computer&lt;/a&gt; is located in USA, offering an highly customizable and repairable laptop made to last. They are all 100% compatible with Ubuntu, Fedora, Ubuntu and Windows. An &lt;a href="https://community.frame.work/" rel="noopener noreferrer"&gt;official forum&lt;/a&gt; and  &lt;a href="https://guides.frame.work/c/Framework_Laptop" rel="noopener noreferrer"&gt;installation/repair guides&lt;/a&gt;  are available. Documentation about this laptop can also be found on &lt;a href="https://wiki.archlinux.org/title/Framework_Laptop_13" rel="noopener noreferrer"&gt;ArchLinux&lt;/a&gt; and &lt;a href="https://wiki.gentoo.org/wiki/Framework_Laptop_13" rel="noopener noreferrer"&gt;Gentoo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can follow them on &lt;a href="https://www.twitter.com/FrameworkPuter" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;, &lt;a href="https://www.youtube.com/@FrameworkComputer" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;, &lt;a href="https://www.instagram.com/FrameworkComputer" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://www.facebook.com/FrameworkComputer" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/frameworkcomputer" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt; and &lt;a href="https://github.com/FrameworkComputer" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  GPD Technology
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;GPD Gamepad Company Limited focus on developing high quality of Windows mini Laptop, UMPC and Android handheld game consoles since 2013. With our efforts and the trust from our customers, we have established a deep and long partnership with many overseas distributors all around the world. We proudly share 80% of the target market.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.gpd.hk/" rel="noopener noreferrer"&gt;GPD Technology&lt;/a&gt; is a Chinese company, located in Hong Kong. The GPD Pocket 2 was my working tool for 4 months during my MakerTour. It was not really a good experience, the support was not really good and the keyboard was not designed for long coding session or writing articles. In other hand, it was quite powerful for its size and was after used to host local pipeline runners for test. The company itself is not really pro-open-source, but the hardware used is compatible with most of the modern Linux distributed available (it will require a bit of hacking sometimes though).&lt;/p&gt;

&lt;p&gt;You can follow them on &lt;a href="https://www.youtube.com/@gpdgameconsole" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;, &lt;a href="https://twitter.com/softwincn" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;, &lt;a href="https://www.facebook.com/4GPDWin/" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt; and &lt;a href="https://www.tiktok.com/@gpdgameconsole" rel="noopener noreferrer"&gt;TikTok&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Juno Computers
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Juno Computers is a Linux computer company based in London, UK. Our goal is to help integrate Linux and non-Linux users to one friendly system. Offering an alternative to Windows and Apple. All of our machines come pre-installed with Ubuntu.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://junocomputers.com/us/" rel="noopener noreferrer"&gt;Juno Computers LTD&lt;/a&gt; is located in United Kingdom. Their devices to be highly compatible with open-source OS The &lt;a href="https://junocomputers.com/support/" rel="noopener noreferrer"&gt;official support page&lt;/a&gt; is quite small, giving only access to some commands to install drivers on Ubuntu/Debian distributions. They are active on Twitter and Facebook.&lt;/p&gt;

&lt;p&gt;You can follow them on &lt;a href="https://twitter.com/junocomputers" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;, &lt;a href="https://instagram.com/juno_computers" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/juno-computers" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt; and &lt;a href="https://facebook.com/junocomputersltd" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Malibal
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;MALIBAL is a forward-thinking technology company that produces high-performance, custom laptops, mobile workstations, and mobile servers. Our commitment is to user autonomy, data privacy, and the right to repair. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.malibal.com/" rel="noopener noreferrer"&gt;Malibal&lt;/a&gt; company is located in USA. They are manufacturing powerful open-source friendly laptops, also compatible with Windows.&lt;/p&gt;

&lt;p&gt;You can follow them on &lt;a href="https://x.com/MALIBAL" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Minifree
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Minifree Ltd (based in the United Kingdom) sells secure, high quality computer systems with Free, Libre, Open Source Software (FOSS) pre-installed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://minifree.org/" rel="noopener noreferrer"&gt;Minifree&lt;/a&gt; is a company located in United Kingdom. They are reusing old IBM/Lenovo laptop and secures them by installing &lt;a href="https://libreboot.org/" rel="noopener noreferrer"&gt;LibreBoot&lt;/a&gt; (open-source BIOS). All laptops are installed with an encrypted version of &lt;a href="https://www.debian.org/" rel="noopener noreferrer"&gt;Debian&lt;/a&gt; or  &lt;a href="https://www.qubes-os.org/" rel="noopener noreferrer"&gt;QubesOS&lt;/a&gt;. Most of the Lenovo laptops are highly compatible with open-source operating systems. If, like me, you like Lenovo, but you don't want to mess with your hardware, just take a moment to check their offers.&lt;/p&gt;

&lt;h1&gt;
  
  
  Mnt Reform
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;MNT Research is driven by the idea of a digital future that is open source, collaborative, and modular. Our users make the existence of MNT possible and we are dedicated to give back to them by creating open source hardware that is customizable, repairable, and modifiable. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;a href="https://mntre.com/" rel="noopener noreferrer"&gt;Mnt Reform&lt;/a&gt; is a crazy laptop project. The goal is to product an highly productive environment &lt;a href="https://mntre.com/about.html" rel="noopener noreferrer"&gt;100% compatible for open-source software&lt;/a&gt;. A lot of  &lt;a href="https://mntre.com/docs-all.html" rel="noopener noreferrer"&gt;documentation and specifications&lt;/a&gt; can also be found. Their company is located in Germany and if you like retro-computing as well (like Amiga), you should really check their website and shop.&lt;/p&gt;

&lt;h1&gt;
  
  
  Nitrokey
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Nitrokey is the world-leading company in open source security hardware. Nitrokey develops IT security hardware for data encryption, key management and user authentication, as well as secure network devices, PCs, laptops and smartphones.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.nitrokey.com/" rel="noopener noreferrer"&gt;Nitrokey&lt;/a&gt; is located in Germany. They are specialized in open-source security hardware and they are selling open-source compatible laptops, smartphones, tablets and desktops computers. &lt;a href="https://docs.nitrokey.com/" rel="noopener noreferrer"&gt;Documentations&lt;/a&gt; about their products are quite complete&lt;/p&gt;

&lt;p&gt;You can follow them on &lt;a href="https://twitter.com/nitrokey" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;, &lt;a href="https://social.nitrokey.com/@nitrokey" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;, &lt;a href="https://github.com/nitrokey" rel="noopener noreferrer"&gt;Github&lt;/a&gt;, &lt;a href="https://www.youtube.com/nitrokey" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/nitrokey" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;, &lt;a href="https://www.instagram.com/nitrokey/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; and &lt;a href="https://www.facebook.com/nitrokey" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  NovaCustom
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Own. Your. Tech.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://novacustom.com/" rel="noopener noreferrer"&gt;NovaCustom&lt;/a&gt; is located in Netherlands. They are manufacturing and selling highly compatible open-source hardware including laptops, desktop and smartphones. Some of their laptops are &lt;a href="https://www.qubes-os.org/news/2025/05/20/qubes-certified-novacustom-v54-v56-now-available-with-heads/" rel="noopener noreferrer"&gt;QubeOS certified&lt;/a&gt;. In case of issues, a &lt;a href="https://novacustom.com/forum/" rel="noopener noreferrer"&gt;public forum&lt;/a&gt; is available.&lt;/p&gt;

&lt;p&gt;You can follow them on &lt;a href="https://mastodon.online/@novacustom/" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;, &lt;a href="https://www.reddit.com/r/NovaCustom/" rel="noopener noreferrer"&gt;Reddit&lt;/a&gt;,  &lt;a href="https://www.linkedin.com/company/novacustom/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt; and &lt;a href="https://www.youtube.com/channel/UC5nyBvxISx7NqzWhqIFBk6g" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Olimex
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Olimex Ltd is a leading provider for IoT solutions, small Linux computers, development tools and programmers for embedded market.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.olimex.com" rel="noopener noreferrer"&gt;Olimex&lt;/a&gt; is located in Bulgeria. They are mainly reselling electronics stuff, but have also DIY laptops available: the &lt;a href="https://www.olimex.com/Products/DIY-Laptop/KITS/TERES-A64-WHITE/open-source-hardware" rel="noopener noreferrer"&gt;Teres A64&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can follow them on &lt;a href="http://twitter.com/Olimex" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;, &lt;a href="https://github.com/OLIMEX" rel="noopener noreferrer"&gt;Github&lt;/a&gt;, &lt;a href="http://www.youtube.com/tsvetanusunov" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;, &lt;a href="http://www.facebook.com/pages/Olimex/284513994899126" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;, &lt;a href="https://www.instagram.com/olimexbulgaria/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; and &lt;a href="https://discord.gg/QQEBXZk7y8" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Pyra
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;The DragonBox Pyra being the successor to the OpenPandora, means countless&lt;br&gt;
improvements upon what was already a unique device. Sporting a feature-set not&lt;br&gt;
found in any other mobile device.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://pyra-handheld.com" rel="noopener noreferrer"&gt;Pyra&lt;/a&gt; is more like an open-source DIY projects than a reseller. The design seems a bit old-school. It looks like a cheap alternative to GPD laptops, but it is 100% open-source and libre.&lt;/p&gt;

&lt;h1&gt;
  
  
  Pinebook
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Our goal is to push the envelope and deliver ARM and RISC-V devices that you want to use and develop for. To this end, we actively work with the development community and champion end-user initiatives. Rather than applying business to a FOSS setting, we allow FOSS principles to guide our business.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://pine64.org/" rel="noopener noreferrer"&gt;Pine64&lt;/a&gt; team is offering laptops, smartphones and tablets based on ARM64 CPU. All their devices are &lt;a href="https://pine64.org/documentation/" rel="noopener noreferrer"&gt;highly documented&lt;/a&gt; and 100% compatible with open-source software.&lt;/p&gt;

&lt;p&gt;You can follow them on &lt;a href="https://twitter.com/thepine64" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt; and &lt;a href="https://social.treehouse.systems/@pine64" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Purism
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;We believe people should have technology that fully respects their freedoms. Being a social purpose company means doing social good for society before maximizing profits, and that makes us quite a different company indeed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://puri.sm/" rel="noopener noreferrer"&gt;Purism&lt;/a&gt; is located in USA. They are selling open-source friendly smartphones, laptops, tablets and desktops. They also have created their own Linux distribution based on Debian called &lt;a href="https://pureos.net/" rel="noopener noreferrer"&gt;PureOS&lt;/a&gt; (see &lt;a href="https://distrowatch.com/table.php?distribution=pureos" rel="noopener noreferrer"&gt;distrowatch&lt;/a&gt;).&lt;/p&gt;

&lt;h1&gt;
  
  
  Skikk
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;SKIKK does not believe in one-size-fits-all. We build laptops and mini PCs exactly the way you need them — for school, work, gaming or creativity. Customization at its best.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.skikk.eu/" rel="noopener noreferrer"&gt;Skikk&lt;/a&gt; is located in Netherlands. They are selling open-source compatible laptops and desktops.&lt;/p&gt;

&lt;p&gt;You can follow them on &lt;a href="https://x.com/skikkeu" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;, &lt;a href="https://www.instagram.com/skikk.eu" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://www.youtube.com/@skikk" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;, &lt;a href="https://www.facebook.com/skikkhq/" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt; and &lt;a href="https://www.linkedin.com/company/skikk/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Slimbook
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Update it. Repair it. Enjoy it longer.&lt;br&gt;
At Slimbook, we design our devices so you can upgrade, maintain, and repair them with ease. Because we believe in hardware that adapts to you and stays with you for years.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://slimbook.com/en/" rel="noopener noreferrer"&gt;Slimbook&lt;/a&gt; is located in Spain. They are selling highly open-source compatible and repairable laptops, desktops and mini-PC. They are also offering their own Linux distribution based on Ubuntu and called &lt;a href="https://slimbook.com/en/slimbook-os" rel="noopener noreferrer"&gt;SlimBookOS&lt;/a&gt; (see  &lt;a href="https://distrowatch.com/table.php?distribution=slimbookos" rel="noopener noreferrer"&gt;distrowatch&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;You can follow them on &lt;a href="https://x.com/slimbook?mx=2" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;, &lt;a href="https://www.facebook.com/slimbook.es" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/slimbook" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;, &lt;a href="https://www.youtube.com/@slimbook-laptops" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;, &lt;a href="https://www.instagram.com/slimbook/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; and &lt;a href="https://github.com/Slimbook-Team" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Starlabs
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Linux hardware, built to be owned. Privacy-conscious laptops, open firmware options, and repairable hardware designed for people who want control without turning every setup into a project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://starlabs.systems/" rel="noopener noreferrer"&gt;Starlabs&lt;/a&gt; is an UK company. They are selling open-source friendly laptops and mini-PCs.&lt;/p&gt;

&lt;p&gt;You can follow them on &lt;a href="https://twitter.com/starlabsltd" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;, &lt;a href="https://www.facebook.com/starlabssystems/" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;,  &lt;a href="https://www.instagram.com/starlabssystems" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://www.youtube.com/@starlabssystems" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt; and &lt;a href="https://github.com/StarLabsLtd" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  System76
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;System76 proudly engineers and manufactures premium Linux computers and keyboards at our factory in Denver, Colorado. Our user-driven products, alongside Pop!_OS, give creators, makers, and builders the means to bring forth the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://system76.com" rel="noopener noreferrer"&gt;System76&lt;/a&gt; company is located in USA. They are selling open-source friendly hardware from laptops and desktops to servers and components. They are also in charge to maintain their own Linux distribution called &lt;a href="https://system76.com/pop" rel="noopener noreferrer"&gt;Pop_OS&lt;/a&gt; based on Debian (see &lt;a href="https://distrowatch.com/table.php?distribution=popos" rel="noopener noreferrer"&gt;distrowatch&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;You can follow them on &lt;a href="https://x.com/system76/" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/system76" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;, &lt;a href="https://github.com/system76" rel="noopener noreferrer"&gt;Github&lt;/a&gt;, &lt;a href="https://youtube.com/system76/" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;, and &lt;a href="https://instagram.com/system76_com" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Technoethical
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Technoethical is an online shop that sells hardware compatible with operating systems that fully respect users' freedom as defined by the GNU Project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://tehnoetic.com/" rel="noopener noreferrer"&gt;Technoethical&lt;/a&gt; is located in Romania. They are selling mostly open-source compatible hardware and devices including laptops.&lt;/p&gt;

&lt;h1&gt;
  
  
  Think Penguin
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;ThinkPenguin, Inc. was founded in 2008 to improve support for GNU/Linux and other free software operating systems. The company's main objective has been to bring together the products, services, documentation, and support needed for users to confidently utilize free software operating systems.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.thinkpenguin.com/" rel="noopener noreferrer"&gt;ThinkPenguin&lt;/a&gt; is based in USA. They are selling a lot of open-source oriented hardware and devices, including desktop/servers and laptops.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tuxedo Computers
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;At TUXEDO Computers you don't only get individually configurable Linux No‍te‍books und PCs, but also hardware that is perfectly optimized for running Linux!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.tuxedocomputers.com" rel="noopener noreferrer"&gt;Tuxedo Computers&lt;/a&gt; is based in Germany. They are selling high-end and high quality laptops and desktops. All their devices are open-source compatible. They created also their own Linux distribution called &lt;a href="https://www.tuxedocomputers.com/en/TUXEDO-OS_1.tuxedo" rel="noopener noreferrer"&gt;TuxedoOS&lt;/a&gt; based on Debian (see &lt;a href="https://distrowatch.com/table.php?distribution=tuxedo" rel="noopener noreferrer"&gt;distrowatch&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;You can follow them on &lt;a href="https://www.reddit.com/r/tuxedocomputers/" rel="noopener noreferrer"&gt;Reddit&lt;/a&gt;, &lt;a href="https://www.facebook.com/tuxedocomputers/" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/tuxedo-computers-gmbh/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;, and &lt;a href="https://linuxrocks.online/@tuxedocomputers" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Vikings
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;vikings specialises in fast and effective solutions for *Linux and *BSD environments, and their operation, ranging from day-to-day upkeep to solving complex challenges.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://vikings.net/" rel="noopener noreferrer"&gt;Vikings&lt;/a&gt; is located in Germany. This is not really a manufacturer, but they are recycling Lenovo laptops and configure them for more privacy. The list of laptop available is small, but if one is looking for cheap, robust and open source friendly devices, it can be a great choice.&lt;/p&gt;

&lt;h1&gt;
  
  
  Other Smaller projects/companies
&lt;/h1&gt;

&lt;p&gt;Lot of open-source projects are offering their own laptop designs. In fact, more than 10 years ago, it was probably the only way to have a true open-source friendly laptop. The big problem is coming from their configuration (old) and all the hacks required to make them usable.&lt;/p&gt;

&lt;h2&gt;
  
  
  GenBook RK3588
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.crowdsupply.com/shenzhen-tianmao-technology/genbook-rk3588" rel="noopener noreferrer"&gt;GenBook RK3588&lt;/a&gt; made by the &lt;a href="https://www.cool-pi.com/" rel="noopener noreferrer"&gt;CoolPi&lt;/a&gt; is an affordable open-source laptop based on an ARM CPU with 32GB of RAM. The crowd-funding was stopped in December 2024. The &lt;a href="https://www.cool-pi.com/product/genbook/" rel="noopener noreferrer"&gt;full specification of this laptop&lt;/a&gt; is available on their website. The beast is available on &lt;a href="https://fr.aliexpress.com/item/1005007356248607.html" rel="noopener noreferrer"&gt;AliExpress&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Novena
&lt;/h2&gt;

&lt;p&gt;Remember &lt;a href="https://en.wikipedia.org/wiki/Andrew_Huang_(hacker)" rel="noopener noreferrer"&gt;Andrew Bunnie Huang&lt;/a&gt;? Yeah, the author of the amazing book &lt;a href="https://nostarch.com/hardwarehackerpaperback" rel="noopener noreferrer"&gt;"The Hardware Hacker: Adventures in Making and Breaking Harware"&lt;/a&gt; created an open-source laptop called &lt;a href="https://spectrum.ieee.org/novena-a-laptop-with-no-secrets" rel="noopener noreferrer"&gt;Novena&lt;/a&gt;. I'm not sure if this project is still active, but it was a really great one.&lt;/p&gt;

&lt;h2&gt;
  
  
  CrowPi2
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.crowpi.cc/" rel="noopener noreferrer"&gt;Crowpi&lt;/a&gt; manufacturer offers solutions for Arduino-like platform. One of their project called &lt;a href="https://www.crowpi.cc/collections/crowpi/products/crowpi2-all-in-one-raspberry-pi-laptop-stem-learning-platform?variant=39547335966853" rel="noopener noreferrer"&gt;CrowPi2&lt;/a&gt; was created to offer a fully hackable laptop based on RasberryPi. If you are interested by electronics, embedded systems or to learn how a system works, it's probably a good choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anyon_e
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/byrantech" rel="noopener noreferrer"&gt;Anyon_e project&lt;/a&gt; has been create by Byran Huang. The goal is to create an high end open-source friendly laptop. Everything about the project and the source code can be seen on the &lt;a href="https://github.com/byrantech/laptop" rel="noopener noreferrer"&gt;official repository&lt;/a&gt; on Github. I discovered this project recently and I don't know it is still active but this is a great initiative.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loongson
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.loongson.cn/EN" rel="noopener noreferrer"&gt;Loongson&lt;/a&gt; is a Chinese company already created few laptops in the past with their own MIPS-like CPU. I got one more than 10 years ago to play with it, it was usable, but the system was pretty slow and the support not so good, but most of the hardware - if not all - was open-source. This hardware is still supported by many project like &lt;a href="https://lwn.net/ml/debian-devel-announce/c92784a9ff7f69d4d136936fb615d513e98163ee.camel%40physik.fu-berlin.de/" rel="noopener noreferrer"&gt;Debian&lt;/a&gt;, &lt;a href="https://ftp.openbsd.org/pub/OpenBSD/7.9/loongson/INSTALL.loongson" rel="noopener noreferrer"&gt;OpenBSD&lt;/a&gt; or &lt;a href="https://wiki.netbsd.org/ports/evbmips/loongson/" rel="noopener noreferrer"&gt;NetBSD&lt;/a&gt;. In 2025, they announced working on another laptop, so, maybe a new open-source friendly laptop will be available. The &lt;a href="https://loongson.github.io/LoongArch-Documentation/Loongson-3A5000-usermanual-EN.pdf" rel="noopener noreferrer"&gt;Loongson CPU/architecture documentation&lt;/a&gt; can easily be found online.&lt;/p&gt;

&lt;p&gt;Note: for the one thinking CCP will spy on their movement, you should probably think about what the CIA and NSA did during past years on their own citizens and allies. A lot of hardware is already corrupted and backdoored, this is not a news, it has been reveled by Assange and Snowden more than 10 years ago.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Even in 2026, it's still hard to find a decent laptop in this ecosystem. Lot of choice but their is a lake of resources and objective reviews. I will stay on my good old Lenovo laptops, they are working pretty well with all Linux distributions and OpenBSD, that's all I need. Yes, they are a bit expensive, but in fact, open-source friendly hardware are not cheap either. Anyway, at least, you can have an exhaustive list of the alternative available today!&lt;/p&gt;

&lt;p&gt;Have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@loganvoss?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Logan Voss&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/abstract-pattern-of-heat-sinks-dKBpYgSVbRk?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>hardware</category>
      <category>laptop</category>
      <category>opensource</category>
      <category>manufacturer</category>
    </item>
    <item>
      <title>Standalone HTTP Server in Elixir with Bandit</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Wed, 03 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/standalone-http-server-in-elixir-with-bandit-lne</link>
      <guid>https://dev.to/niamtokik/standalone-http-server-in-elixir-with-bandit-lne</guid>
      <description>&lt;p&gt;Writing &lt;a href="https://elixir-lang.org/" rel="noopener noreferrer"&gt;Elixir&lt;/a&gt; code is not really exciting to me, but, to be honest, if someone today wants to create an application from scratch and is looking for a big pool developers and a battle tested distributed infrastructure (the &lt;a href="https://www.erlang-solutions.com/blog/the-beam-erlangs-virtual-machine/" rel="noopener noreferrer"&gt;BEAM VM&lt;/a&gt;), Elixir is probably one of the best choice nowadays. The community is active, the documentation is great, the language looks like a mix between Ruby and Python, without the annoying object part and with all the good feature from &lt;a href="https://erlang.org/" rel="noopener noreferrer"&gt;Erlang&lt;/a&gt;. This is why, today, we will use &lt;a href="https://elixir-lang.org/" rel="noopener noreferrer"&gt;Elixir&lt;/a&gt; with &lt;a href="https://hex.pm/packages/bandit" rel="noopener noreferrer"&gt;&lt;code&gt;bandit&lt;/code&gt;&lt;/a&gt; to create a minimal web server supporting both HTTP and WebSockets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mtrudel/bandit" rel="noopener noreferrer"&gt;&lt;code&gt;bandit&lt;/code&gt;&lt;/a&gt; has been created as an alternative to &lt;a href="https://github.com/ninenines/cowboy" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy&lt;/code&gt;&lt;/a&gt;, fully coded in Elixir and designed to be integrated and highly compatible with the &lt;a href="https://www.phoenixframework.org/" rel="noopener noreferrer"&gt;Phoenix framework&lt;/a&gt;. When it came out few years ago, the community did a lot of noise and I never find a moment to test it. &lt;a href="https://github.com/mtrudel/bandit" rel="noopener noreferrer"&gt;&lt;code&gt;bandit&lt;/code&gt;&lt;/a&gt; &amp;nbsp;supports &lt;a href="https://www.rfc-editor.org/info/rfc2616" rel="noopener noreferrer"&gt;HTTP/1.1&lt;/a&gt; and &lt;a href="https://www.rfc-editor.org/info/rfc7540/" rel="noopener noreferrer"&gt;HTTP/2&lt;/a&gt; natively. &lt;a href="https://www.rfc-editor.org/rfc/rfc6455.html" rel="noopener noreferrer"&gt;WebSockets&lt;/a&gt; protocols support can be added with the help of &lt;a href="https://github.com/phoenixframework/websock" rel="noopener noreferrer"&gt;&lt;code&gt;WebSock&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/phoenixframework/websock_adapter" rel="noopener noreferrer"&gt;&lt;code&gt;WebSockAdapter&lt;/code&gt;&lt;/a&gt; modules. At this time, it does not support &lt;a href="https://datatracker.ietf.org/doc/html/rfc9114" rel="noopener noreferrer"&gt;HTTP/3&lt;/a&gt; (yet?).&lt;/p&gt;

&lt;p&gt;The idea here is not to follow blindly the &lt;a href="https://phoenix.hexdocs.pm/overview.html" rel="noopener noreferrer"&gt;Phoenix framework documentation&lt;/a&gt; and to create yet another website-like-project, but just to learn how to use &lt;a href="https://github.com/mtrudel/bandit" rel="noopener noreferrer"&gt;&lt;code&gt;bandit&lt;/code&gt;&lt;/a&gt; API, a bit like I did in a &lt;a href="https://dev.to/niamtokik/http-server-in-erlang-with-cowboy-28g0"&gt;previous publication on &lt;code&gt;cowboy&lt;/code&gt;&lt;/a&gt;. Let start our journey by creating a new &lt;a href="https://elixir.hexdocs.pm/main/library-guidelines.html" rel="noopener noreferrer"&gt;Elixir library&lt;/a&gt; called &lt;a href="https://github.com/niamtokik/desperados" rel="noopener noreferrer"&gt;&lt;code&gt;desperados&lt;/code&gt;&lt;/a&gt;. It will contain all our experiments.&lt;/p&gt;

&lt;h1&gt;
  
  
  Bootstrapping
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://elixir-lang.org/" rel="noopener noreferrer"&gt;Elixir&lt;/a&gt; is using &lt;a href="https://hexdocs.pm/elixir/introduction-to-mix.html" rel="noopener noreferrer"&gt;Mix&lt;/a&gt; as project manager. To create a new library-like project, one can simply call &lt;a href="https://hexdocs.pm/mix/Mix.Tasks.New.html" rel="noopener noreferrer"&gt;&lt;code&gt;mix new&lt;/code&gt;&lt;/a&gt; command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mix new desperados
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A library template project should have been generated into the &lt;code&gt;desperados&lt;/code&gt; directory. I will not create an application or an umbrella for now, I just want something minimalist to break. It also means the application will be started manually, usually with a &lt;code&gt;start&lt;/code&gt; like function.&lt;/p&gt;

&lt;h1&gt;
  
  
  Starting Bandit
&lt;/h1&gt;

&lt;p&gt;The first file to modify is &lt;code&gt;lib/desperados.ex&lt;/code&gt;, it will contain the methods to start and stop the application. Let simply implement &lt;code&gt;start_link/1&lt;/code&gt; and &lt;code&gt;start_link/0&lt;/code&gt; for now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start_link&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:plug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plugs&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:scheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:http&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8082&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:any&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:startup_log&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:debug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Bandit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;bandit&lt;/code&gt; is using &lt;a href="https://github.com/mtrudel/thousand_island" rel="noopener noreferrer"&gt;&lt;code&gt;thousand_island&lt;/code&gt;&lt;/a&gt; to manage the transport layer (TCP). This project is also a rewrite of &lt;a href="https://github.com/ninenines/ranch" rel="noopener noreferrer"&gt;&lt;code&gt;ranch&lt;/code&gt;&lt;/a&gt; in pure Erlang and designed for the Phoenix framework. The function to start a new &lt;code&gt;bandit&lt;/code&gt; server is &lt;a href="https://hexdocs.pm/bandit/Bandit.html#start_link/1" rel="noopener noreferrer"&gt;&lt;code&gt;Bandit.start_link/1&lt;/code&gt;&lt;/a&gt;. The unique argument passed to it contains the &lt;a href="https://hexdocs.pm/bandit/Bandit.html#t:options/0" rel="noopener noreferrer"&gt;&lt;code&gt;options()&lt;/code&gt;&lt;/a&gt; to configure the server.&lt;/p&gt;

&lt;p&gt;The smallest configuration is to only set a &lt;a href="https://hexdocs.pm/plug/1.19.1/Plug.html" rel="noopener noreferrer"&gt;&lt;code&gt;Plug&lt;/code&gt;&lt;/a&gt; handler.  This Plug callback is mandatory. In fact, when using &lt;code&gt;bandit&lt;/code&gt; with Phoenix, this callback can be seen as the &lt;a href="https://hexdocs.pm/phoenix/Phoenix.Router.html" rel="noopener noreferrer"&gt;Phoenix router&lt;/a&gt;. Anyway, to work correctly, a &lt;code&gt;Plug&lt;/code&gt; handler is required, so let call it &lt;code&gt;Desperados.Plug&lt;/code&gt; for now. The rest of the options are for configuring the server itself&lt;/p&gt;

&lt;h1&gt;
  
  
  Anatomy of a &lt;code&gt;Plug&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;A &lt;a href="https://hexdocs.pm/plug/1.19.1/Plug.html" rel="noopener noreferrer"&gt;&lt;code&gt;Plug&lt;/code&gt;&lt;/a&gt;  is a piece of code containing mandatory callback functions (&lt;a href="https://elixir.hexdocs.pm/1.4.5/behaviours.html" rel="noopener noreferrer"&gt;behaviours&lt;/a&gt;) to manage initialization, connections, requests and events. These callbacks functions are following a contract (specification) and must return a certain kind of value. If you are more familiar with Erlang, one can think of a &lt;a href="https://ninenines.eu/docs/en/cowboy/2.15/guide/middlewares/" rel="noopener noreferrer"&gt;&lt;code&gt;middleware&lt;/code&gt;&lt;/a&gt; in &lt;code&gt;cowboy&lt;/code&gt;. The &lt;code&gt;Plug&lt;/code&gt; concept is the foundation of any Phoenix application.&lt;/p&gt;

&lt;p&gt;Why &lt;code&gt;Plug&lt;/code&gt;s are great? Well, because they are following a contract, those modules can be connected together using a pipeline. The output of one &lt;code&gt;Plug&lt;/code&gt; can be the input of another one. This kind of concept makes everything easier. You can see that a bit like a layer of features, where every &lt;code&gt;Plug&lt;/code&gt; will deal with only with one request passing through one &lt;code&gt;Plug&lt;/code&gt; at a time (e.g. authentication, sanitization...) and the "final" value returned by one of those &lt;code&gt;Plug&lt;/code&gt; will be the response to the client.&lt;/p&gt;

&lt;p&gt;Let create the most simple &lt;code&gt;Plug&lt;/code&gt; possible, &lt;code&gt;Desperados.Plugs.Inspect&lt;/code&gt;, a simple &lt;code&gt;Plug&lt;/code&gt; to inspect the &lt;a href="https://hexdocs.pm/plug/1.19.1/Plug.Conn.html#t:t/0" rel="noopener noreferrer"&gt;&lt;code&gt;%Plug.Conn{}&lt;/code&gt;&lt;/a&gt; data structure passed in the first argument of the &lt;code&gt;call/2&lt;/code&gt; function callback. This file will be created under &lt;code&gt;lib/desperados/plugs&lt;/code&gt; directory and called &lt;code&gt;inspect.ex&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plugs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Inspect&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@behaviour&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{}}&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;Plug&lt;/code&gt; is a concise module, when a request is coming to the server from a client, the first callback executed is &lt;a href="https://hexdocs.pm/plug/1.19.1/Plug.html#c:init/1" rel="noopener noreferrer"&gt;&lt;code&gt;init/1&lt;/code&gt;&lt;/a&gt;, it will simply initialize the the &lt;code&gt;Plug&lt;/code&gt; using some kind of arguments passed previously. In this step, a &lt;a href="https://plug.hexdocs.pm/1.19.2/Plug.html#t:opts/0" rel="noopener noreferrer"&gt;state&lt;/a&gt; can be defined, in our case, an empty &lt;a href="https://hexdocs.pm/elixir/1.12/Map.html" rel="noopener noreferrer"&gt;&lt;code&gt;Map&lt;/code&gt;&lt;/a&gt; is returned. This state will be then be passed to other callback functions.&lt;/p&gt;

&lt;p&gt;If the initialization phase was a success, then the server will call the &lt;a href="https://hexdocs.pm/plug/1.19.1/Plug.html#c:call/2" rel="noopener noreferrer"&gt;&lt;code&gt;call/2&lt;/code&gt;&lt;/a&gt; function callback with the full request in the first argument using a &lt;a href="https://hexdocs.pm/plug/1.19.1/Plug.Conn.html" rel="noopener noreferrer"&gt;&lt;code&gt;%Plug.Conn{}&lt;/code&gt;&lt;/a&gt; data structure and the state previously returned by the &lt;code&gt;init/1&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;This new plug can be directly used in the &lt;code&gt;Bandit.start_link/1&lt;/code&gt; options but well, I told you previously we can pipeline &lt;code&gt;Plugs&lt;/code&gt; together. This feature is already available by default in Phoenix when using the &lt;a href="https://hexdocs.pm/phoenix/plug.html#router-plugs" rel="noopener noreferrer"&gt;&lt;code&gt;Router plugs&lt;/code&gt;&lt;/a&gt; feature. We are not using Phoenix, but, we can use &lt;a href="https://hexdocs.pm/plug/1.19.1/Plug.Builder.html" rel="noopener noreferrer"&gt;&lt;code&gt;Plug.Builder&lt;/code&gt;&lt;/a&gt; to create our own Plug router using &lt;a href="https://hexdocs.pm/plug/1.19.1/Plug.Builder.html" rel="noopener noreferrer"&gt;&lt;code&gt;Plug.Builder&lt;/code&gt;&lt;/a&gt;. Let create a new modules called &lt;code&gt;Desperados.Plugs&lt;/code&gt; in &lt;code&gt;lib/desperados/plugs.ex&lt;/code&gt;. It will contain the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plugs&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Builder&lt;/span&gt;
  &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plugs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Inspect&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Plug.Builder&lt;/code&gt; module offers the &lt;code&gt;plug&lt;/code&gt; macro. After compilation, all the plugs defined are then put together in a pipeline, where the first defined will be executed at first and the last one, executed during the last step. Let start our minimal configuration right now using &lt;code&gt;iex&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;iex &lt;span class="nt"&gt;-S&lt;/span&gt; mix
&lt;span class="go"&gt;Erlang/OTP 28 [erts-16.0.1] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit:ns]

Interactive Elixir (1.19.5) - press Ctrl+C to exit (type h() ENTER for help)
&lt;/span&gt;&lt;span class="gp"&gt;iex(1)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Desperados.start_link
&lt;span class="go"&gt;
13:31:27.131 [debug] Running Desperados.Plugs with Bandit 1.11.1 at 0.0.0.0:8082 (http)
&lt;/span&gt;&lt;span class="gp"&gt;{:ok, #&lt;/span&gt;PID&amp;lt;0.214.0&amp;gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It seems good, the server is started. We can now use &lt;code&gt;curl&lt;/code&gt; to check that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-svfq&lt;/span&gt; http://localhost:8082/test&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="go"&gt;* Host localhost:8082 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* HTTPS-RR: -
*   Trying [::1]:8082...
* connect to ::1 port 8082 from ::1 port 38724 failed: Connection refused
*   Trying 127.0.0.1:8082...
* Established connection to localhost (127.0.0.1 port 8082) from 127.0.0.1 port 42484 
* using HTTP/1.x
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;GET /test HTTP/1.1
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Host: localhost:8082
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;User-Agent: curl/8.20.0
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Accept: &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="go"&gt;* Request completely sent off
&amp;lt; HTTP/1.1 500 Internal Server Error
&amp;lt; connection: close
* The requested URL returned error: 500
&amp;lt; 
&lt;/span&gt;&lt;span class="gp"&gt;* closing connection #&lt;/span&gt;0
&lt;span class="go"&gt;22
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ah! It returns an error an error 500! Let check the console on &lt;code&gt;iex&lt;/code&gt; to see what happened...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;%Plug.Conn{
  adapter: {Bandit.Adapter, :...},
  assigns: %{},
  body_params: %Plug.Conn.Unfetched{aspect: :body_params},
  cookies: %Plug.Conn.Unfetched{aspect: :cookies},
  halted: false,
  host: "localhost",
  method: "GET",
  owner: nil,
  params: %Plug.Conn.Unfetched{aspect: :params},
  path_info: ["test"],
  path_params: %{},
  port: 8082,
  private: %{},
  query_params: %Plug.Conn.Unfetched{aspect: :query_params},
  query_string: "",
  remote_ip: {127, 0, 0, 1},
  req_cookies: %Plug.Conn.Unfetched{aspect: :cookies},
  req_headers: [
    {"host", "localhost:8082"},
    {"user-agent", "curl/8.20.0"},
    {"accept", "*/*"}
  ],
  request_path: "/test",
  resp_body: nil,
  resp_cookies: %{},
  resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"}],
  scheme: :http,
  script_name: [],
  secret_key_base: nil,
  state: :unset,
  status: nil
}

13:32:18.749 [error] ** (Plug.Conn.NotSentError) a response was neither set nor sent from the connection
    (bandit 1.11.1) lib/bandit/pipeline.ex:169: Bandit.Pipeline.commit_response!/1
    (bandit 1.11.1) lib/bandit/pipeline.ex:46: Bandit.Pipeline.run/5
    (bandit 1.11.1) lib/bandit/http1/handler.ex:13: Bandit.HTTP1.Handler.handle_data/3
    (bandit 1.11.1) lib/bandit/delegating_handler.ex:18: Bandit.DelegatingHandler.handle_data/3
    (bandit 1.11.1) lib/bandit/delegating_handler.ex:8: Bandit.DelegatingHandler.handle_continue/2
    (stdlib 7.0.1) gen_server.erl:2424: :gen_server.try_handle_continue/3
    (stdlib 7.0.1) gen_server.erl:2291: :gen_server.loop/4
    (stdlib 7.0.1) proc_lib.erl:333: :proc_lib.init_p_do_apply/3

&lt;/span&gt;&lt;span class="gp"&gt;iex(2)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It "works", at least, the &lt;code&gt;conn&lt;/code&gt; argument is printed to stdout but the application print also an error. In fact, this is normal, the request does not return a response to the client and then, &lt;code&gt;bandit&lt;/code&gt; throw an &lt;a href="https://plug.hexdocs.pm/1.19.2/Plug.Conn.NotSentError.html" rel="noopener noreferrer"&gt;&lt;code&gt;Plug.Conn.NotSentError&lt;/code&gt;&lt;/a&gt; error. To avoid this kind of issue, a &lt;code&gt;Plug&lt;/code&gt; must give a response to a client. Let fix that in the next section.&lt;/p&gt;

&lt;p&gt;Quick note regarding a &lt;code&gt;Plug&lt;/code&gt;, it's also possible to create one using a function, in this case, the first argument passed must be a &lt;a href="https://hexdocs.pm/plug/1.19.1/Plug.Conn.html" rel="noopener noreferrer"&gt;&lt;code&gt;%Plug.Conn{}&lt;/code&gt;&lt;/a&gt; and the second argument can be anything (options).&lt;/p&gt;

&lt;p&gt;Anyway, if you want to know a bit more about a plug, check those links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The official &lt;a href="https://hexdocs.pm/plug/1.19.1/Plug.html" rel="noopener noreferrer"&gt;&lt;code&gt;Plug&lt;/code&gt; documentation API&lt;/a&gt; where you will find great example and the description of the behavior;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The official phoenix &lt;a href="https://hexdocs.pm/phoenix/plug.html" rel="noopener noreferrer"&gt;&lt;code&gt;Plug&lt;/code&gt; tutorial&lt;/a&gt;, wwhere you will find how plugs are working when using them in a Phoenix project;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/elixir-plug/plug/blob/v1.19.2/lib/plug.ex#L1" rel="noopener noreferrer"&gt;&lt;code&gt;Plug&lt;/code&gt;&lt;/a&gt; source code on Github, where you will understand how the plug's macros have been implemented;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/elixir-plug/plug/blob/v1.19.1/lib/plug/router.ex#L1" rel="noopener noreferrer"&gt;&lt;code&gt;Plug.Router&lt;/code&gt;&lt;/a&gt; source code on Github, where you will understand how the router has been implemented using mostly Elixir macros;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/elixir-plug/plug/blob/v1.19.1/lib/plug/builder.ex" rel="noopener noreferrer"&gt;&lt;code&gt;Plug.Builder&lt;/code&gt;&lt;/a&gt; source code on Github where you will learn how the Plug Builder has been created and how the pipeline is working under the hood.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Dealing with HTTP Requests
&lt;/h1&gt;

&lt;p&gt;We can print the connections/requests received with &lt;code&gt;Desperados.Plugs.Inspect&lt;/code&gt; &lt;code&gt;Plug&lt;/code&gt;, but it's a bit useless. It returns an error to the client and except for debugging purpose, there are no real values. Let create a new module called &lt;code&gt;Desperados.Endpoints.HTTP&lt;/code&gt; to deal with the HTTP requests. A new file in &lt;code&gt;lib/desperados/endpoints/http.ex&lt;/code&gt; can be created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Endpoints&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@behaviour&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Conn&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_opts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{}}&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;send_resp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"not found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Plug structure is the very same than the previous one we just created. We have the &lt;code&gt;init/1&lt;/code&gt; function callback doing mostly nothing and then we have the &lt;code&gt;call/2&lt;/code&gt; function doing something interesting. When a request is received, this plug will call &lt;a href="https://plug.hexdocs.pm/1.19.2/Plug.Conn.html#send_resp/3" rel="noopener noreferrer"&gt;&lt;code&gt;Plug.Conn.send_resp/3&lt;/code&gt;&lt;/a&gt; function to reply a message to the client. In our case, this code is simply returning the code &lt;code&gt;404&lt;/code&gt; with the message &lt;code&gt;"not found"&lt;/code&gt;. Let add this in our &lt;code&gt;Desperados.Plugs&lt;/code&gt; "pipeline".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plugs&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Builder&lt;/span&gt;
  &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plugs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Inspect&lt;/span&gt;
  &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Endpoints&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project can now be recompiled in live using &lt;code&gt;recompile&lt;/code&gt; function inside the &lt;code&gt;iex&lt;/code&gt; shell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;iex(2)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;recompile
&lt;span class="go"&gt;:ok
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great. &lt;code&gt;curl&lt;/code&gt; can now be used to request the server with a &lt;code&gt;GET&lt;/code&gt; method. If it works, it should return a 404 not found.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://localhost:8082/test
&lt;span class="go"&gt;* Host localhost:8082 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* HTTPS-RR: -
*   Trying [::1]:8082...
* connect to ::1 port 8082 from ::1 port 34846 failed: Connection refused
*   Trying 127.0.0.1:8082...
* Established connection to localhost (127.0.0.1 port 8082) from 127.0.0.1 port 35288 
* using HTTP/1.x
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;GET /test HTTP/1.1
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Host: localhost:8082
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;User-Agent: curl/8.20.0
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Accept: &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="go"&gt;* Request completely sent off
&amp;lt; HTTP/1.1 404 Not Found
&amp;lt; date: Sat, 30 May 2026 11:57:36 GMT
&amp;lt; content-length: 9
&amp;lt; vary: accept-encoding
&amp;lt; cache-control: max-age=0, private, must-revalidate
&amp;lt; 
&lt;/span&gt;&lt;span class="gp"&gt;* Connection #&lt;/span&gt;0 to host localhost:8082 left intact
&lt;span class="go"&gt;not found
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No more errors, &lt;code&gt;Desperados.Endpoints.HTTP&lt;/code&gt; is returning an "awesome" 404 not found.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;%Plug.Conn{
  adapter: {Bandit.Adapter, :...},
  assigns: %{},
  body_params: %Plug.Conn.Unfetched{aspect: :body_params},
  cookies: %Plug.Conn.Unfetched{aspect: :cookies},
  halted: false,
  host: "localhost",
  method: "GET",
  owner: nil,
  params: %Plug.Conn.Unfetched{aspect: :params},
  path_info: ["test"],
  path_params: %{},
  port: 8082,
  private: %{},
  query_params: %Plug.Conn.Unfetched{aspect: :query_params},
  query_string: "",
  remote_ip: {127, 0, 0, 1},
  req_cookies: %Plug.Conn.Unfetched{aspect: :cookies},
  req_headers: [
    {"host", "localhost:8082"},
    {"user-agent", "curl/8.20.0"},
    {"accept", "*/*"}
  ],
  request_path: "/test",
  resp_body: nil,
  resp_cookies: %{},
  resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"}],
  scheme: :http,
  script_name: [],
  secret_key_base: nil,
  state: :unset,
  status: nil
}
&lt;/span&gt;&lt;span class="gp"&gt;iex(3)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No crashes on the server side anymore as well. The &lt;code&gt;conn&lt;/code&gt; variable is still correctly printed to STDOUT and everything is working correctly. Is it? Well, perhaps not, what about WebSocketd support?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; ws://localhost:8082/test
&lt;span class="go"&gt;* Host localhost:8082 was resolved.                                                                     * IPv6: ::1
* IPv4: 127.0.0.1
* HTTPS-RR: -
*   Trying [::1]:8082...
* connect to ::1 port 8082 from ::1 port 52100 failed: Connection refused
*   Trying 127.0.0.1:8082...
* Established connection to localhost (127.0.0.1 port 8082) from 127.0.0.1 port 43686 
* using HTTP/1.x
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;GET /test HTTP/1.1
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Host: localhost:8082
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;User-Agent: curl/8.20.0
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Accept: &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Upgrade: websocket
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Sec-WebSocket-Version: 13
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Sec-WebSocket-Key: &lt;span class="nv"&gt;mkqOjlvEfpDPU53U0uVnHw&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Connection: Upgrade
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="go"&gt;* Request completely sent off
&amp;lt; HTTP/1.1 404 Not Found
&amp;lt; date: Sat, 30 May 2026 11:59:43 GMT
&amp;lt; content-length: 9
&amp;lt; vary: accept-encoding
&amp;lt; cache-control: max-age=0, private, must-revalidate
* Refused WebSocket upgrade: 404
&amp;lt; 
&lt;/span&gt;&lt;span class="gp"&gt;* closing connection #&lt;/span&gt;0
&lt;span class="go"&gt;curl: (22) Refused WebSocket upgrade: 404
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ah! It does not work... Let fix that in the next section then. Anyway, if you want to know more about managing HTTP requests with &lt;code&gt;Plug&lt;/code&gt;s, you should also check those links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://plug.hexdocs.pm/1.19.2/Plug.Conn.html" rel="noopener noreferrer"&gt;&lt;code&gt;Plug.Conn&lt;/code&gt; module documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/elixir-plug/plug/blob/v1.19.2/lib/plug/conn.ex#L3" rel="noopener noreferrer"&gt;&lt;code&gt;Plug.Conn&lt;/code&gt; source code&lt;/a&gt; on Github&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Dealing with WebSockets
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;bandit&lt;/code&gt; does not offer WebSockets natively and depends of 2 modules for doing that: &lt;code&gt;WebSock&lt;/code&gt; and &lt;code&gt;WebSockAdapter&lt;/code&gt;. The first one is used to deal with the events by "extending" the &lt;code&gt;Plug&lt;/code&gt; behavior, the second one is used to initialize the WebSocket link with the client. The creation of a small &lt;code&gt;Plug&lt;/code&gt; in charge of WebSockets connection still remains easy to implement. Let create a new endpoint called &lt;code&gt;Desperados.Endpoints.Websocket&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Endpoints&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Websocket&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@behaviour&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;count:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;WebSockAdapter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upgrade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{},&lt;/span&gt; &lt;span class="ss"&gt;timeout:&lt;/span&gt; &lt;span class="mi"&gt;60_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;halt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_opts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"ping&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;opcode:&lt;/span&gt; &lt;span class="n"&gt;opcode&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:reply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;opcode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"pong&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="n"&gt;state&lt;/span&gt; 
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;opcode:&lt;/span&gt; &lt;span class="n"&gt;opcode&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:reply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;opcode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;Plug&lt;/code&gt; assumes the client is starting to upgrade the HTTP connection to a WebSocket one. During the &lt;a href="https://plug.hexdocs.pm/1.19.2/Plug.html#c:call/2" rel="noopener noreferrer"&gt;&lt;code&gt;call/2&lt;/code&gt;&lt;/a&gt; callback, the &lt;a href="https://websock-adapter.hexdocs.pm/WebSockAdapter.html#upgrade/4" rel="noopener noreferrer"&gt;&lt;code&gt;WebSockAdapter.upgrade/4&lt;/code&gt;&lt;/a&gt; function is called to acknowledge the upgrade. The first argument is the connection, the second one is the module callback (in our case the name of the current module), the third argument is a state and fourth arguments are websocket connection options. If the handshake is valid, this function will give the control to this connection to a &lt;code&gt;WebSock&lt;/code&gt; process.&lt;/p&gt;

&lt;p&gt;In this situation, every time an events (e.g. messages) are received, they are transmitted to the &lt;a href="https://websock.hexdocs.pm/WebSock.html#c:handle_in/2" rel="noopener noreferrer"&gt;&lt;code&gt;handle_in/2&lt;/code&gt;&lt;/a&gt; function callback. The Plug router can be modified,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plugs&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Builder&lt;/span&gt;
  &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plugs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Inspect&lt;/span&gt;
  &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Endpoints&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Websocket&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Desperados.Endpoints.HTTP&lt;/code&gt; has been removed there, because the pipeline cannot support both HTTP and WebSockets at the same time (for now). Again, let recompile the project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;iex (3)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;recompile
&lt;span class="go"&gt;:ok
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;curl&lt;/code&gt;&amp;nbsp;can now be used again, this time as a WebSocket client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-vT&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--no-progress-meter&lt;/span&gt; ws://localhost:8082/test
&lt;span class="go"&gt;* Host localhost:8082 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* HTTPS-RR: -
*   Trying [::1]:8082...
* connect to ::1 port 8082 from ::1 port 36962 failed: Connection refused
*   Trying 127.0.0.1:8082...
* Established connection to localhost (127.0.0.1 port 8082) from 127.0.0.1 port 56496 
* using HTTP/1.x
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;GET /test HTTP/1.1
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Host: localhost:8082
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;User-Agent: curl/8.20.0
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Accept: &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Upgrade: websocket
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Sec-WebSocket-Version: 13
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Sec-WebSocket-Key: &lt;span class="nv"&gt;GIPpTnAnww9DUOoZGJSiYg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Connection: Upgrade
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="go"&gt;* Request completely sent off
&amp;lt; HTTP/1.1 101 Switching Protocols
&amp;lt; date: Sat, 30 May 2026 18:12:50 GMT
&amp;lt; upgrade: websocket
&amp;lt; connection: Upgrade
&amp;lt; sec-websocket-accept: aMzngyTPS2svr8fgKowmhG9vb44=
&amp;lt; cache-control: max-age=0, private, must-revalidate
&amp;lt; 
* Received 101, Switching to WebSocket
* [WS] Received 101, switch to WebSocket
test
} [11 bytes data]
test
test
test
* upload completely sent off: 22 bytes
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works as expected, the WebSocket server is returning the messages we are sending. Now, I would like to also implement something neat, a plug to detect if the client wants to upgrade the connection and assign a &lt;code&gt;:websocket&lt;/code&gt; tag to it if it's the case. In short: one path with HTTP and WebSocket support. Let create that in the next section.&lt;/p&gt;

&lt;p&gt;If you want to know more about WebSockets in Elixir, here few links to read:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://websock-adapter.hexdocs.pm/WebSockAdapter.html" rel="noopener noreferrer"&gt;&lt;code&gt;WebSockAdapter&lt;/code&gt; API documentation&lt;/a&gt;, where you will learn how to upgrade an HTTP connection to a &lt;code&gt;WebSocket&lt;/code&gt; one, including all available parameters;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://websock.hexdocs.pm/WebSock.html" rel="noopener noreferrer"&gt;&lt;code&gt;WebSock&lt;/code&gt; API documentation&lt;/a&gt;, where you will learn how to create a WebSocket callback module using the &lt;code&gt;WebSock&lt;/code&gt; behavior;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/phoenixframework/websock_adapter/blob/main/lib/websock_adapter.ex#L1" rel="noopener noreferrer"&gt;&lt;code&gt;WebSockAdapter&lt;/code&gt; module source code&lt;/a&gt; where all the transition between HTTP to WebSocket happens;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/phoenixframework/websock/blob/main/lib/websock.ex#L1" rel="noopener noreferrer"&gt;&lt;code&gt;WebSock&lt;/code&gt; module source code&lt;/a&gt; where the &lt;code&gt;WebSock&lt;/code&gt; behavior is implemented;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;a href="https://plug.hexdocs.pm/1.19.2/readme.html#hello-world-websockets" rel="noopener noreferrer"&gt;Hello world websockets&lt;/a&gt; Plug example for Phoenix framework application&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  HTTP/WebSockets Multi Protocol Endpoint
&lt;/h1&gt;

&lt;p&gt;Usually, one path only is set with WebSocket support. On many examples, one can see &lt;code&gt;/ws&lt;/code&gt; endpoint is dedicated for this task but what if one is able to bypass this convention and let a path be compatible with both HTTP and WebSockets? Yeah, it's a bit dirty, but it's also a good way to learn how WebSockets is working.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://datatracker.ietf.org/doc/html/rfc6455" rel="noopener noreferrer"&gt;WebSockets protocol&lt;/a&gt; is starting with the client requesting a connection upgrade containing at least 3 mandatory HTTP headers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc6455#section-11.2" rel="noopener noreferrer"&gt;&lt;code&gt;Upgrade&lt;/code&gt;&lt;/a&gt; set with the string &lt;code&gt;websocket&lt;/code&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc6455#section-11.3.5" rel="noopener noreferrer"&gt;&lt;code&gt;Sec-WebSocket-Version&lt;/code&gt;&lt;/a&gt; set with the version (or the list of versions) of the WebSocket protocol to use;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc6455#section-11.3.1" rel="noopener noreferrer"&gt;&lt;code&gt;Sec-WebSocket-key&lt;/code&gt;&lt;/a&gt; set with a random string (usually a base64 one) and mostly used to ensure this is a correct WebSocket connection requested by the client.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this information, a &lt;code&gt;Plug&lt;/code&gt; can be created to check if those headers are present in the request. If it's the case, we can be pretty sure this is a WebSocket request from a client, else, this is probably a simple HTTP request. Let create a new &lt;code&gt;Plug&lt;/code&gt; called &lt;code&gt;Desperados.Plugs.Websocket&lt;/code&gt; to help us identify those kind of connections.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plugs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Websocket&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@behaviour&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Conn&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{}}&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;header_upgrade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;header_upgrade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;get_req_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"upgrade"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"websocket"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ws_info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;header_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;header_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Conn&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;assigns:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;ws_info:&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;get_req_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sec-websocket-version"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ws_info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;header_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;header_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;header_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Conn&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;assigns:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;ws_info:&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;get_req_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sec-websocket-key"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ws_info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;final&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;header_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;final&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Conn&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;assigns:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;ws_info:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]}},&lt;/span&gt; &lt;span class="n"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:websocket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;final&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not a really complex &lt;code&gt;Plug&lt;/code&gt;, every functions are acting like a waterfall, the first one is looking for the &lt;code&gt;Upgrade&lt;/code&gt; header, if it's present, then it will call the second function. This one will look for the &lt;code&gt;Sec-Websocket-Version&lt;/code&gt; header, if it's present, then it will call the third function to check if the &lt;code&gt;Sec-Websocket-Key&lt;/code&gt; can be found. If those 3 headers are present, the &lt;code&gt;:websocket&lt;/code&gt; key set to &lt;code&gt;true&lt;/code&gt; is assigned to the connection.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Desperados.Endpoints.Websocket&lt;/code&gt; needs a small patch now, to only start the upgrading process when a connection has the &lt;code&gt;:websocket&lt;/code&gt; key assigned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Endpoints&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Websocket&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@behaviour&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;count:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Conn&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;assigns:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;websocket:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt; &lt;span class="n"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;WebSockAdapter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upgrade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{},&lt;/span&gt; &lt;span class="ss"&gt;timeout:&lt;/span&gt; &lt;span class="mi"&gt;60_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;halt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_opts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"ping&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;opcode:&lt;/span&gt; &lt;span class="n"&gt;opcode&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:reply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;opcode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"pong&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="n"&gt;state&lt;/span&gt; 
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;opcode:&lt;/span&gt; &lt;span class="n"&gt;opcode&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:reply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;opcode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The magic in the &lt;code&gt;call/2&lt;/code&gt; callback function, with the pattern &lt;code&gt;%Plug.Conn{assigns: %{websocket: true}}&lt;/code&gt;. If it matches, then the connection will be upgraded, else, it will simply returns the raw connection to the pipeline.&lt;/p&gt;

&lt;h1&gt;
  
  
  Plug Pipeline
&lt;/h1&gt;

&lt;p&gt;This is mostly done, one of the last section of this post. We have previously created a &lt;code&gt;Plug&lt;/code&gt; to print the connection data-structure to stdout, then, a &lt;code&gt;Plug&lt;/code&gt; to deal with HTTP requests and another one to deal with WebSockets. Another one was created to alter the connection data-structure and identify if a client is trying to initiate a WebSocket connection. Let update our pipeline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plugs&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Builder&lt;/span&gt;

  &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Plugs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Websocket&lt;/span&gt;
  &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Endpoints&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Websocket&lt;/span&gt;
  &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="no"&gt;Desperados&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Endpoints&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lean, isn't it? The first &lt;code&gt;Plug&lt;/code&gt; will check the client request to see if it's a WebSocket upgrade, if it's the case, the second &lt;code&gt;Plug&lt;/code&gt; will deal with it, else, the HTTP &lt;code&gt;Plug&lt;/code&gt; will do the job. &lt;code&gt;Desperados.Plugs.Inspect&lt;/code&gt; as be removed, it's currently not necessary to have it, but it can be added whenever needed.&lt;/p&gt;

&lt;h1&gt;
  
  
  Final Test
&lt;/h1&gt;

&lt;p&gt;This is the final part of this post, and it's now the time to test all the features. A test suite would have been the best thing to do, but this publication is already too long. Let just restart the application from scratch and play with &lt;code&gt;curl&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;iex &lt;span class="nt"&gt;-S&lt;/span&gt; mix
&lt;span class="go"&gt;Erlang/OTP 28 [erts-16.0.1] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit:ns]

Interactive Elixir (1.19.5) - press Ctrl+C to exit (type h() ENTER for help)
&lt;/span&gt;&lt;span class="gp"&gt;iex(1)&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Desperados.start_link&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="go"&gt;
20:11:42.808 [debug] Running Desperados.Plugs with Bandit 1.11.1 at 0.0.0.0:8082 (http)
&lt;/span&gt;&lt;span class="gp"&gt;{:ok, #&lt;/span&gt;PID&amp;lt;0.245.0&amp;gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let run the &lt;code&gt;curl&lt;/code&gt; WebSocket test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-vT&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--no-progress-meter&lt;/span&gt; ws://localhost:8082/test
&lt;span class="go"&gt;* Host localhost:8082 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* HTTPS-RR: -
*   Trying [::1]:8082...
* connect to ::1 port 8082 from ::1 port 43916 failed: Connection refused
*   Trying 127.0.0.1:8082...
* Established connection to localhost (127.0.0.1 port 8082) from 127.0.0.1 port 39416 
* using HTTP/1.x
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;GET /test HTTP/1.1
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Host: localhost:8082
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;User-Agent: curl/8.20.0
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Accept: &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Upgrade: websocket
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Sec-WebSocket-Version: 13
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Sec-WebSocket-Key: 7S1xZKpgqoGgC+gI3dtKyQ&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Connection: Upgrade
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="go"&gt;* Request completely sent off
&amp;lt; HTTP/1.1 101 Switching Protocols
&amp;lt; date: Sat, 30 May 2026 18:42:15 GMT
&amp;lt; upgrade: websocket
&amp;lt; connection: Upgrade
&amp;lt; sec-websocket-accept: jLWCKodpG0BsuWJT39MhgZTNLq8=
&amp;lt; cache-control: max-age=0, private, must-revalidate
&amp;lt; 
* Received 101, Switching to WebSocket
* [WS] Received 101, switch to WebSocket
test
} [11 bytes data]
test
test
test
* upload completely sent off: 22 bytes
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let run the &lt;code&gt;curl&lt;/code&gt; HTTP test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://localhost:8082/test
&lt;span class="go"&gt;* Host localhost:8082 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* HTTPS-RR: -
*   Trying [::1]:8082...
* connect to ::1 port 8082 from ::1 port 40422 failed: Connection refused
*   Trying 127.0.0.1:8082...
* Established connection to localhost (127.0.0.1 port 8082) from 127.0.0.1 port 34666 
* using HTTP/1.x
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;GET /test HTTP/1.1
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Host: localhost:8082
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;User-Agent: curl/8.20.0
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Accept: &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="go"&gt;* Request completely sent off
&amp;lt; HTTP/1.1 404 Not Found
&amp;lt; date: Sat, 30 May 2026 18:42:54 GMT
&amp;lt; content-length: 9
&amp;lt; vary: accept-encoding
&amp;lt; cache-control: max-age=0, private, must-revalidate
&amp;lt; 
&lt;/span&gt;&lt;span class="gp"&gt;* Connection #&lt;/span&gt;0 to host localhost:8082 left intact
&lt;span class="go"&gt;not found
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is working! More is needed to make it production ready, but a client can use both HTTP or WebSockets protocol with the same endpoint! Yeah, that's not really useful, but it was a small objective just to demonstrate how &lt;code&gt;Plug&lt;/code&gt;s can be used.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;The code required to create a really small HTTP/WebSocket server is incredibly lean. That's a good thing. Even more, if we are trusting the documentation, &lt;code&gt;bandit&lt;/code&gt; is faster than &lt;code&gt;cowboy&lt;/code&gt;. If we add the Elixir/Phoenix ecosystem, one can create a strong backend in a short amount of time with a huge ton of features.&lt;/p&gt;

&lt;p&gt;Have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@richiemortis?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Richie Bettencourt&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-close-up-of-a-spider-web-on-a-green-background-dtL0ElIr6Po?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>bandit</category>
      <category>http</category>
      <category>web</category>
    </item>
    <item>
      <title>WebSocket and curl</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Tue, 02 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/websocket-and-curl-46nj</link>
      <guid>https://dev.to/niamtokik/websocket-and-curl-46nj</guid>
      <description>&lt;p&gt;In a previous publication I was talking about the lake of WebSocket support from curl, but it seems I was not using the latest version at this time. I retried that with the &lt;a href="https://github.com/stunnel/static-curl/releases/tag/8.20.0" rel="noopener noreferrer"&gt;&lt;code&gt;8.20.0&lt;/code&gt;&lt;/a&gt; release after reading &lt;a href="https://eissing.org/icing/posts/curl-websocket/" rel="noopener noreferrer"&gt;"websocket in curl"&lt;/a&gt; post by &lt;a href="https://github.com/icing/" rel="noopener noreferrer"&gt;Stefan Eissing&lt;/a&gt;. It's not "perfect" but it works like &lt;code&gt;netcat&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The first step is to fetch/install the latest curl version for your system, at this time, the &lt;a href="https://github.com/stunnel/static-curl/releases/tag/8.20.0" rel="noopener noreferrer"&gt;&lt;code&gt;8.20.0&lt;/code&gt; release&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;wget https://github.com/stunnel/static-curl/releases/download/8.20.0/curl-linux-x86_64-glibc-8.20.0.tar.xz
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;xvf curl-linux-x86_64-glibc-8.20.0.tar.xz
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./curl &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="go"&gt;curl 8.20.0 (x86_64-pc-linux-gnu) libcurl/8.20.0 OpenSSL/4.0.0 zlib/1.3.2 brotli/1.2.0 zstd/1.5.7 c-ares/1.34.6 libidn2/2.3.8 libpsl/0.21.5 libssh2/1.11.1 nghttp2/1.69.0 ngtcp2/1.22.1 nghttp3/1.15.0
Release-Date: 2026-04-29
Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns mqtt mqtts pop3 pop3s rtsp scp sftp smtp smtps telnet tftp ws wss
Features: alt-svc asyn-rr AsynchDNS brotli ECH HSTS HTTP2 HTTP3 HTTPS-proxy HTTPSRR IDN IPv6 Largefile libz PSL SSL SSLS-EXPORT threadsafe TLS-SRP UnixSockets zstd
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can use it against a WebSocket server, let try it with &lt;code&gt;buckaroo&lt;/code&gt; project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./curl &lt;span class="nt"&gt;-T&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-kv&lt;/span&gt; &lt;span class="nt"&gt;--no-progress-meter&lt;/span&gt; ws://127.0.0.1:8081/ws
&lt;span class="go"&gt;*   Trying 127.0.0.1:8081...
* Established connection to 127.0.0.1 (127.0.0.1 port 8081) from 127.0.0.1 port 43194 
* using HTTP/1.x
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;GET /ws HTTP/1.1
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Host: 127.0.0.1:8081
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;User-Agent: curl/8.20.0
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Accept: &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Upgrade: websocket
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Sec-WebSocket-Version: 13
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Sec-WebSocket-Key: Y7nHv/MjumObou+aYHQJTg&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Connection: Upgrade
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="go"&gt;* Request completely sent off
&amp;lt; HTTP/1.1 101 Switching Protocols
&amp;lt; connection: Upgrade
&amp;lt; date: Wed, 27 May 2026 13:01:47 GMT
&amp;lt; sec-websocket-accept: nYSHGW+IDHwj5BJCocg6kA6Jsz8=
&amp;lt; server: Cowboy
&amp;lt; upgrade: websocket
&amp;lt; 
* Received 101, Switching to WebSocket
* [WS] Received 101, switch to WebSocket
{ [5 bytes data]
test
heytest
hello!
hello!
* upload completely sent off: 24 bytes
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It... Works! Curl is still one of the greatest open-source software available on this planet. Having the power to use a WebSocket like &lt;code&gt;netcat&lt;/code&gt;  with curl is really really cool.&lt;/p&gt;

&lt;p&gt;Have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@sschusterphotoart?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Sebastian Schuster&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-thick-coiled-rope-in-a-spiral-pattern-FofozD3XziQ?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>websocket</category>
      <category>curl</category>
      <category>web</category>
      <category>event</category>
    </item>
    <item>
      <title>HTTP/3 and QUIC in Erlang with Cowboy</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Mon, 01 Jun 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/http3-and-quic-in-erlang-with-cowboy-549o</link>
      <guid>https://dev.to/niamtokik/http3-and-quic-in-erlang-with-cowboy-549o</guid>
      <description>&lt;p&gt;Since &lt;a href="https://ninenines.eu/articles/cowboy-2.14.0/" rel="noopener noreferrer"&gt;Cowboy 2.14.0&lt;/a&gt; an experimental implementation of &lt;a href="https://www.rfc-editor.org/rfc/rfc9114" rel="noopener noreferrer"&gt;HTTP/3&lt;/a&gt; (or &lt;a href="https://quicwg.org/" rel="noopener noreferrer"&gt;QUIC&lt;/a&gt; or &lt;a href="https://www.rfc-editor.org/rfc/rfc9000" rel="noopener noreferrer"&gt;RFC9000&lt;/a&gt;) has been released. That's a pretty good news, and if stable enough, will change a lot on the web ecosystem. Unfortunately, this feature is not documented yet - because unstable - but it's not a good reason to play with it and try to understand how to configure a cowboy server with HTTP/3 and QUIC.&lt;/p&gt;

&lt;p&gt;Cowboy does not support natively QUIC and depends on &lt;code&gt;quicer&lt;/code&gt; application to work correctly. To be clear, this is a &lt;strong&gt;BIG NO-NO&lt;/strong&gt; for a deployment in production environment. The list of dependencies directly required by &lt;a href="https://github.com/emqx/quic" rel="noopener noreferrer"&gt;&lt;code&gt;quicer&lt;/code&gt;&lt;/a&gt; is insane, it fetch many python-like modules and recompile OpenSSL from scratch (&lt;a href="https://github.com/quictls/openssl.git" rel="noopener noreferrer"&gt;OpenSSL 3.1.7+quic&lt;/a&gt;, from an unmaintained and archived repository, with all &lt;a href="https://openssl-library.org/news/openssl-3.1-notes/" rel="noopener noreferrer"&gt;security issues&lt;/a&gt; that could implies).&lt;/p&gt;

&lt;p&gt;Anyway, the &lt;code&gt;COWBOY_QUICER&lt;/code&gt; Erlang macro must be set to &lt;code&gt;1&lt;/code&gt; to compile cowboy with HTTP/3 and QUIC support. Then, the &lt;code&gt;rebar.config&lt;/code&gt; file will need few modifications. The configuration for the &lt;code&gt;cowboy&lt;/code&gt; dependency must be overridden and the previous macro must be activated. The &lt;code&gt;quicer&lt;/code&gt; depency is also required, because this is an experimental feature, cowboy does not include it. To test that, let reuse the &lt;a href="https://github.com/niamtokik/buckaroo" rel="noopener noreferrer"&gt;buckaroo project&lt;/a&gt; created in a previous publication.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;erl_opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="n"&gt;debug_info&lt;/span&gt;
&lt;span class="p"&gt;]}.&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="n"&gt;cowboy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;quicer&lt;/span&gt;
&lt;span class="p"&gt;]}.&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;buckaroo&lt;/span&gt;&lt;span class="p"&gt;]}]}.&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;overrides&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cowboy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;erl_opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;'COWBOY_QUICER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]}&lt;/span&gt;
  &lt;span class="p"&gt;]}&lt;/span&gt;
&lt;span class="p"&gt;]}.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A cleanup will be also required there with a full recompilation from scratch of all the dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; _build
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rebar3 compile
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In theory, after this step, cowboy should now be able to start a QUIC server and use HTTP/3. At least, an Erlang shell can be started without problem, and it seems buckaroo is running correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rebar3 shell
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Verifying dependencies...
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Analyzing applications...
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Compiling buckaroo
&lt;span class="go"&gt;Erlang/OTP 29 [erts-17.0] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit:ns]

Eshell V17.0 (press Ctrl+G to abort, type help(). for help)
&lt;/span&gt;&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Booted cowlib
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Booted ranch
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Booted cowboy
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Booted buckaroo
&lt;span class="gp"&gt;1&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A TLS certificate is required, we don't need to create a clean one, simply generating a self-signed with &lt;a href="https://man.openbsd.org/openssl#req" rel="noopener noreferrer"&gt;&lt;code&gt;openssl req&lt;/code&gt;&lt;/a&gt; for now will do the job.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;openssl req &lt;span class="nt"&gt;-x509&lt;/span&gt; &lt;span class="nt"&gt;-newkey&lt;/span&gt; rsa:4096 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  -keyout key.pem \
  -out cert.pem \
  -sha256 \
  -days 3650 \
  -nodes -subj \
  "/C=XX/ST=StateName/L=CityName/O=CompanyName/OU=CompanySectionName/CN=CommonNameOrHostname"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the certificate has been correctly created, &lt;code&gt;quicer&lt;/code&gt; can be started via cowboy with the help of &lt;a href="https://github.com/ninenines/cowboy/blob/master/src/cowboy.erl#L87" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy:start_quic/3&lt;/code&gt;&lt;/a&gt; function. This function is not exposed by default and to have access to it, one must compile &lt;code&gt;cowboy&lt;/code&gt; with &lt;a href="https://github.com/ninenines/cowboy/blob/master/src/cowboy.erl#L79" rel="noopener noreferrer"&gt;&lt;code&gt;COWBOY_QUIC&lt;/code&gt;&lt;/a&gt;&amp;nbsp;macro enabled (what we configured previously). Anyway , to add this step, we can modify the &lt;code&gt;buckaroo_cowboy&lt;/code&gt; module by creating a new function called &lt;code&gt;start_quic/3&lt;/code&gt;. Most of the parameters will be reused from the normal way to start &lt;code&gt;cowboy&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;start_quic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="nn"&gt;application&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;ensure_all_started&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quicer&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nn"&gt;cowboy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;start_quic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;socket_opts&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;transport_options&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;certfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cert.pem"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;keyfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"key.pem"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;protocol_options&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let start the application now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rebar3 shell
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Verifying dependencies...
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Analyzing applications...
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Compiling buckaroo
&lt;span class="go"&gt;Erlang/OTP 29 [erts-17.0] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit:ns]

Eshell V17.0 (press Ctrl+G to abort, type help(). for help)
&lt;/span&gt;&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Booted cowlib
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Booted ranch
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Booted cowboy
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Booted buckaroo
&lt;span class="gp"&gt;1&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;buckaroo_cowboy:start_quic&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="gp"&gt;{ok,#&lt;/span&gt;Ref&amp;lt;0.2047991422.1293549576.157826&amp;gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="gp"&gt;2&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service started... Okay, that's a good step but we still need to be sure it is listening on the right port and see if it's working correctly. Let use &lt;a href="https://manpages.debian.org/trixie/iproute2/ss.8.en.html" rel="noopener noreferrer"&gt;&lt;code&gt;ss&lt;/code&gt;&lt;/a&gt; (or &lt;a href="https://man.openbsd.org/netstat" rel="noopener noreferrer"&gt;&lt;code&gt;netstat&lt;/code&gt;&lt;/a&gt; if you are using another OS).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ss &lt;span class="nt"&gt;-nlpu&lt;/span&gt; sport 8081
&lt;span class="go"&gt;State   Recv-Q  Send-Q   Local Address:Port   Peer Address:Port Process                                  
UNCONN  0       0                    *:8081              *:*     users:(("beam.smp",pid=2784595,fd=86))  
UNCONN  0       0                    *:8081              *:*     users:(("beam.smp",pid=2784595,fd=90))  
UNCONN  0       0                    *:8081              *:*     users:(("beam.smp",pid=2784595,fd=94))  
UNCONN  0       0                    *:8081              *:*     users:(("beam.smp",pid=2784595,fd=98))  
UNCONN  0       0                    *:8081              *:*     users:(("beam.smp",pid=2784595,fd=102)) 
UNCONN  0       0                    *:8081              *:*     users:(("beam.smp",pid=2784595,fd=106)) 
UNCONN  0       0                    *:8081              *:*     users:(("beam.smp",pid=2784595,fd=110)) 
UNCONN  0       0                    *:8081              *:*     users:(("beam.smp",pid=2784595,fd=114)) 
UNCONN  0       0                    *:8081              *:*     users:(("beam.smp",pid=2784595,fd=118)) 
UNCONN  0       0                    *:8081              *:*     users:(("beam.smp",pid=2784595,fd=122)) 
UNCONN  0       0                    *:8081              *:*     users:(("beam.smp",pid=2784595,fd=126)) 
UNCONN  0       0                    *:8081              *:*     users:(("beam.smp",pid=2784595,fd=130)) 
UNCONN  0       0                    *:8081              *:*     users:(("beam.smp",pid=2784595,fd=134)) 
UNCONN  0       0                    *:8081              *:*     users:(("beam.smp",pid=2784595,fd=138)) 
UNCONN  0       0                    *:8081              *:*     users:(("beam.smp",pid=2784595,fd=142)) 
UNCONN  0       0                    *:8081              *:*     users:(("beam.smp",pid=2784595,fd=146)) 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A QUIC service must listen on UDP, and this is the case. The output of &lt;code&gt;ss&lt;/code&gt; command returns the what we are looking for, an application (&lt;code&gt;beam.smp&lt;/code&gt;) listening to all interfaces on UDP/8082.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-kv&lt;/span&gt; &lt;span class="nt"&gt;--http3-only&lt;/span&gt; https://localhost:8081/&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;span class="go"&gt;* Host localhost:8081 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* HTTPS-RR: -
*   Trying [::1]:8081...
* SSL Trust: peer verification disabled
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / x25519 / RSASSA-PSS
* Server certificate:
&lt;/span&gt;&lt;span class="gp"&gt;*   subject: C=XX;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;StateName&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;L&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;CityName&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;CompanyName&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;OU&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;CompanySectionName&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;CN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;CommonNameOrHostname
&lt;span class="go"&gt;*   start date: May 27 04:15:27 2026 GMT
*   expire date: May 24 04:15:27 2036 GMT
&lt;/span&gt;&lt;span class="gp"&gt;*   issuer: C=XX;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;StateName&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;L&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;CityName&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;O&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;CompanyName&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;OU&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;CompanySectionName&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;CN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;CommonNameOrHostname
&lt;span class="go"&gt;*   Certificate level 0: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
* OpenSSL verify result: 12
*  SSL certificate verification failed, continuing anyway!
* Established connection to localhost (::1 port 8081) from ::1 port 33253 
* using HTTP/3
* [HTTP/3] [0] OPENED stream for https://localhost:8081/
* [HTTP/3] [0] [:method: GET]
* [HTTP/3] [0] [:scheme: https]
* [HTTP/3] [0] [:authority: localhost:8081]
* [HTTP/3] [0] [:path: /]
* [HTTP/3] [0] [user-agent: curl/8.20.0]
* [HTTP/3] [0] [accept: */*]
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;GET / HTTP/3
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Host: localhost:8081
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;User-Agent: curl/8.20.0
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Accept: &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="go"&gt;* Request completely sent off
&amp;lt; HTTP/3 200 
&amp;lt; content-length: 5
&amp;lt; content-type: text/plain
&amp;lt; date: Wed, 27 May 2026 12:49:14 GMT
&amp;lt; server: Cowboy
&amp;lt; 
&lt;/span&gt;&lt;span class="gp"&gt;* Connection #&lt;/span&gt;0 to host localhost:8081 left intact
&lt;span class="go"&gt;hello
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks good, cowboy returns the famous &lt;code&gt;hello&lt;/code&gt; message and curl is correctly using HTTP/3. &lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;I would really like to use HTTP/3 and QUIC in production with Erlang and cowboy, but I don't really think this is still a good time to use it. Many things must be improved, and based on the amount of dependencies required, it's a bit scary.&lt;/p&gt;

&lt;p&gt;This project example can still be seen on &lt;a href="https://github.com/niamtokik/buckaroo" rel="noopener noreferrer"&gt;niamtokik/buckaroo&lt;/a&gt; repository at Github.&lt;/p&gt;

&lt;p&gt;Have fun!&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@chrisliverani?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Chris Liverani&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/white-and-blue-analog-tachometer-gauge-HUJDz6CJEaM?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>erlang</category>
      <category>cowboy</category>
      <category>http</category>
      <category>quic</category>
    </item>
    <item>
      <title>OpenBSD 7.9 feedback: iwx powersave issue</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Sat, 30 May 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/openbsd-79-iwx-powersave-issue-252l</link>
      <guid>https://dev.to/niamtokik/openbsd-79-iwx-powersave-issue-252l</guid>
      <description>&lt;p&gt;A quick update regarding the last release of &lt;a href="https://www.openbsd.org/79.html" rel="noopener noreferrer"&gt;OpenBSD 7.9&lt;/a&gt;, I upgraded one of my old laptop (Lenovo Thinkpad AMD T14 gen1) and got a problem with the &lt;a href="https://man.openbsd.org/iwx.4" rel="noopener noreferrer"&gt;iwx&lt;/a&gt; driver (the one in charge of the wifi). During the startup I got those messages (visible in &lt;code&gt;dmesg&lt;/code&gt; as well):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dmesg | &lt;span class="nb"&gt;grep &lt;/span&gt;iwx0
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;iwx0 at pci3 dev 0 function 0 "Intel Wi-Fi 6 AX200" rev 0x1a, msix
iwx0: hw rev 0x340, fw 77.30b1cbd8.0, address e0:d4:aa:bb:cc:dd
iwx0 at pci3 dev 0 function 0 "Intel Wi-Fi 6 AX200" rev 0x1a, msix
iwx0: could not read firmware iwx-cc-a0-77 (error 2)
iwx0: failed to load init firmware
iwx0: hw rev 0x340, fw 77.30b1cbd8.0, address e0:d4:aa:bb:cc:dd
iwx0 at pci3 dev 0 function 0 "Intel Wi-Fi 6 AX200" rev 0x1a, msix
iwx0: hw rev 0x340, fw 77.30b1cbd8.0, address e0:d4:aa:bb:cc:dd
iwx0: fatal firmware error
iwx0: fatal firmware error
iwx0: fatal firmware error
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, my first action was to check if the firmware can be upgraded using &lt;a href="https://man.openbsd.org/fw_update" rel="noopener noreferrer"&gt;&lt;code&gt;fw_update&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;doas fw_update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nope. No new firmware. Let check if the bug is already known by the team and a &lt;a href="https://man.openbsd.org/syspatch" rel="noopener noreferrer"&gt;&lt;code&gt;syspatch&lt;/code&gt;&lt;/a&gt; has been released?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;doas syspatch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing at all, as expected (the &lt;a href="https://www.openbsd.org/errata79.html" rel="noopener noreferrer"&gt;errata page&lt;/a&gt; was empty).&lt;/p&gt;

&lt;p&gt;Okay, let just quickly check the &lt;a href="https://www.openbsd.org/79.html" rel="noopener noreferrer"&gt;release note&lt;/a&gt; again:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Enabled 64-bit DMA transfers on aq(4), em(4), rge(4), re(4), iavf(4), ix(4), ixv(4), ixl(4), igc(4), ice(4) and iwx(4).&lt;/li&gt;
&lt;li&gt;Added support for a 160 MHz window at 5 GHz and enabled it on iwx(4).&lt;/li&gt;
&lt;li&gt;Enabled iwx(4) on i386.&lt;/li&gt;
&lt;li&gt;Fixed iwx(4) issues related to roaming and PMF and firmware crypto keys.&lt;/li&gt;
&lt;li&gt;Set the assoc ID field in iwx(4) firmware commands correctly.&lt;/li&gt;
&lt;li&gt;Added support for BZ devices with WiFi 6e radio to iwx(4).&lt;/li&gt;
&lt;li&gt;Made iwx(4) not load incomplete firmware images and report a proper error instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Added powersave support to iwx(4) and enable by default&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Added support for 160 MHz channel width to iwx(4).&lt;/li&gt;
&lt;li&gt;Increased the VHT frame aggregation size limit from 64k to 1024k on iwx(4).&lt;/li&gt;
&lt;li&gt;Aligned iwx(4) antenna patterns and STBC with iwlwifi.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ah! The culprit could be the power saving enabled by default! I hope so... Because if it's not the case, I will need to upgrade to &lt;code&gt;stable&lt;/code&gt; and I would like to avoid that on this laptop. Luckily, disabling the power saving is not complex, the &lt;a href="https://man.openbsd.org/ifconfig#powersave~2" rel="noopener noreferrer"&gt;&lt;code&gt;-powersave&lt;/code&gt;&lt;/a&gt; flag needs to be added on this device, let put that in &lt;a href="https://man.openbsd.org/man5/hostname.if.5" rel="noopener noreferrer"&gt;&lt;code&gt;/etc/hostname.iwx0&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"-powersave"&lt;/span&gt; | doas &lt;span class="nb"&gt;tee&lt;/span&gt; /etc/hostname.iwx0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can restart the interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;doas /etc/netstart iwx0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And... It works! No more error messages. The problem was the power saving. In short, if you are using a Intel Wi-Fi 6 AX200 device, on a Lenovo T14 AMD Gen1, the power saving does not work with the latest OpenBSD 7.9 version.&lt;/p&gt;

&lt;p&gt;Unfortunately, the next day it was not working anymore. The firmware was still unhappy for some unknown reason. Let switch to debug mode, just to have an idea of the problem. Part of the &lt;code&gt;dmesg&lt;/code&gt; has been removed to avoid leaking my personal network information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;doas ifconfig iwx0 debug
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dmesg
&lt;span class="gp"&gt;iwx0: RUN -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;INIT
&lt;span class="go"&gt;iwx0: begin active scan
&lt;/span&gt;&lt;span class="gp"&gt;iwx0: INIT -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;SCAN
&lt;span class="go"&gt;iwx0: end active scan
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;iwx0: missed beacon threshold set to 25 beacons, beacon interval is 120 TU
iwx0: dumping device error log
iwx0: Start Error Log Dump:
iwx0: Status: 0x879, count: 6
iwx0: 0x00000071 | NMI_INTERRUPT_UMAC_FATAL    
iwx0: 008022F3 | trm_hw_status0
iwx0: 00000000 | trm_hw_status1
iwx0: 004F8F22 | branchlink2
iwx0: 00007FC0 | interruptlink1
iwx0: 00007FC0 | interruptlink2
iwx0: 00015050 | data1
iwx0: 00001000 | data2
iwx0: 00000000 | data3
iwx0: 00087FCC | beacon time
iwx0: 00378032 | tsf low
iwx0: 00000000 | tsf hi
iwx0: 00000000 | time gp1
iwx0: 0037DB02 | time gp2
iwx0: 00000001 | uCode revision type
iwx0: 0000004D | uCode version major
iwx0: 30B1CBD8 | uCode version minor
iwx0: 00000340 | hw version
iwx0: 18089000 | board version
iwx0: 0102001C | hcmd
iwx0: 00021000 | isr0
iwx0: 61040000 | isr1
iwx0: 18F00002 | isr2
iwx0: 00C3400D | isr3
iwx0: 00000000 | isr4
iwx0: 0101001C | last cmd Id
iwx0: 00015050 | wait_event
iwx0: 00000288 | l2p_control
iwx0: 00000000 | l2p_duration
iwx0: 000000BF | l2p_mhvalid
iwx0: 000000EF | l2p_addr_match
iwx0: 00000009 | lmpm_pmg_sel
iwx0: 00000000 | timestamp
iwx0: 00003050 | flow_handler
iwx0: Start UMAC Error Log Dump:
iwx0: Status: 0x879, count: 7
iwx0: 0x20101A0D | ADVANCED_SYSASSERT
iwx0: 0x00000000 | umac branchlink1
iwx0: 0x80455D7A | umac branchlink2
iwx0: 0x0106E0A4 | umac interruptlink1
iwx0: 0x00000000 | umac interruptlink2
iwx0: 0x00000005 | umac data1
iwx0: 0x00000001 | umac data2
iwx0: 0x00000001 | umac data3
iwx0: 0x0000004D | umac major
iwx0: 0x30B1CBD8 | umac minor
iwx0: 0x0037DAFC | frame pointer
iwx0: 0xC0886BC4 | stack pointer
iwx0: 0x001C050F | last host cmd
iwx0: 0x00000000 | isr status reg
driver status:
  tx ring  0: qid=0  cur=29  cur_hw=29  queued=1  
  tx ring  1: qid=1  cur=3   cur_hw=3   queued=1  
  tx ring  2: qid=2  cur=0   cur_hw=0   queued=0  
  tx ring  3: qid=3  cur=0   cur_hw=0   queued=0  
  tx ring  4: qid=4  cur=0   cur_hw=0   queued=0  
  tx ring  5: qid=5  cur=0   cur_hw=0   queued=0  
  tx ring  6: qid=6  cur=0   cur_hw=0   queued=0  
  tx ring  7: qid=7  cur=0   cur_hw=0   queued=0  
  tx ring  8: qid=8  cur=0   cur_hw=0   queued=0  
  tx ring  9: qid=9  cur=0   cur_hw=0   queued=0  
  rx ring: cur=51
  802.11 state RUN
iwx0: fatal firmware error
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hmm, while investigating, I also see the interface was crashing when it was using the &lt;code&gt;11a&lt;/code&gt; mode. Let force the driver to use &lt;code&gt;11n&lt;/code&gt; for example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"mode 11n"&lt;/span&gt; | doas &lt;span class="nb"&gt;tee&lt;/span&gt; /etc/hostname.iwx0
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;doas sh /etc/netstart iwx0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The network is up again! Okay, I really think there is a bug somewhere in the &lt;code&gt;iwx&lt;/code&gt; driver or with the firmware. I never had a similar issue with that in the past, so, I assume something has been upgraded there during OpenBSD 7.9 migration. Anyway, if the issue persist, I will try to find another solution and investigate a bit more.&lt;/p&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@brice_cooper18?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Brice Cooper&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-rusted-out-bus-sitting-in-the-middle-of-a-forest-57mzM8SGNn8?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>openbsd</category>
      <category>iwx</category>
      <category>wifi</category>
      <category>wireless</category>
    </item>
    <item>
      <title>HTTP Server in Erlang with Cowboy</title>
      <dc:creator>Mathieu Kerjouan</dc:creator>
      <pubDate>Fri, 29 May 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/niamtokik/http-server-in-erlang-with-cowboy-28g0</link>
      <guid>https://dev.to/niamtokik/http-server-in-erlang-with-cowboy-28g0</guid>
      <description>&lt;p&gt;Finally, a bit of Erlang! Instead of writing the same 101 introduction to this marvelous language, we will switch directly on the most used HTTP Web Server on this ecosystem: Cowboy. The goal will be to create a flexible module letting us configuring &lt;a href="https://www.rfc-editor.org/info/rfc2616/" rel="noopener noreferrer"&gt;HTTP/1.1&lt;/a&gt;, &lt;a href="https://httpwg.org/specs/rfc7540.html" rel="noopener noreferrer"&gt;HTTP/2&lt;/a&gt;, &lt;a href="https://datatracker.ietf.org/doc/html/rfc9114" rel="noopener noreferrer"&gt;HTTP/3&lt;/a&gt; and &lt;a href="https://www.rfc-editor.org/rfc/rfc6455.html" rel="noopener noreferrer"&gt;WebSocket&lt;/a&gt; easily. Why do I need that? For some of my local test, especially for Dart/Flutter.&lt;/p&gt;

&lt;p&gt;Anyway, it's time to create a new fresh project to explain that, let call it Buckaroo.&lt;/p&gt;

&lt;h1&gt;
  
  
  Project Bootstrapping with rebar3
&lt;/h1&gt;

&lt;p&gt;Just a quick reminder for the newcomers who never tried Erlang, below, the command to generate a new app project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rebar3 new app &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;buckaroo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All the magic will happen in the &lt;code&gt;src/&lt;/code&gt; directory but cowboy must be added in the dependencies list in &lt;code&gt;rebar.config&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;erl_opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;debug_info&lt;/span&gt;&lt;span class="p"&gt;]}.&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cowboy&lt;/span&gt;&lt;span class="p"&gt;]}.&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;buckaroo&lt;/span&gt;&lt;span class="p"&gt;]}]}.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let fetch everything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="sc"&gt;$ &lt;/span&gt;&lt;span class="n"&gt;rebar3&lt;/span&gt; &lt;span class="nb"&gt;get&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;deps&lt;/span&gt;
&lt;span class="sc"&gt;$ &lt;/span&gt;&lt;span class="n"&gt;rebar3&lt;/span&gt; &lt;span class="n"&gt;compile&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Starting Cowboy
&lt;/h1&gt;

&lt;p&gt;To make things easier, a dedicated module to manage cowboy called &lt;code&gt;buckaroo_cowboy&lt;/code&gt; is created. And to avoid too much complexity, only 2 functions will be exported&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;start_link/0&lt;/code&gt; to start the cowboy listener;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;stop/0&lt;/code&gt; to stop the cowboy listener.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buckaroo_cowboy&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;export&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;cowboy&lt;/code&gt; listener needs a name, let create a function called &lt;code&gt;name/0&lt;/code&gt; to deal with that. It will return the listener name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;buckaroo_listener&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the HTTP API routes are required, the &lt;a href="https://ninenines.eu/docs/en/cowboy/2.15/guide/routing/" rel="noopener noreferrer"&gt;cowboy routing documentation&lt;/a&gt; is complete and should be way enough to understand how to routes requests with cowboy. 2 routes will be created there, the default one (&lt;code&gt;/&lt;/code&gt;) will be used for HTTP requests, and (&lt;code&gt;/ws&lt;/code&gt;) for the WebSockets. In both case, the last element of the tuple passed by the router will be a &lt;code&gt;map()&lt;/code&gt;, this value will be used as connection state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;'_'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buckaroo_handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;{}},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"/ws"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buckaroo_websocket_handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;{}}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}].&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The routes, then, must be compiled with the help of the &lt;a href="https://ninenines.eu/docs/en/cowboy/2.15/manual/cowboy_router.compile/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_router:compile/1&lt;/code&gt;&lt;/a&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="nn"&gt;cowboy_router&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A cowboy listener will also require 2 kinds of options, the &lt;a href="https://ninenines.eu/docs/en/ranch/2.1/manual/ranch/#_transport_opts_socketopts" rel="noopener noreferrer"&gt;transport options&lt;/a&gt; (TCP) and the &lt;a href="https://ninenines.eu/docs/en/cowboy/2.15/manual/cowboy/#_opts" rel="noopener noreferrer"&gt;protocol options&lt;/a&gt; (including &lt;a href="https://ninenines.eu/docs/en/cowboy/2.15/manual/cowboy_http/" rel="noopener noreferrer"&gt;HTTP&lt;/a&gt; and &lt;a href="https://ninenines.eu/docs/en/cowboy/2.15/manual/cowboy_http2/" rel="noopener noreferrer"&gt;HTTP/2&lt;/a&gt; options). In our case, only the listening port (TCP/8081) will be set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;transport_options&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8081&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;].&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The protocol options can be defined, in this case, only the routing will be configured via the &lt;code&gt;dispatch&lt;/code&gt; key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;protocol_options&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To start a cowboy listener, the (&lt;code&gt;cowboy:start_clear/3&lt;/code&gt;)(&lt;a href="https://ninenines.eu/docs/en/cowboy/2.15/manual/cowboy.start_clear/" rel="noopener noreferrer"&gt;https://ninenines.eu/docs/en/cowboy/2.15/manual/cowboy.start_clear/&lt;/a&gt;) function can be used. If a TLS certificate is configured, the &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy.start_tls/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy:start_tls/3&lt;/code&gt;&lt;/a&gt; must be used instead. The first argument is the name of the listener, the second the transport options and the last one the protocol options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="nn"&gt;cowboy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;start_clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nf"&gt;transport_options&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nf"&gt;protocol_options&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the &lt;code&gt;stop()&lt;/code&gt; function can be created to help shutdown cowboy, using simply the name previously configured and with the &lt;a href="https://ninenines.eu/docs/en/cowboy/2.15/manual/cowboy.stop_listener/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy:stop_listener/1&lt;/code&gt;&lt;/a&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nn"&gt;cowboy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;stop_listener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick remark regarding my coding style. I like creating really small functions and call them in some part of the code. It can help to document but also to test the module. We will see that in another publication about finite state machines.&lt;/p&gt;

&lt;p&gt;The final modification, is to add the cowboy listener in the supervision tree by editing &lt;code&gt;src/buckaroo_sup.erl&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buckaroo_sup&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;behavior&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;supervisor&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;export&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;

&lt;span class="nf"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="nn"&gt;supervisor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;MODULE&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;MODULE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;

&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(_)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;supervisor_flags&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;child_specs&lt;/span&gt;&lt;span class="p"&gt;()}}.&lt;/span&gt;

&lt;span class="nf"&gt;supervisor_flags&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;one_for_all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;intensity&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;period&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;}.&lt;/span&gt;

&lt;span class="nf"&gt;child_specs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;buckaroo_cowboy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;buckaroo_cowboy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start_link&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;].&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the application can be started, for development and debug purpose only, it will be started with an Erlang shell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rebar3 shell
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Verifying dependencies...
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Analyzing applications...
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Compiling buckaroo
&lt;span class="go"&gt;Erlang/OTP 29 [erts-17.0] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit:ns]

Eshell V17.0 (press Ctrl+G to abort, type help(). for help)
&lt;/span&gt;&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Booted cowlib
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Booted ranch
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Booted cowboy
&lt;span class="gp"&gt;===&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Booted buckaroo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well done, but it will not work correctly, because the handlers are missing... Let fix that in the next sections of this article.&lt;/p&gt;

&lt;h1&gt;
  
  
  Dealing with HTTP Requests
&lt;/h1&gt;

&lt;p&gt;An handler can usually deal with HTTP requests but also with WebSocket ones. Let&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buckaroo_handler&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;export&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;include_lib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"kernel/include/logger.hrl"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="nv"&gt;Reply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;cowboy_req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"content-type"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"text/plain"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;Req&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Reply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cowboy request object is the core data structure used by any handlers, here a quick view of its content when using &lt;code&gt;curl&lt;/code&gt; locally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1260&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;8081&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;scheme&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"http"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;'HTTP/1.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"localhost"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="mi"&gt;50986&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;bindings&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="n"&gt;cert&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"accept"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"*/*"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"host"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"localhost:8081"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"user-agent"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"curl/8.14.1"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;buckaroo_listener&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;host_info&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;path_info&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;streamid&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;body_length&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;has_body&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;qs&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="mi"&gt;8081&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="p"&gt;}.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, this is a simple &lt;code&gt;map()&lt;/code&gt; containing specific keys. The value of those keys can be retrieved using Erlang pattern matching, or by using the cowboy functions helpers from the module &lt;code&gt;cowboy_req&lt;/code&gt;. This data structure will be used to route the request and alter the final response, so, let check them one by one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;pid&lt;/code&gt; is the process id of the process in charge of the request;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;port&lt;/code&gt; is the port from the listener and can also be extracted using &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_req.port/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_req:port/1&lt;/code&gt;&lt;/a&gt; function;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;scheme&lt;/code&gt; is the scheme using, usually &lt;code&gt;http&lt;/code&gt; or &lt;code&gt;https&lt;/code&gt;. This information can be obtained using &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_req.scheme/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_req:scheme/1&lt;/code&gt;&lt;/a&gt; function as well;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;version&lt;/code&gt; is the protocol version used, it can be &lt;code&gt;HTTP/1.0&lt;/code&gt;, &lt;code&gt;HTTP/1.1&lt;/code&gt;, &lt;code&gt;HTTP/2&lt;/code&gt; or &lt;code&gt;HTTP/3&lt;/code&gt;. It can be extracted with the help of &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_req.version/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_req:version/1&lt;/code&gt;&lt;/a&gt; function;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;path&lt;/code&gt; represents the full path requested by the client, this value is more or less related to the routing defined when starting cowboy, it can be extracted using &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_req.path/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_req:path/1&lt;/code&gt;&lt;/a&gt; function;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;host&lt;/code&gt; represents the host name requested by the client, it is also a part of the routing configuration, this value can also be extracted using &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_req.host/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_req:host/1&lt;/code&gt;&lt;/a&gt; function;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;peer&lt;/code&gt; contains the IP address/port of the client, it can also be extracted using &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_req.peer/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_req:peer/1&lt;/code&gt;&lt;/a&gt; function;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;bindings&lt;/code&gt; is set when the route is configured with pattern matching, if it was the case, the variables defined in the route would have been converted into bindings;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;cert&lt;/code&gt; is set when a certificate is sent by the client, it can be extracted using &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_req.cert/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_req:cert/1&lt;/code&gt;&lt;/a&gt; function;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;headers&lt;/code&gt; are the HTTP Headers sent by the client, an header value can be extracted using &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_req.header/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_req:header/2&lt;/code&gt;&lt;/a&gt; or  &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_req.header/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_req:header/3&lt;/code&gt;&lt;/a&gt; functions;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ref&lt;/code&gt; (internal usage) stores the name of the listener;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;host_info&lt;/code&gt; returns the routing pattern matching result, it can also be accessed using &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_req.host_info/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_req:host_info/1&lt;/code&gt;&lt;/a&gt; function;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;path_info&lt;/code&gt; returns the routing pattern matching result, it can also be extracted using &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_req.path_info/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_req:path_info/1&lt;/code&gt;&lt;/a&gt; function;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;streamid&lt;/code&gt; (internal usage) defines the stream id of the connection;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;method&lt;/code&gt; defines the HTTP method used by the client for the request, it can also be retrieved using &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_req.method/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_req:method/1&lt;/code&gt;&lt;/a&gt; function;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;body_length&lt;/code&gt; defines the size of the data sent by the client in the HTTP body, the value of this key can be also fetched using &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_req.body_length/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_req:body_length/1&lt;/code&gt;&lt;/a&gt; function;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;has_body&lt;/code&gt; defines if the request contains a body or not. Mostly used with &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt; and other HTTP methods sending data. It can also be checked using &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_req.has_body/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_req:has_body/1&lt;/code&gt;&lt;/a&gt; function;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;qs&lt;/code&gt; is the raw query string, it should be parsed using &lt;a href="https://www.erlang.org/doc/apps/stdlib/uri_string.html#dissect_query/1" rel="noopener noreferrer"&gt;&lt;code&gt;uri_string:dissect_query/1&lt;/code&gt;&lt;/a&gt; function or with your own method. The value can also be retrieved using &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_req.qs/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_req:qs/1&lt;/code&gt;&lt;/a&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;sock&lt;/code&gt; contains the IP address and port of the server, it can be extracted using &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_req.sock/" rel="noopener noreferrer"&gt;&lt;code&gt;cowboy_req:sock/1&lt;/code&gt;&lt;/a&gt; function.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The handler is done, it's a really simple one, but just to check if it's working, we can use &lt;code&gt;curl&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-kv&lt;/span&gt; http://localhost:8081/&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;span class="go"&gt;* Host localhost:8081 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* HTTPS-RR: -
*   Trying [::1]:8081...
* connect to ::1 port 8081 from ::1 port 48828 failed: Connection refused
*   Trying 127.0.0.1:8081...
* Established connection to localhost (127.0.0.1 port 8081) from 127.0.0.1 port 48800 
* using HTTP/1.x
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;GET / HTTP/1.1
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Host: localhost:8081
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;User-Agent: curl/8.20.0
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Accept: &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="go"&gt;* Request completely sent off
&amp;lt; HTTP/1.1 200 OK
&amp;lt; content-length: 5
&amp;lt; content-type: text/plain
&amp;lt; date: Wed, 27 May 2026 12:20:21 GMT
&amp;lt; server: Cowboy
&amp;lt; 
&lt;/span&gt;&lt;span class="gp"&gt;* Connection #&lt;/span&gt;0 to host localhost:8081 left intact
&lt;span class="go"&gt;hello
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Dealing with WebSockets
&lt;/h1&gt;

&lt;p&gt;Cowboy can also managed &lt;a href="https://ninenines.eu/docs/en/cowboy/2.14/guide/ws_handlers/" rel="noopener noreferrer"&gt;WebSocket connections&lt;/a&gt; using &lt;a href="http://ninenines.eu/docs/en/cowboy/2.14/manual/cowboy_websocket/" rel="noopener noreferrer"&gt;websocket handlers&lt;/a&gt;. The idea is to create 4 callback functions, one for the HTTP request and three for the WebSocket events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buckaroo_websocket_handler&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;export&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;export&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;websocket_init&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;websocket_handle&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;websocket_info&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="ni"&gt;include_lib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"kernel/include/logger.hrl"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a client wants to use WebSocket protocol, it will first send an HTTP request with some specific information, then the server will start the negotiation, this step, is mostly done in &lt;code&gt;init/2&lt;/code&gt; callback.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="nv"&gt;WebSocketOpts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;active_n&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;compress&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;data_delivuery&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;stream_handlers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;data_delivery_flow&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;deflate_opts&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="n"&gt;dynamic_buffer&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;131&lt;/span&gt;&lt;span class="p"&gt;_&lt;/span&gt;&lt;span class="mi"&gt;072&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;idle_timeout&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;_&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_frame_size&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;_&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;req_filter&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;Req&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;validate_utf8&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;LOG_DEBUG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;~p&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;~p&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;~p~n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;MODULE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;FUNCTION_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;WebSocketOpts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;Req&lt;/span&gt;
  &lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cowboy_websocket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;WebSocketOpts&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the end of the negociation, the server can do some initialization state using the &lt;code&gt;websocket_init/1&lt;/code&gt; callback. In our case, the text event &lt;code&gt;hey\n&lt;/code&gt; will send to the client by the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;websocket_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;LOG_DEBUG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;~p&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;~p&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;~p~n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;MODULE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;FUNCTION_NAME&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="p"&gt;{[{&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"hey&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To handle messages sent by the client, a &lt;code&gt;websocket_handle/2&lt;/code&gt; function must be created and exported by the module. The first argument received is a tuple containing the type of data (defined in the &lt;a href="https://github.com/ninenines/cowboy/blob/2.15.0/src/cowboy_websocket.erl#L60" rel="noopener noreferrer"&gt;&lt;code&gt;websocket_handle&lt;/code&gt; callback&lt;/a&gt; and also defined as &lt;a href="https://ninenines.eu/docs/en/cowboy/2.15/manual/cowboy_websocket/#_cow_ws_frame" rel="noopener noreferrer"&gt;&lt;code&gt;cow_ws:frame()&lt;/code&gt;&lt;/a&gt; type), and its content. The second argument is the connection state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;websocket_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;LOG_DEBUG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;~p&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;~p&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;~p&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="si"&gt;~p~n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;MODULE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;FUNCTION_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Frame&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="p"&gt;{[{&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nf"&gt;websocket_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;LOG_DEBUG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;~p&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;~p&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;~p&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="si"&gt;~p~n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;MODULE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;FUNCTION_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Frame&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="p"&gt;{[{&lt;/span&gt;&lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nf"&gt;websocket_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;LOG_DEBUG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;~p&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;~p&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;~p&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="si"&gt;~p~n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;MODULE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;FUNCTION_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Frame&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="p"&gt;{[&lt;/span&gt;&lt;span class="nv"&gt;Frame&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The websocket handler is running in a process, and this process can receive message from another running process from the system. In this case, the message will be handled by &lt;code&gt;websocket_info/2&lt;/code&gt; function callback.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erlang"&gt;&lt;code&gt;&lt;span class="nf"&gt;websocket_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;LOG_DEBUG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;~p&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;~p&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;~p&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="si"&gt;~p~n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;MODULE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nv"&gt;FUNCTION_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let check that with &lt;code&gt;curl&lt;/code&gt; to see what will happen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--no-progress-meter&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; ws://localhost:8081/ws
&lt;span class="go"&gt;* Host localhost:8081 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* HTTPS-RR: -
*   Trying [::1]:8081...
* connect to ::1 port 8081 from ::1 port 45396 failed: Connection refused
*   Trying 127.0.0.1:8081...
* Established connection to localhost (127.0.0.1 port 8081) from 127.0.0.1 port 43458 
* using HTTP/1.x
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;GET /ws HTTP/1.1
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Host: localhost:8081
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;User-Agent: curl/8.20.0
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Accept: &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Upgrade: websocket
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Sec-WebSocket-Version: 13
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Sec-WebSocket-Key: +t+tuHiQUK6MMQySUqDGtQ&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Connection: Upgrade
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="go"&gt;* Request completely sent off
&amp;lt; HTTP/1.1 101 Switching Protocols
&amp;lt; connection: Upgrade
&amp;lt; date: Wed, 27 May 2026 12:21:53 GMT
&amp;lt; sec-websocket-accept: F87i2EOBLl+kwTQ3PCdZqjjcxuA=
&amp;lt; server: Cowboy
&amp;lt; upgrade: websocket
&amp;lt; 
* Received 101, Switching to WebSocket
* [WS] Received 101, switch to WebSocket
{ [5 bytes data]
hey

hello!
hello!
test1
test1
* upload completely sent off: 32 bytes
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The websocket handler correctly acts as an echo server, it looks good!&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;It's always a pleasure to write Erlang code, even more after many weeks where I passed all my time learning Dart/Flutter. Erlang code is concise and efficient. Cowboy is following the same philosophy there, and give us a good idea how to manage requests.&lt;/p&gt;

&lt;p&gt;This project example can be seen on &lt;a href="https://github.com/niamtokik/buckaroo" rel="noopener noreferrer"&gt;niamokik/buckaroo&lt;/a&gt; repository at Github.&lt;/p&gt;

&lt;p&gt;Have fun!&lt;/p&gt;

&lt;h1&gt;
  
  
  References and Resources
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/ninenines/cowboy" rel="noopener noreferrer"&gt;Cowboy Source Code&lt;/a&gt; on Github&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Cover Image by &lt;a href="https://unsplash.com/@jjying?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;JJ Ying&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/purple-and-blue-light-digital-wallpaper-8bghKxNU1j0?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>erlang</category>
      <category>cowboy</category>
      <category>web</category>
      <category>http</category>
    </item>
  </channel>
</rss>
