<?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: Vortico</title>
    <description>The latest articles on DEV Community by Vortico (@vortico).</description>
    <link>https://dev.to/vortico</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%2F1233261%2F61508674-6653-45d0-81a2-ba32ac9ec894.png</url>
      <title>DEV Community: Vortico</title>
      <link>https://dev.to/vortico</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vortico"/>
    <language>en</language>
    <item>
      <title>Releasing Flama 1.10</title>
      <dc:creator>Vortico</dc:creator>
      <pubDate>Wed, 05 Mar 2025 12:30:02 +0000</pubDate>
      <link>https://dev.to/vortico/releasing-flama-110-54d9</link>
      <guid>https://dev.to/vortico/releasing-flama-110-54d9</guid>
      <description>&lt;p&gt;We’re happy to announce &lt;a href="https://github.com/vortico/flama/releases/tag/v1.10.0/" rel="noopener noreferrer"&gt;Flama 1.10&lt;/a&gt; 🎉, packed with exciting features that have been brewing since our last major update. This release rolls up the goodness from 1.8 and 1.9, which we didn’t get around to shouting about earlier, and tops it off with some shiny new additions in 1.10. Let’s dive into the highlights!&lt;/p&gt;

&lt;p&gt;This release brings a slew of new capabilities to make your ML API development smoother, smarter, and more powerful. Here’s what’s new across these versions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access and Refresh Token Components&lt;/strong&gt; (1.8.0): Say hello to enhanced security and flexibility! Flama now includes components for access and refresh tokens, making it easier to implement robust authentication flows and manage user sessions seamlessly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Support for Python 3.13 and Dropping 3.8&lt;/strong&gt; (1.8.0): We’re keeping up with the times—Flama now supports Python 3.13, bringing you the latest language features and performance boosts. To keep things lean, we’ve waved goodbye to Python 3.8 compatibility.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Adapted Serialization for New TensorFlow Version&lt;/strong&gt; (1.8.0): Machine learning devs, rejoice! Flama’s serialization has been updated to play nicely with the latest TensorFlow version, ensuring your models integrate effortlessly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Typing.Annotated for Schema Types&lt;/strong&gt; (1.9.0): We’ve leveled up our type system by adopting &lt;code&gt;typing.Annotated&lt;/code&gt; for schema definitions. This makes your code more expressive and type-safe, giving you clearer control over your API schemas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Allow Importing DDD Without SQLAlchemy Installed&lt;/strong&gt; (1.9.0): Flexibility is key! You can now use Flama’s &lt;code&gt;ddd&lt;/code&gt; module without needing SQLAlchemy, making it lighter and more adaptable to your project’s needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compatibility Lib for Older Python Versions&lt;/strong&gt; (1.9.0): While we’ve embraced the future with 3.13, we’ve also added a compatibility library to ensure smooth sailing on older Python versions where needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Path Params Component&lt;/strong&gt; (1.10.0): Dynamic routing just got better! The new path parameters component lets you handle URL variables with ease, giving your APIs more versatility.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Replace Black and Isort with Ruff&lt;/strong&gt; (1.10.0): We’ve swapped out Black and Isort for Ruff, a faster and more comprehensive tool for code formatting and linting. Your development workflow just got a speed boost!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Replace Poetry with UV&lt;/strong&gt; (1.10.0): Dependency management is now powered by UV instead of Poetry, offering a snappier and more efficient way to handle your project’s packages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Module for Gathering Telemetry Data&lt;/strong&gt; (1.10.0): Get insights like never before! Flama now includes a telemetry module to help you track usage and performance, empowering data-driven decisions for your APIs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enrich OpenAPI Specification&lt;/strong&gt; (1.10.0): Your API docs are about to get a glow-up. We’ve enriched the OpenAPI spec generation, making it more detailed and user-friendly for consumers of your APIs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docs Page Using Scalar&lt;/strong&gt; (1.10.0): Speaking of docs, we’ve introduced a sleek new documentation page powered by Scalar. It’s prettier, more interactive, and makes exploring Flama’s features a breeze.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To help you make the most of these updates, we’ll be rolling out a few detailed follow-up posts with examples showcasing how to leverage telemetry, path params, and the enriched OpenAPI specs in your projects. Keep an eye out!&lt;/p&gt;

&lt;p&gt;Stay tuned for more, and happy coding! 🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://flama.dev/docs/" rel="noopener noreferrer"&gt;Flama documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vortico/flama" rel="noopener noreferrer"&gt;Flama GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/flama/" rel="noopener noreferrer"&gt;Flama PyPI package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  About the authors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vortico.tech/" rel="noopener noreferrer"&gt;Vortico&lt;/a&gt;: We’re specialised in software development to help businesses enhance and expand their AI and technology capabilities.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flama</category>
      <category>machinelearning</category>
      <category>python</category>
      <category>api</category>
    </item>
    <item>
      <title>Native Domain-Driven Design with Flama</title>
      <dc:creator>Vortico</dc:creator>
      <pubDate>Mon, 28 Oct 2024 08:03:09 +0000</pubDate>
      <link>https://dev.to/vortico/native-domain-driven-design-with-flama-l9o</link>
      <guid>https://dev.to/vortico/native-domain-driven-design-with-flama-l9o</guid>
      <description>&lt;p&gt;You've probably already heard about the recent release of &lt;a href="https://dev.to/vortico/releasing-flama-17-3n78"&gt;Flama 1.7&lt;/a&gt;, which brought some exciting new features to help you with the development and productionalisation of your ML APIs. This post is precisely devoted to one of the main highlights of that release: &lt;strong&gt;Support for Domain-Driven Design&lt;/strong&gt;. We recommend you to consider the following resources (and, get familiar with them if you aren't already):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Official Flama documentation: &lt;a href="https://flama.dev/docs/" rel="noopener noreferrer"&gt;Flama documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Post introducing Flama for ML APIs: &lt;a href="https://dev.to/vortico/introducing-flama-for-robust-machine-learning-apis-b3n"&gt;Introduction to Flama for Robust Machine Learning APIs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, let's get started with the new feature and see how you can leverage it to build robust and maintainable ML APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;p&gt;This post is structured as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
What is Domain-Driven Design?

&lt;ul&gt;
&lt;li&gt;Brief Overview&lt;/li&gt;
&lt;li&gt;Key Concepts&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Implementing DDD with Flama

&lt;ul&gt;
&lt;li&gt;Setting up the development environment&lt;/li&gt;
&lt;li&gt;Base application&lt;/li&gt;
&lt;li&gt;DDD in action&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;li&gt;Support our work&lt;/li&gt;

&lt;li&gt;References&lt;/li&gt;

&lt;li&gt;About the authors&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Domain-Driven Design?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Brief Overview
&lt;/h3&gt;

&lt;p&gt;In modern software development, aligning business logic with the technical design of an application is essential. This is where Domain-Driven Design (DDD) shines. DDD emphasizes building software that reflects the core domain of the business, breaking down complex problems by organizing code around business concepts. By doing so, DDD helps developers to create maintainable, scalable, and robust applications. In what follows we introduce what we consider the most important concepts of DDD that you should be aware of. Let's remark that this post is not intended to be a comprehensive guide to DDD, nor a substitute of the main references on the topic. Indeed, we recommend the following resources to get a deeper understanding of DDD:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.cosmicpython.com/" rel="noopener noreferrer"&gt;Cosmic Python&lt;/a&gt; by Harry Percival and Bob Gregory: This book is a great resource to learn how to apply DDD in Python.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.oreilly.com/library/view/domain-driven-design-tackling/0321125215/" rel="noopener noreferrer"&gt;Domain-Driven Design: Tackling Complexity in the Heart of Software&lt;/a&gt; by Eric Evans: This is the book that introduced DDD to the world, and it's a must-read for anyone interested in developing a deep understanding of DDD.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key Concepts
&lt;/h3&gt;

&lt;p&gt;Before exploring any of the key concepts of DDD, we recommend you to have a look at a quite useful figure by &lt;a href="https://www.cosmicpython.com/" rel="noopener noreferrer"&gt;Cosmic Python&lt;/a&gt; where these are shown in the context of an app, thus showing how they're interconnected: &lt;a href="https://www.cosmicpython.com/book/images/apwp_0602.png" rel="noopener noreferrer"&gt;figure&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Domain Model
&lt;/h4&gt;

&lt;p&gt;The concept of &lt;strong&gt;domain model&lt;/strong&gt; can be explained by a simplistic definition of its terms: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;domain&lt;/em&gt; refers to the specific subject area of activity (or knowledge) that our software is being built to support for.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;model&lt;/em&gt; refers to a simple representation (or abstraction) of the system or process that we are trying to encode in our software.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thus, the &lt;strong&gt;domain model&lt;/strong&gt; is a fancy (but standard and useful) way to refer to the set of concepts and rules that business owners have in their mind about how the business works. This is what we also, and commonly, refer to as the &lt;em&gt;business logic&lt;/em&gt; of the application, including the rules, constraints, and relationships that govern the behavior of the system.&lt;/p&gt;

&lt;p&gt;We'll refer to the &lt;strong&gt;domain model&lt;/strong&gt; as the &lt;em&gt;model&lt;/em&gt; from now on.&lt;/p&gt;

&lt;h4&gt;
  
  
  Repository pattern
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;repository pattern&lt;/strong&gt; is a design pattern that allows for the decoupling the model from the data access. The main idea behind the repository pattern is to create an abstraction layer between the data access logic and the business logic of an application. This abstraction layer allows for the separation of concerns, making the code more maintainable and testable.&lt;/p&gt;

&lt;p&gt;When implementing the repository pattern, we typically define an interface that specifies the standard methods that any other repository must implement (&lt;code&gt;AbstractRepository&lt;/code&gt;). And, then, a particular repository is defined with the concrete implementation of these methods where the data access logic is implemented (e.g., &lt;code&gt;SQLAlchemyRepository&lt;/code&gt;). This design pattern aims at isolating the data manipulation methods so that they can be used seamlessly elsewhere in the application, e.g. in our domain model.&lt;/p&gt;

&lt;h4&gt;
  
  
  Unit of work pattern
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;unit of work pattern&lt;/strong&gt; is the missing part to finally decouple the model from the data access. The unit of work encapsulates the data access logic and provides a way to group all the operations that must be performed on the data source within a single transaction. This pattern ensures that all the operations are performed atomically.&lt;/p&gt;

&lt;p&gt;When implementing the unit of work pattern, we typically define an interface that specifies the standard methods that any other unit of work must implement (&lt;code&gt;AbstractUnitOfWork&lt;/code&gt;). And, then, a particular unit of work is defined with the concrete implementation of these methods where the data access logic is implemented (e.g., &lt;code&gt;SQLAlchemyUnitOfWork&lt;/code&gt;). This design allows for a systematic handling of the connection to the data source, without the need to change the implementation of the business logic of the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing DDD with Flama
&lt;/h2&gt;

&lt;p&gt;After the quick introduction to the main concepts of DDD, we're ready to dive into the implementation of DDD with Flama. In this section, we'll guide you through the process of setting up the development environment, building a base application, and implementing DDD concepts with Flama.&lt;/p&gt;

&lt;p&gt;Prior to the example, let's have a look at &lt;strong&gt;Flama&lt;/strong&gt;'s naming convention regarding the main DDD concepts we've just reviewed:&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%2Fa0ss1xkpeq604bogw0un.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%2Fa0ss1xkpeq604bogw0un.png" alt="DDD with Flama" width="800" height="636"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see in the figure above, the naming convention is quite intuitive: &lt;code&gt;Repository&lt;/code&gt; refers to the repository pattern; and, &lt;code&gt;Worker&lt;/code&gt; refers to the unit of work. Now, we can now move on to the implementation of a Flama API which uses DDD. But, before we start, if you need to review the basics on how to create a simple API with &lt;strong&gt;flama&lt;/strong&gt;, or how to run the API once you've already the code ready, then you might want to check out the &lt;a href="https://flama.dev/docs/getting-started/quickstart/" rel="noopener noreferrer"&gt;quick start guide&lt;/a&gt;. There, you'll find the fundamental concepts and steps required to follow this post. Now, without further ado, let's get started with the implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the development environment
&lt;/h3&gt;

&lt;p&gt;Our first step is to create our development environment, and install all required dependencies for this project. The good thing is that for this example we only need to install &lt;strong&gt;flama&lt;/strong&gt; to have all the necessary tools to implement JWT authentication. We'll be using &lt;a href="https://python-poetry.org/" rel="noopener noreferrer"&gt;&lt;code&gt;poetry&lt;/code&gt;&lt;/a&gt; to manage our dependencies, but you can also use &lt;code&gt;pip&lt;/code&gt; if you prefer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poetry add &lt;span class="s2"&gt;"flama[full]"&lt;/span&gt; &lt;span class="s2"&gt;"aiosqlite"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;aiosqlite&lt;/code&gt; package is required to use SQLite with SQLAlchemy, which is the database we'll be using in this example. &lt;/p&gt;

&lt;p&gt;If you want to know how we typically organise our projects, have a look at our previous post &lt;a href="https://dev.to/vortico/introducing-flama-for-robust-machine-learning-apis-b3n#development-environment"&gt;here&lt;/a&gt;, where we explain in detail how to set up a python project with &lt;code&gt;poetry&lt;/code&gt;, and the project folder structure we usually follow. &lt;/p&gt;

&lt;h3&gt;
  
  
  Base application
&lt;/h3&gt;

&lt;p&gt;Let's start with a simple application that has a single public endpoint. This endpoint will return a brief description of the API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# src/app.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flama&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flama&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Domain-driven API&lt;/span&gt;&lt;span class="sh"&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;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Domain-driven design with Flama 🔥&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/docs/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;info&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    tags:
        - Info 
    summary:
        Ping
    description:
        Returns a brief description of the API
    responses:
        200:
            description:
                Successful ping.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to run this application, you can save the code above in a file called &lt;code&gt;app.py&lt;/code&gt; under the &lt;code&gt;src&lt;/code&gt; folder, and then run the following command (remember to have the &lt;code&gt;poetry&lt;/code&gt; environment activated, otherwise you'll need to prefix the command with &lt;code&gt;poetry run&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flama run &lt;span class="nt"&gt;--server-reload&lt;/span&gt; src.app:app

INFO:     Started server process &lt;span class="o"&gt;[&lt;/span&gt;3267]
INFO:     Waiting &lt;span class="k"&gt;for &lt;/span&gt;application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 &lt;span class="o"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where the &lt;code&gt;--server-reload&lt;/code&gt; flag is optional and is used to reload the server automatically when the code changes. This is very useful during development, but you can remove it if you don't need it. For a full list of the available options, you can run &lt;code&gt;flama run --help&lt;/code&gt;, or check the &lt;a href="https://flama.dev/docs/flama-cli/run/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Alternatively, you can also run the application by running the following script, which you can save as &lt;code&gt;__main__.py&lt;/code&gt; under the  &lt;code&gt;src&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# src/__main__.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;flama&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;flama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;flama_app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src.app:app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="n"&gt;server_host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="n"&gt;server_port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="n"&gt;server_reload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then, you can run the application by executing the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poetry run python src/__main__.py

INFO:     Started server process &lt;span class="o"&gt;[&lt;/span&gt;3267]
INFO:     Waiting &lt;span class="k"&gt;for &lt;/span&gt;application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 &lt;span class="o"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DDD in action
&lt;/h3&gt;

&lt;p&gt;Now, having set up a minimal skeleton for our application, we can start implementing the DDD concepts we've just reviewed within the &lt;br&gt;
context of a simple example which tries to mimic a real-world scenario. Let's assume we are requested to develop an API to manage users, and we are provided with the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We want to create new users via a &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;/user/&lt;/code&gt;, providing the user's name, surname, email, and password.&lt;/li&gt;
&lt;li&gt;Any user created will be stored in a database with the following schema:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt;: unique identifier for the user.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt;: user's name.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;surname&lt;/code&gt;: user's surname.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;email&lt;/code&gt;: user's email.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;password&lt;/code&gt;: user's password. This should be hashed before storing it in the database.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;active&lt;/code&gt;: a boolean flag to indicate whether the user is active or not. By default, users are created as inactive.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Users created must activate their account by sending a &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;/user/activate/&lt;/code&gt; with their email and password. Once the user is activated, the user's status must be updated in the database to active.&lt;/li&gt;
&lt;li&gt;Users can sign in by sending a &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;/user/signin/&lt;/code&gt; with their email and password. If the user is active, the API must return all user's information. Otherwise, the API must return an error message.&lt;/li&gt;
&lt;li&gt;Users that want to deactivate their account can do so by sending a &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;/user/deactivate/&lt;/code&gt; with their email and password. Once the user is deactivated, the user's status must be updated in the database to inactive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This set of requirements constitute what we've previously referred to as the &lt;em&gt;domain model&lt;/em&gt; of our application, which essentially is nothing but a materialisation of the following user workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A user is created via a &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;/user/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The user activates their account via a &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;/user/activate/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The user signs in via a &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;/user/signin/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The user deactivates their account via a &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;/user/deactivate/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The user can repeat steps 2-4 as many times as they want.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, let's implement the domain model using the repository and worker patterns. We'll start by defining the data model, and then we'll implement the repository and worker patterns.&lt;/p&gt;
&lt;h4&gt;
  
  
  Data model
&lt;/h4&gt;

&lt;p&gt;Our users data will be stored in a SQLite database (you can use any other database supported by SQLAlchemy). We'll use the following data model to represent the users (you can save this code in a file called &lt;code&gt;models.py&lt;/code&gt; under the &lt;code&gt;src&lt;/code&gt; folder):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# src/models.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.sqlalchemy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy.dialects.postgresql&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt;

&lt;span class="n"&gt;__all__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_table&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;metadata&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;user_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;as_uuid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;surname&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;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;Besides the data model, we need a migration script to create the database and the table. For this, we can save the following code in a file called &lt;code&gt;migrations.py&lt;/code&gt; at the root of the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# migrations.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_engine&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;src.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Set up the SQLite database
&lt;/span&gt;    &lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sqlite:///models.db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Create the database tables
&lt;/span&gt;    &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Print a success message
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Database and User table created successfully.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then, we can run the migration script by executing the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; poetry run python migrations.py

Database and User table created successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Repository
&lt;/h4&gt;

&lt;p&gt;In this example we are going to need only one repository, namely the repository which will handle the atomic operations on the user table, the name of which will be &lt;code&gt;UserRepository&lt;/code&gt;. Thankfully, &lt;strong&gt;flama&lt;/strong&gt; provides a base class for repositories related to SQLAlchemy tables, called &lt;code&gt;SQLAlchemyTableRepository&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The class &lt;code&gt;SQLAlchemyTableRepository&lt;/code&gt; provides a set of methods to perform CRUD operations on the table, specifically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;create&lt;/code&gt;: Creates new elements in the table. If the element already exists, it will raise an exception (&lt;code&gt;IntegrityError&lt;/code&gt;), otherwise it will return the primary key of the new element.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;retrieve&lt;/code&gt;: Retrieves an element from the table. If the element does not exist, it will raise an exception (&lt;code&gt;NotFoundError&lt;/code&gt;), otherwise it will return the element. If more than one element is found, it will raise an exception (&lt;code&gt;MultipleRecordsError&lt;/code&gt;). &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;update&lt;/code&gt;: Updates an element in the table. If the element does not exist, it will raise an exception (&lt;code&gt;NotFoundError&lt;/code&gt;), otherwise it will return the updated element. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;delete&lt;/code&gt;: Deletes an element from the table. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;list&lt;/code&gt;: Lists all the elements in the table that match the clauses and filters passed. If no clauses or filters are given, it returns all the elements in the table. If no elements are found, it returns an empty list.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;drop&lt;/code&gt;: Drops the table from the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the purposes of our example, we don't need any further action on the table, so the methods provided by the &lt;code&gt;SQLAlchemyTableRepository&lt;/code&gt; are sufficient. We can save the following code in a file called &lt;code&gt;repositories.py&lt;/code&gt; under the &lt;code&gt;src&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# src/repositories.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.ddd&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SQLAlchemyTableRepository&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="n"&gt;__all__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UserRepository&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SQLAlchemyTableRepository&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_table&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the &lt;code&gt;UserRepository&lt;/code&gt; class is a subclass of &lt;code&gt;SQLAlchemyTableRepository&lt;/code&gt;, and it only requires the table to be set in the &lt;code&gt;_table&lt;/code&gt; attribute. This is the only thing we need to do to have a fully functional repository for the user table.&lt;/p&gt;

&lt;p&gt;If we wanted to add custom methods beyond the standard CRUD operations, we could do so by defining them in the &lt;code&gt;UserRepository&lt;/code&gt; class. For example, if we wanted to add a method to count the number of active users, we could do so as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# src/repositories.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.ddd&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SQLAlchemyTableRepository&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="n"&gt;__all__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UserRepository&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SQLAlchemyTableRepository&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_table&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_active_users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although we won't be using this method in our example, it's good to know that we can add custom methods to the repository if needed, and how they are implemented &lt;br&gt;
in the context of the repository pattern. This is a powerful design pattern as we can already see, since we can implement here all the data access logic without having to change the business logic of the application (which is implemented in the corresponding resource methods).&lt;/p&gt;
&lt;h4&gt;
  
  
  Worker
&lt;/h4&gt;

&lt;p&gt;The unit-of-work pattern is used to encapsulate the data access logic and provide a way to group all the operations that must be performed on the data source within a single transaction. In &lt;strong&gt;flama&lt;/strong&gt; the UoW pattern is implemented with the name of &lt;code&gt;Worker&lt;/code&gt;. In the same way as with the repository pattern, &lt;strong&gt;flama&lt;/strong&gt; provides a base class for workers related to SQLAlchemy tables, called &lt;code&gt;SQLAlchemyWorker&lt;/code&gt;. In essence, the &lt;code&gt;SQLAlchemyWorker&lt;/code&gt; provides a connection and a transaction to the database, and instantiates all its repositories with the worker connection. In this example, our worker will only use a single repository (namely, the &lt;code&gt;UserRepository&lt;/code&gt;) but we could add more repositories if needed. &lt;/p&gt;

&lt;p&gt;Our worker will be called &lt;code&gt;RegisterWorker&lt;/code&gt;, and we can save the following code in a file called &lt;code&gt;workers.py&lt;/code&gt; under the &lt;code&gt;src&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# src/workers.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.ddd&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SQLAlchemyWorker&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;repositories&lt;/span&gt;

&lt;span class="n"&gt;__all__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RegisterWorker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RegisterWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SQLAlchemyWorker&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;repositories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserRepository&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thus, if we had more repositories to work with, for instance &lt;code&gt;ProductRepository&lt;/code&gt; and &lt;code&gt;OrderRepository&lt;/code&gt;, we could add them to the worker as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# src/workers.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.ddd&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SQLAlchemyWorker&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;repositories&lt;/span&gt;

&lt;span class="n"&gt;__all__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RegisterWorker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RegisterWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SQLAlchemyWorker&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;repositories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserRepository&lt;/span&gt;
    &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;repositories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProductRepository&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;repositories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderRepository&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As simple as that, we have implemented the repository and worker patterns in our application. Now, we can move on to implement the resource methods that will provide the API endpoints needed to interact with the user data. &lt;/p&gt;

&lt;h4&gt;
  
  
  Resources
&lt;/h4&gt;

&lt;p&gt;Resources are one of the main building blocks of a &lt;strong&gt;flama&lt;/strong&gt; application. They are used to represent application resources (in the sense of RESTful resources) and to define the API endpoints that interact with them. &lt;/p&gt;

&lt;p&gt;In our example, we will define a resource for the user, called &lt;code&gt;UserResource&lt;/code&gt;, which will contain the methods to create, activate, sign in, and deactivate users. Resources need to derive, at least, from the &lt;strong&gt;flama&lt;/strong&gt; built-in &lt;code&gt;Resource&lt;/code&gt; class, although &lt;strong&gt;flama&lt;/strong&gt; provides more sophisticated classes to work with such as &lt;code&gt;RESTResource&lt;/code&gt;and &lt;code&gt;CRUDResource&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;We can save the following code in a file called &lt;code&gt;resources.py&lt;/code&gt; under the &lt;code&gt;src&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# src/resources.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.ddd.exceptions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;NotFoundError&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.exceptions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HTTPException&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;APIResponse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.resources&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource_method&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;

&lt;span class="n"&gt;__all__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AdminResource&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UserResource&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;ENCRYPTION_SALT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nb"&gt;hex&lt;/span&gt;
&lt;span class="n"&gt;ENCRYPTION_PEPER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nb"&gt;hex&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha512&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha512&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_password&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ENCRYPTION_SALT&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ENCRYPTION_PEPER&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;verbose_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="nd"&gt;@resource_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterWorker&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;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserDetails&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        tags:
            - User
        summary:
            User create
        description:
            Create a user
        responses:
            200:
                description:
                    User created in successfully.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;worker&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;NotFoundError&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;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Password&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;APIResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPStatus&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="nd"&gt;@resource_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/signin/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;signin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterWorker&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;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserCredentials&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        tags:
            - User
        summary:
            User sign in
        description:
            Create a user
        responses:
            200:
                description:
                    User signed in successfully.
            401:
                description:
                    User not active.
            404:
                description:
                    User not found.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Password&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&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;user&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;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;NotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NOT_FOUND&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UNAUTHORIZED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BAD_REQUEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User must be activated via /user/activate/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;APIResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPStatus&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="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@resource_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/activate/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;activate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterWorker&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;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserCredentials&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        tags:
            - User
        summary:
            User activate
        description:
            Activate an existing user
        responses:
            200:
                description:
                    User activated successfully.
            401:
                description:
                    User activation failed due to invalid credentials.
            404:
                description:
                    User not found.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;worker&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;user&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;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;NotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NOT_FOUND&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nc"&gt;Password&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UNAUTHORIZED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="sh"&gt;"&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;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;APIResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPStatus&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="nd"&gt;@resource_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/deactivate/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deactivate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;deactivate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterWorker&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;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserCredentials&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        tags:
            - User
        summary:
            User deactivate
        description:
            Deactivate an existing user
        responses:
            200:
                description:
                    User deactivated successfully.
            401:
                description:
                    User deactivation failed due to invalid credentials.
            404:
                description:
                    User not found.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;worker&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;user&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;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;NotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NOT_FOUND&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nc"&gt;Password&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UNAUTHORIZED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="sh"&gt;"&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;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;APIResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPStatus&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Base application with DDD
&lt;/h4&gt;

&lt;p&gt;Now that we have implemented the data model, the repository and worker patterns, and the resource methods, we need to modify the base application we introduced before, so that everything works as expected. We need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the SQLAlchemy connection to the application, and this is achieved by adding the &lt;code&gt;SQLAlchemyModule&lt;/code&gt; to the application constructor as a module.&lt;/li&gt;
&lt;li&gt;Add the worker to the application, and this is achieved by adding the &lt;code&gt;RegisterWorker&lt;/code&gt; to the application constructor as a component.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This will leave the &lt;code&gt;app.py&lt;/code&gt; file as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# src/app.py
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flama&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.ddd&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WorkerComponent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.sqlalchemy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SQLAlchemyModule&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;

&lt;span class="n"&gt;DATABASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sqlite+aiosqlite:///models.db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flama&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Domain-driven API&lt;/span&gt;&lt;span class="sh"&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;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Domain-driven design with Flama 🔥&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/docs/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;SQLAlchemyModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
    &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;WorkerComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RegisterWorker&lt;/span&gt;&lt;span class="p"&gt;())],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/user/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserResource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;info&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    tags:
        - Info
    summary:
        Ping
    description:
        Returns a brief description of the API
    responses:
        200:
            description:
                Successful ping.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should be apparent to you already how the DDD pattern has allowed us to separate the &lt;em&gt;business logic&lt;/em&gt; of the application (which is easily readable in the resource methods) from the &lt;em&gt;data access logic&lt;/em&gt; (which is implemented in the repository and worker patterns). It's also worth noting how this sepration of concerns has made the code more maintainable and testable, and how the code is now more aligned with the business requirements we were given at the beginning of this example.&lt;/p&gt;

&lt;h4&gt;
  
  
  Running the application
&lt;/h4&gt;

&lt;p&gt;Before running any command, please check that your development environment is set up correctly, and that the folder structure is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── README.md
├── migration.py
├── models.db
└── src
    ├── __init__.py
    ├── __main__.py
    ├── app.py
    ├── models.py
    ├── repositories.py
    ├── resources.py
    ├── schemas.py
    └── worker.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything is set up correctly, you can run the application by executing the following command (remember to run the migration script before running the application):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; poetry run flama run &lt;span class="nt"&gt;--server-reload&lt;/span&gt; src.app:app

INFO:     Will watch &lt;span class="k"&gt;for &lt;/span&gt;changes &lt;span class="k"&gt;in &lt;/span&gt;these directories: &lt;span class="o"&gt;[&lt;/span&gt;...]
INFO:     Uvicorn running on http://127.0.0.1:8000 &lt;span class="o"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span class="o"&gt;)&lt;/span&gt;
INFO:     Started reloader process &lt;span class="o"&gt;[&lt;/span&gt;35369] using WatchFiles
INFO:     Started server process &lt;span class="o"&gt;[&lt;/span&gt;35373]
INFO:     Waiting &lt;span class="k"&gt;for &lt;/span&gt;application startup.
INFO:     Application startup complete.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can try the business logic we've just implemented. Remember, you can try this either by using a tool like &lt;code&gt;curl&lt;/code&gt; or &lt;code&gt;Postman&lt;/code&gt;, or by using the auto-generated docs UI provided by &lt;strong&gt;flama&lt;/strong&gt; by navigating to &lt;code&gt;http://localhost:8000/docs/&lt;/code&gt; in your browser and trying the endpoints from there.&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%2F4oqolzo1a04meh0t5bqr.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%2F4oqolzo1a04meh0t5bqr.png" alt="Autogenerated docs UI" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Create a user
&lt;/h5&gt;

&lt;p&gt;To create a user, you can send a &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;/user/&lt;/code&gt; with the following payload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"surname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john@doe.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, we can use &lt;code&gt;curl&lt;/code&gt; to send the request as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:8000/user/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
  "name": "John",
  "surname": "Doe",
  "email": "john@doe.com",
  "password": "123456"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the request is successful, you should receive a &lt;code&gt;200&lt;/code&gt; response with an empty body, and the user will be created in the database.&lt;/p&gt;

&lt;h5&gt;
  
  
  Sign in
&lt;/h5&gt;

&lt;p&gt;To sign in, you can send a &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;/user/signin/&lt;/code&gt; with the following payload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john@doe.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123456"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, we can use &lt;code&gt;curl&lt;/code&gt; to send the request as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:8000/user/signin/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
  "email": "john@doe.com",
  "password": "123456"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Given that the user is not active, you should receive something like the following response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"User must be activated via /user/activate/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"HTTPException"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also test what would happen if someone tries to sign in with the wrong password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:8000/user/signin/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
    "email": "john@doe.com",
    "password": "1234567"
    }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, you should receive a &lt;code&gt;401&lt;/code&gt; response with the following body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Unauthorized"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"HTTPException"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we should also try to sign in with a user that doesn't exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:8000/user/signin/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
    "email": "foo@bar.com",
    "password": "123456"
    }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, you should receive a &lt;code&gt;404&lt;/code&gt; response with the following body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status_code"&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="w"&gt; 
  &lt;/span&gt;&lt;span class="nl"&gt;"detail"&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"HTTPException"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  User activation
&lt;/h5&gt;

&lt;p&gt;Having explored the sign in process, we can now activate the user by sending a &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;/user/activate/&lt;/code&gt; with the credentials of the user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:8000/user/activate/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
  "email": "john@doe.com",
  "password": "123456" 
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this request, the user should be activated, and you should receive a &lt;code&gt;200&lt;/code&gt; response with an empty body.&lt;/p&gt;

&lt;p&gt;As in the previous case, we can also test what would happen if someone tries to activate the user with the wrong password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:8000/user/activate/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
    "email": "john@doe.com",
    "password": "1234567" 
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, you should receive a &lt;code&gt;401&lt;/code&gt; response with the following body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Unauthorized"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"HTTPException"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we should also try to activate a user that doesn't exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:8000/user/activate/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
    "email": "foo@bar.com",
    "password": "123456"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, you should receive a &lt;code&gt;404&lt;/code&gt; response with the following body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status_code"&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="w"&gt; 
  &lt;/span&gt;&lt;span class="nl"&gt;"detail"&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="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"HTTPException"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  User sign in after activation
&lt;/h5&gt;

&lt;p&gt;Now that the user is activated, we can try to sign in again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:8000/user/signin/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
    "email": "john@doe.com",
     "password": "123456"
    }'&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which, this time, should return a &lt;code&gt;200&lt;/code&gt; response with the user's information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"john@doe.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"surname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"d73d4a62-dfe9-4907-91f4-f6b06f33c534"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"active"&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="w"&gt; 
  &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  User deactivation
&lt;/h5&gt;

&lt;p&gt;Finally, we can deactivate the user by sending a &lt;code&gt;POST&lt;/code&gt; request to &lt;code&gt;/user/deactivate/&lt;/code&gt; with the credentials of the user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:8000/user/deactivate/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
    "email": "john@doe.com",
    "password": "123456"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this request, the user should be deactivated, and you should receive a &lt;code&gt;200&lt;/code&gt; response with an empty body.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post we've ventured into the world of Domain-Driven Design (DDD) and how it can be implemented in a &lt;strong&gt;flama&lt;/strong&gt; application. We've seen how DDD can help us to separate the business logic of the application from the data access logic, and how this separation of concerns can make the code more maintainable and testable. We've also seen how the repository and worker patterns can be implemented in a &lt;strong&gt;flama&lt;/strong&gt; application, and how they can be used to encapsulate the data access logic and provide a way to group all the operations that must be performed on the data source within a single transaction. Finally, we've seen how the resource methods can be used to define the API endpoints that interact with the user data, and how the DDD pattern can be used to implement the business requirements we were given at the beginning of this example.&lt;/p&gt;

&lt;p&gt;Although the sign-in process we've described here is not entirely realistic, you could combine the material of this and a previous post on JWT authentication to implement a more realistic process, in which the sign-in ends up returning a JWT token. If you're interested in this, you can check out the post on &lt;a href="https://dev.to/vortico/protected-ml-apis-with-flama-jwt-authentication-3emn"&gt;JWT authentication with &lt;strong&gt;flama&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We hope you've found this post useful, and that you're now ready to implement DDD in your own &lt;strong&gt;flama&lt;/strong&gt; applications. If you have any questions or comments, feel free to reach out to us. We're always happy to help!&lt;/p&gt;

&lt;p&gt;Stay tuned for more posts on &lt;strong&gt;flama&lt;/strong&gt; and other exciting topics in the world of AI and software development. Until next time!&lt;/p&gt;

&lt;h2&gt;
  
  
  Support our work
&lt;/h2&gt;

&lt;p&gt;If you like what we do, there is a free and easy way to support our work. Gifts us a ⭐ at &lt;a href="https://github.com/vortico/flama" rel="noopener noreferrer"&gt;Flama&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;GitHub ⭐'s mean a world to us, and give us the sweetest fuel to keep working on it to help others on its journey to build robust Machine Learning APIs. &lt;/p&gt;

&lt;p&gt;You can also follow us on &lt;a href="https://x.com/VorticoTech" rel="noopener noreferrer"&gt;𝕏&lt;/a&gt;, where we share our latest news and updates, besides interesting threads on AI, software development, and much more.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://flama.dev/docs/" rel="noopener noreferrer"&gt;Flama documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vortico/flama" rel="noopener noreferrer"&gt;Flama GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/flama/" rel="noopener noreferrer"&gt;Flama PyPI package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  About the authors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vortico.tech/" rel="noopener noreferrer"&gt;Vortico&lt;/a&gt;: We're specialised in software development to help businesses enhance and expand their AI and technology capabilities.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flama</category>
      <category>api</category>
      <category>python</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Protected ML APIs with Flama JWT Authentication</title>
      <dc:creator>Vortico</dc:creator>
      <pubDate>Wed, 11 Sep 2024 08:30:34 +0000</pubDate>
      <link>https://dev.to/vortico/protected-ml-apis-with-flama-jwt-authentication-3emn</link>
      <guid>https://dev.to/vortico/protected-ml-apis-with-flama-jwt-authentication-3emn</guid>
      <description>&lt;p&gt;You've probably heard about the recent release of &lt;a href="https://dev.to/vortico/releasing-flama-17-3n78"&gt;Flama 1.7&lt;/a&gt; already, which brought some exciting new features to help you with the development and productionalisation of your ML APIs. This post is precisely devoted to one of the main highlights of that release: &lt;strong&gt;Support for JWT Authentication&lt;/strong&gt;. But, before we dive into the details with a hands-on example, we recommend you to bear in mind the following resources (and, get familiar with them if you haven't already):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Official Flama documentation: &lt;a href="https://flama.dev/docs/" rel="noopener noreferrer"&gt;Flama documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Post introducing Flama for ML APIs: &lt;a href="https://dev.to/vortico/introducing-flama-for-robust-machine-learning-apis-b3n"&gt;Introduction to Flama for Robust Machine Learning APIs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, let's get started with the new feature and see how you can secure your API endpoints with token-based authentication over headers or cookies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;p&gt;This post is structured as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is JSON Web Token (JWT)?&lt;/li&gt;
&lt;li&gt;
JWT Authentication with Flama

&lt;ul&gt;
&lt;li&gt;Set up the development environment&lt;/li&gt;
&lt;li&gt;Base application&lt;/li&gt;
&lt;li&gt;Flama JWT Component&lt;/li&gt;
&lt;li&gt;Protected endpoints&lt;/li&gt;
&lt;li&gt;Running the application&lt;/li&gt;
&lt;li&gt;Login endpoint&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusions&lt;/li&gt;

&lt;li&gt;Support our work&lt;/li&gt;

&lt;li&gt;References&lt;/li&gt;

&lt;li&gt;About the authors&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is JSON Web Token?
&lt;/h2&gt;

&lt;p&gt;If you're already familiar with the concept of JSON Web Token (JWT) and how it works, feel free to skip this section and jump straight to Implementing JWT Authentication with Flama. Otherwise, we're going to try to provide you with a succinct explanation of what JWT is and why it's so useful for authorisation purposes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Brief introduction
&lt;/h3&gt;

&lt;p&gt;We can start with the official definition given in the &lt;a href="https://datatracker.ietf.org/doc/html/rfc7519" rel="noopener noreferrer"&gt;RFC 7519&lt;/a&gt; document:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;JSON Web Token (JWT) is a compact, URL-safe means of representing&lt;br&gt;
   claims to be transferred between two parties.  The claims in a JWT&lt;br&gt;
   are encoded as a JSON object that is used as the payload of a JSON&lt;br&gt;
   Web Signature (JWS) structure or as the plaintext of a JSON Web&lt;br&gt;
   Encryption (JWE) structure, enabling the claims to be digitally&lt;br&gt;
   signed or integrity protected with a Message Authentication Code&lt;br&gt;
   (MAC) and/or encrypted.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, in simple terms, JWT is an open standard that defines a way for transmitting information between two parties in the form of a JSON object. The information being transmitted is digitally signed to ensure its integrity and authenticity. This is why one of the main use cases of JWT is for authorisation purposes. &lt;/p&gt;

&lt;p&gt;A prototypical JWT-based authentication flow would look something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user logs in to a system and receives a JWT token.&lt;/li&gt;
&lt;li&gt;The user sends this token with every subsequent request to the server.&lt;/li&gt;
&lt;li&gt;The server verifies the token and grants access to the requested resource if the token is valid.&lt;/li&gt;
&lt;li&gt;If the token is invalid, the server denies access to the resource.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, JWT tokens are not only useful to identify users, but they can also be used to share information between different services in a secure way via the payload of the token. Besides, given that the signature of the token is calculated using the header and the payload, the receiver can verify the integrity of the token and ensure that it hasn't been altered in transit.&lt;/p&gt;

&lt;h3&gt;
  
  
  JWT Structure
&lt;/h3&gt;

&lt;p&gt;A JWT is represented as a sequence of URL-safe parts separated by dots (&lt;code&gt;.&lt;/code&gt;), with each part containing a base64url-encoded JSON object. The number of parts in the JWT can vary depending on the representation being used, either JWS (JSON Web Signature) &lt;a href="https://datatracker.ietf.org/doc/html/rfc7515" rel="noopener noreferrer"&gt;RFC-7515&lt;/a&gt; or JWE (JSON Web Encryption) &lt;a href="https://datatracker.ietf.org/doc/html/rfc7516" rel="noopener noreferrer"&gt;RFC-7516&lt;/a&gt;. &lt;br&gt;
As of this point, we can assume we'll be using JWS, which is the most common representation for JWT tokens. In this case, a JWT token consists of three parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Header&lt;/strong&gt;: Contains metadata about the token and the cryptographic operations applied to it. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payload&lt;/strong&gt;: Contains the claims (information) that the token is carrying.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signature&lt;/strong&gt;: Ensures the integrity of the token and is used to verify that the sender of the token is who it claims to be.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thus, the syntax of a prototypical JWS JSON using the flattened JWS JSON Serialization is as follows (for more info, see &lt;a href="https://datatracker.ietf.org/doc/html/rfc7515#section-7.2.2" rel="noopener noreferrer"&gt;RFC-7515 Sec. 7.2.2&lt;/a&gt;):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
   "payload":"&amp;lt;payload contents&amp;gt;",
   "header":&amp;lt;header contents&amp;gt;,
   "signature":"&amp;lt;signature contents&amp;gt;"
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the JWS Compact Serialization, the JTW is represented as the  concatenation:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

BASE64URL(UTF8(JWS Header)) || '.' || BASE64URL(JWS Payload) || '.' || BASE64URL(JWS Signature)  


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;An example of a JWT token would look like this (taken from one of the Flama tests &lt;a href="https://github.com/vortico/flama/blob/7b79ae4b38efefc0fb02223a4a2fcb13837283af/tests/authentication/test_jwt.py#L11" rel="noopener noreferrer"&gt;here&lt;/a&gt;):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJkYXRhIjogeyJmb28iOiAiYmFyIn0sICJpYXQiOiAwfQ==.J3zdedMZSFNOimstjJat0V28rM_b1UU62XCp9dg_5kg=


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  The Header
&lt;/h4&gt;

&lt;p&gt;For a general JWT object, the header can contain a variety of fields (e.g., describing the cryptographic operations applied to the token) depending on whether the JWT is a JWS or JWE. However, there are some fields which are common to both cases, and also the most commonly used: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;alg&lt;/code&gt;&lt;/strong&gt;: The algorithm used to sign the token, e.g. &lt;code&gt;HS256&lt;/code&gt;, &lt;code&gt;HS384&lt;/code&gt;, &lt;code&gt;HS512&lt;/code&gt;. To see the list of supported algorithms in &lt;strong&gt;flama&lt;/strong&gt;, have a look at it &lt;a href="https://github.com/vortico/flama/blob/7b79ae4b38efefc0fb02223a4a2fcb13837283af/flama/authentication/jwt/jws.py#L25" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;typ&lt;/code&gt;&lt;/strong&gt;: The type parameter is used to declare the media type of the JWT. If present, it is recommended to have the value &lt;code&gt;JWT&lt;/code&gt; to indicate that the object is a JWT. For more information on this field, see &lt;a href="https://datatracker.ietf.org/doc/html/rfc7519#section-5.1" rel="noopener noreferrer"&gt;RFC-7519 Sec. 5.1&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cty&lt;/code&gt;&lt;/strong&gt;: The content type is used to communicate structural information about the JWT. For example, if the JWT is a Nested JWT, the value of this field would be &lt;code&gt;JWT&lt;/code&gt;. For more information on this field, see &lt;a href="https://datatracker.ietf.org/doc/html/rfc7519#section-5.2" rel="noopener noreferrer"&gt;RFC-7519 Sec. 5.2&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  The Payload
&lt;/h4&gt;

&lt;p&gt;The payload contains the claims (information) that the token is carrying. The claims are represented as a JSON object, and they can be divided into three categories, according to the standard &lt;a href="https://datatracker.ietf.org/doc/html/rfc7519#section-4" rel="noopener noreferrer"&gt;RFC-7519 Sec. 4&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Registered claims&lt;/strong&gt;: These are a set of predefined claims that are not mandatory but recommended. Some of the most common ones are &lt;code&gt;iss&lt;/code&gt; (issuer), &lt;code&gt;sub&lt;/code&gt; (subject), &lt;code&gt;aud&lt;/code&gt; (audience), &lt;code&gt;exp&lt;/code&gt; (expiration time), &lt;code&gt;nbf&lt;/code&gt; (not before), &lt;code&gt;iat&lt;/code&gt; (issued at), and &lt;code&gt;jti&lt;/code&gt; (JWT ID). For a detailed explanation of each of these claims, see &lt;a href="https://datatracker.ietf.org/doc/html/rfc7519#section-4.1" rel="noopener noreferrer"&gt;RFC-7519 Sec. 4.1&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public claims&lt;/strong&gt;: These are claims that are defined by the user and can be used to share information between different services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private claims&lt;/strong&gt;: These are claims that are defined by the user and are intended to be shared between the parties that are involved in the communication.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  The Signature
&lt;/h4&gt;

&lt;p&gt;The signature is used to ensure the integrity of the token and to verify that the sender of the token is who it claims to be. The signature is calculated using the header and the payload, and it is generated using the algorithm specified in the header. The signature is then appended to the token to create the final JWT.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementing JWT Authentication with Flama
&lt;/h2&gt;

&lt;p&gt;Having introduced the concept of JWT, its potential applications, besides a prototypical authentication flow, we can now move on to the implementation of a JWT-authenticated API using Flama. But, before we start, if you need to review the basics on how to create a simple API with &lt;strong&gt;flama&lt;/strong&gt;, or how to run the API once you already have the code ready, then you might want to check out the &lt;a href="https://flama.dev/docs/getting-started/quickstart/" rel="noopener noreferrer"&gt;quick start guide&lt;/a&gt;. There, you'll find the fundamental concepts and steps required to follow this post. Now, without further ado, let's get started with the implementation.&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting up the development environment
&lt;/h3&gt;

&lt;p&gt;Our first step is to create our development environment, and install all required dependencies for this project. The good thing is that for this example we only need to install &lt;strong&gt;flama&lt;/strong&gt; to have all the necessary tools to implement JWT authentication. We'll be using &lt;a href="https://python-poetry.org/" rel="noopener noreferrer"&gt;&lt;code&gt;poetry&lt;/code&gt;&lt;/a&gt; to manage our dependencies, but you can also use &lt;code&gt;pip&lt;/code&gt; if you prefer:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

poetry add flama[full]


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you want to know how we typically organise our projects, have a look at our previous post &lt;a href="https://dev.to/vortico/introducing-flama-for-robust-machine-learning-apis-b3n#development-environment"&gt;here&lt;/a&gt;, where we explain in detail how to set up a python project with &lt;code&gt;poetry&lt;/code&gt;, and the project folder structure we usually follow. &lt;/p&gt;

&lt;h3&gt;
  
  
  Base application
&lt;/h3&gt;

&lt;p&gt;Let's start with a simple application that has a single public endpoint. This endpoint will return a brief description of the API.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flama&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flama&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JWT protected API&lt;/span&gt;&lt;span class="sh"&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;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JWT Authentication with Flama 🔥&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/docs/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/public/info/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public-info&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    tags:
        - Public
    summary:
        Ping
    description:
        Returns a brief description of the API
    responses:
        200:
            description:
                Successful ping.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you want to run this application, you can save the code above in a file called &lt;code&gt;main.py&lt;/code&gt; under the &lt;code&gt;src&lt;/code&gt; folder, and then run the following command (remember to have the &lt;code&gt;poetry&lt;/code&gt; environment activated, otherwise you'll need to prefix the command with &lt;code&gt;poetry run&lt;/code&gt;):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

flama run &lt;span class="nt"&gt;--server-reload&lt;/span&gt; src.main:app

INFO:     Started server process &lt;span class="o"&gt;[&lt;/span&gt;3267]
INFO:     Waiting &lt;span class="k"&gt;for &lt;/span&gt;application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 &lt;span class="o"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span class="o"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;where the &lt;code&gt;--server-reload&lt;/code&gt; flag is optional and is used to reload the server automatically when the code changes. This is very useful during development, but you can remove it if you don't need it. For a full list of the available options, you can run &lt;code&gt;flama run --help&lt;/code&gt;, or check the &lt;a href="https://flama.dev/docs/flama-cli/run/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication: Flama JWT Component
&lt;/h3&gt;

&lt;p&gt;Ok, now that we have our base application running, let's add a new endpoint that requires authentication. To do this, we'll need to use the following &lt;strong&gt;flama&lt;/strong&gt; functionality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Components (specifically &lt;code&gt;JWTComponent&lt;/code&gt;): They're the building blocks for dependency injection in &lt;strong&gt;flama&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Middlewares (specifically &lt;code&gt;AuthenticationMiddleware&lt;/code&gt;): They're used as a processing layer between the incoming requests from clients and the responses sent by the server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's proceed and modify our base application to include the JWT authentication as intended, and then we'll explain the code in more detail.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flama&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.authentication&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AuthenticationMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;JWTComponent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.middleware&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Middleware&lt;/span&gt;

&lt;span class="n"&gt;JWT_SECRET_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&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="nb"&gt;bytes&lt;/span&gt;  &lt;span class="c1"&gt;# The secret key used to signed the token
&lt;/span&gt;&lt;span class="n"&gt;JWT_HEADER_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Authorization header identifie
&lt;/span&gt;&lt;span class="n"&gt;JWT_HEADER_PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Bearer prefix
&lt;/span&gt;&lt;span class="n"&gt;JWT_ALGORITHM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HS256&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Algorithm used to sign the token
&lt;/span&gt;&lt;span class="n"&gt;JWT_TOKEN_EXPIRATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;  &lt;span class="c1"&gt;# 5 minutes in seconds
&lt;/span&gt;&lt;span class="n"&gt;JWT_REFRESH_EXPIRATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2592000&lt;/span&gt;  &lt;span class="c1"&gt;# 30 days in seconds
&lt;/span&gt;&lt;span class="n"&gt;JWT_ACCESS_COOKIE_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;access_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;JWT_REFRESH_COOKIE_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;refresh_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flama&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JWT-protected API&lt;/span&gt;&lt;span class="sh"&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;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JWT Authentication with Flama 🔥&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/docs/&lt;/span&gt;&lt;span class="sh"&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;JWTComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;JWT_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;header_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;JWT_HEADER_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;header_prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;JWT_HEADER_PREFIX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;cookie_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;JWT_ACCESS_COOKIE_KEY&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;Middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AuthenticationMiddleware&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# The same code as before here
# ...
&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Although we've decided to add the component and middleware after the initialisation of the &lt;code&gt;app&lt;/code&gt;, you can also add them directly to the &lt;code&gt;Flama&lt;/code&gt; constructor by passing the &lt;code&gt;components&lt;/code&gt; and &lt;code&gt;middlewares&lt;/code&gt; arguments:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flama&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JWT-protected API&lt;/span&gt;&lt;span class="sh"&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;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JWT Authentication with Flama 🔥&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/docs/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;JWTComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;JWT_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;header_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;JWT_HEADER_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;header_prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;JWT_HEADER_PREFIX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;cookie_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;JWT_ACCESS_COOKIE_KEY&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;middlewares&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;Middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AuthenticationMiddleware&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;This is just a matter of preference, and both ways are completely valid. &lt;/p&gt;

&lt;p&gt;With the modifications introduced above, we can proceed to add a new (and JWT protected) endpoint. However, before we do that, let's briefly explain in more detail the functionality we've just introduced, namely components and middlewares.&lt;/p&gt;

&lt;h4&gt;
  
  
  Flama Components
&lt;/h4&gt;

&lt;p&gt;As you might've already noticed, whenever we create a new application we're instantiating a &lt;code&gt;Flama&lt;/code&gt; object. As the application grows, as is the case right now, also grows the need to add more dependencies to it. Without &lt;a href="https://en.wikipedia.org/wiki/Dependency_injection" rel="noopener noreferrer"&gt;dependency injection&lt;/a&gt; (DI), this would mean that the &lt;code&gt;Flama&lt;/code&gt; class would have to create and manage all its dependencies internally. This would make the class tightly coupled to specific implementations and harder to test or modify. With DI the dependencies are provided to the class from the outside, which decouples the class from specific implementations, making it more flexible and easier to test. And, this is where components come into play in &lt;strong&gt;flama&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;flama&lt;/strong&gt;, a Component is a building block for dependency injection. It encapsulates logic that determines how specific dependencies should be resolved when the application runs. Components can be thought of as self-contained units responsible for providing the necessary resources that the application's constituents need. Each Component in &lt;strong&gt;flama&lt;/strong&gt; has a unique identity, making it easy to look up and inject the correct dependency during execution. Components are highly flexible, allowing you to handle various types of dependencies, whether they're simple data types, complex objects, or asynchronous operations. There are several built-in components in &lt;strong&gt;flama&lt;/strong&gt;, although in this post we're going to exclusively focus on the &lt;code&gt;JWTComponent&lt;/code&gt;, which (as all others) derives from the &lt;a href="https://github.com/vortico/flama/blob/0ba81734bd56d74e728b9edfc2a8516bbf5e45ee/flama/injection/components.py#L12" rel="noopener noreferrer"&gt;&lt;code&gt;Component&lt;/code&gt;&lt;/a&gt; class. &lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/vortico/flama/blob/0ba81734bd56d74e728b9edfc2a8516bbf5e45ee/flama/authentication/components.py#L15" rel="noopener noreferrer"&gt;&lt;code&gt;JWTComponent&lt;/code&gt;&lt;/a&gt; contains all the information and logic necessary for extracting a JWT from either the headers or cookies of an incoming request, decoding it, and then validating its authenticity. The component is initialised with the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;secret&lt;/code&gt;: The secret key used to decode the JWT.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;header_key&lt;/code&gt;: The key used to identify the JWT in the request headers.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;header_prefix&lt;/code&gt;: The prefix used to identify the JWT in the request headers.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cookie_key&lt;/code&gt;: The key used to identify the JWT in the request cookies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the code above, we've initialised the &lt;code&gt;JWTComponent&lt;/code&gt; with some dummy values for the secret key, header key, header prefix, and cookie key. In a real-world scenario, you should replace these values with your own secret key and identifiers.&lt;/p&gt;

&lt;h4&gt;
  
  
  Flama Middlewares
&lt;/h4&gt;

&lt;p&gt;Middleware is a crucial concept that acts as a processing layer between the incoming requests from clients and the responses sent by the server. In simpler terms, middleware functions as a gatekeeper or intermediary that can inspect, modify, or act on requests before they reach the core logic of your application, and also on the responses before they are sent back to the client.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;flama&lt;/strong&gt;, middleware is used to handle various tasks that need to occur before a request is processed or after a response is generated. In this particular case, the task we want to handle is the authentication of incoming requests using JWT. To achieve this, we're going to use the built-in class &lt;a href="https://github.com/vortico/flama/blob/0ba81734bd56d74e728b9edfc2a8516bbf5e45ee/flama/authentication/middlewares.py#L20" rel="noopener noreferrer"&gt;&lt;code&gt;AuthenticationMiddleware&lt;/code&gt;&lt;/a&gt;. This middleware is designed to ensure that only authorised users can access certain parts of your application. It works by intercepting incoming requests, checking for the necessary credentials (such as a valid JWT token), and then allowing or denying access based on the user's permissions which are encoded in the token.&lt;/p&gt;

&lt;p&gt;Here’s how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initialization: The middleware is initialized with the &lt;code&gt;Flama&lt;/code&gt; application instance. This ties the middleware to your app, so it can interact with incoming requests and outgoing responses.&lt;/li&gt;
&lt;li&gt;Handling Requests: The &lt;code&gt;__call__&lt;/code&gt; method of the middleware is called every time a request is made to your application. If the request type is http or websocket, the middleware checks if the route (the path the request is trying to access) requires any specific permissions.&lt;/li&gt;
&lt;li&gt;Permission Check: The middleware extracts the required permissions for the route from the route's tags. If no permissions are required, the request is passed on to the next layer of the application. If permissions are required, the middleware attempts to resolve a JWT token from the request. This is done using the previously discussed &lt;code&gt;JWTComponent&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Validating Permissions: Once the JWT token is resolved, the middleware checks the permissions associated with the token against those required by the route. If the user’s permissions match or exceed the required permissions, the request is allowed to proceed. Otherwise, the middleware returns a &lt;code&gt;403 Forbidden&lt;/code&gt; response, indicating that the user does not have sufficient permissions.&lt;/li&gt;
&lt;li&gt;Handling Errors: If the JWT token is invalid or cannot be resolved, the middleware catches the exception and returns an appropriate error response, such as &lt;code&gt;401 Unauthorized&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Adding a protected endpoint
&lt;/h3&gt;

&lt;p&gt;By now, we should have a pretty solid understanding on what our code does, and how it does it. Nevertheless, we still need to see what we have to do to add a protected endpoint to our application. Let's do it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/private/info/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;private-info&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;permissions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-permission-name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;protected_info&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    tags:
        - Private
    summary:
        Ping
    description:
        Returns a brief description of the API
    responses:
        200:
            description:
                Successful ping.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And that's it! We've added a new endpoint that requires authentication to access. The functionality is exactly the same as the previous endpoint, but this time we've added the &lt;code&gt;tags&lt;/code&gt; parameter to the &lt;code&gt;@app.get&lt;/code&gt; decorator. The tag parameter can be used to specify additional metadata for an endpoint. But, if we use the special key &lt;code&gt;permissions&lt;/code&gt; whilst using the &lt;code&gt;AuthenticationMiddleware&lt;/code&gt;, we can specify the permissions required to access the endpoint. In this case, we've set the permissions to &lt;code&gt;["my-permission-name"]&lt;/code&gt;, which means that only users with the permission &lt;code&gt;my-permission-name&lt;/code&gt; will be able to access this endpoint. If a user tries to access the endpoint without the required permission, they will receive a &lt;code&gt;403 Forbidden&lt;/code&gt; response.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running the application
&lt;/h3&gt;

&lt;p&gt;If we put all the pieces together, we should have a fully functional application that has a public endpoint and a private endpoint that requires authentication. The full code should look something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flama&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.authentication&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AuthenticationMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;JWTComponent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.middleware&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Middleware&lt;/span&gt;

&lt;span class="n"&gt;JWT_SECRET_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&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="nb"&gt;bytes&lt;/span&gt;  &lt;span class="c1"&gt;# The secret key used to signed the token
&lt;/span&gt;&lt;span class="n"&gt;JWT_HEADER_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Authorization header identifie
&lt;/span&gt;&lt;span class="n"&gt;JWT_HEADER_PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Bearer prefix
&lt;/span&gt;&lt;span class="n"&gt;JWT_ALGORITHM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HS256&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Algorithm used to sign the token
&lt;/span&gt;&lt;span class="n"&gt;JWT_TOKEN_EXPIRATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;  &lt;span class="c1"&gt;# 5 minutes in seconds
&lt;/span&gt;&lt;span class="n"&gt;JWT_REFRESH_EXPIRATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2592000&lt;/span&gt;  &lt;span class="c1"&gt;# 30 days in seconds
&lt;/span&gt;&lt;span class="n"&gt;JWT_ACCESS_COOKIE_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;access_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;JWT_REFRESH_COOKIE_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;refresh_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flama&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JWT-protected API&lt;/span&gt;&lt;span class="sh"&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;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JWT Authentication with Flama 🔥&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/docs/&lt;/span&gt;&lt;span class="sh"&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;JWTComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;JWT_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;header_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;JWT_HEADER_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;header_prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;JWT_HEADER_PREFIX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;cookie_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;JWT_ACCESS_COOKIE_KEY&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;Middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AuthenticationMiddleware&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/public/info/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public-info&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    tags:
        - Public
    summary:
        Info
    description:
        Returns a brief description of the API
    responses:
        200:
            description:
                Successful ping.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/private/info/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;private-info&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;permissions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-permission-name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;protected_info&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    tags:
        - Private
    summary:
        Info
    description:
        Returns a brief description of the API
    responses:
        200:
            description:
                Successful ping.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Running the application as before, we should see the following output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

flama run &lt;span class="nt"&gt;--server-reload&lt;/span&gt; src.main:app

INFO:     Uvicorn running on http://127.0.0.1:8000 &lt;span class="o"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span class="o"&gt;)&lt;/span&gt;
INFO:     Started reloader process &lt;span class="o"&gt;[&lt;/span&gt;48145] using WatchFiles
INFO:     Started server process &lt;span class="o"&gt;[&lt;/span&gt;48149]
INFO:     Waiting &lt;span class="k"&gt;for &lt;/span&gt;application startup.
INFO:     Application startup complete.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If we navigate with our favourite browser to &lt;code&gt;http://127.0.0.1:8000/docs/&lt;/code&gt; we should see the documentation of our API as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F4ioc1zi9dqrok6zhu2zh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F4ioc1zi9dqrok6zhu2zh.png" alt="Flama JWT Authentication"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we can already expect, if we send a request to the public endpoint &lt;code&gt;/public/info/&lt;/code&gt; we should receive a successful response:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:8000/public/info/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/json'&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;:&lt;span class="s2"&gt;"JWT protected API"&lt;/span&gt;,&lt;span class="s2"&gt;"description"&lt;/span&gt;:&lt;span class="s2"&gt;"JWT Authentication with Flama 🔥"&lt;/span&gt;,&lt;span class="s2"&gt;"public"&lt;/span&gt;:true&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;However, if we try to access the private endpoint &lt;code&gt;/private/info/&lt;/code&gt; without providing a valid JWT token, we should receive a &lt;code&gt;401 Unauthorized&lt;/code&gt; response:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:8000/private/info/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/json'&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"status_code"&lt;/span&gt;:401,&lt;span class="s2"&gt;"detail"&lt;/span&gt;:&lt;span class="s2"&gt;"Unauthorized"&lt;/span&gt;,&lt;span class="s2"&gt;"error"&lt;/span&gt;:null&lt;span class="o"&gt;}&lt;/span&gt;% 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Thus, we can say that the JWT authentication is working as expected, and only users with a valid JWT token will be able to access the private endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding the login endpoint
&lt;/h3&gt;

&lt;p&gt;As we've just seen, we have a private endpoint which requires a valid JWT token to be accessed. But, how do we get this token? The answer is simple: we need to create a login endpoint that will authenticate the user and return a valid JWT token. To do this, we're going to define the schemas for the input and output of the login endpoint (feel free to use your own schemas if you prefer, for instance, adding more fields to the input schema such as &lt;code&gt;username&lt;/code&gt; or &lt;code&gt;email&lt;/code&gt;):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pydantic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UUID&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pydantic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UUID&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, let's add the login endpoint to our application:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.authentication.jwt&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;JWT&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;APIResponse&lt;/span&gt;

&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/auth/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;signin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;User&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;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;UserToken&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    tags:
        - Public
    summary:
        Authenticate
    description:
        Returns a user token to access protected endpoints
    responses:
        200:
            description:
                Successful ping.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;JWT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;JWT_ALGORITHM&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vortico&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;permissions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-permission-name&lt;/span&gt;&lt;span class="sh"&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="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JWT_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&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="nc"&gt;APIResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPStatus&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="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;UserToken&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;token&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 this code snippet, we've defined a new endpoint &lt;code&gt;/auth/&lt;/code&gt; that receives a &lt;code&gt;User&lt;/code&gt; object as input and returns a &lt;code&gt;UserToken&lt;/code&gt; object as output. The &lt;code&gt;User&lt;/code&gt; object contains the &lt;code&gt;id&lt;/code&gt; of the user and the &lt;code&gt;password&lt;/code&gt; (which is optional for this example, since we're not going to be comparing it with any stored password). The &lt;code&gt;UserToken&lt;/code&gt; object contains the &lt;code&gt;id&lt;/code&gt; of the user and the generated &lt;code&gt;token&lt;/code&gt; that will be used to authenticate the user in the private endpoints. The &lt;code&gt;token&lt;/code&gt; is generated using the &lt;code&gt;JWT&lt;/code&gt; class, and contains the permissions granted to the user, in this case, the permission &lt;code&gt;my-permission-name&lt;/code&gt;, which will allow the user to access the private endpoint &lt;code&gt;/private/info/&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Now, let's test the login endpoint to see if it's working as expected. For this, we can proceed via the &lt;code&gt;/docs/&lt;/code&gt; page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fg6ykli853f9zjypcwcdy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fg6ykli853f9zjypcwcdy.png" alt="Flama JWT Authentication"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or, we can use &lt;code&gt;curl&lt;/code&gt; to send a request to the login endpoint:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:8000/auth/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
  "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  "password": "string"
}'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;which should return a response similar to this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:&lt;span class="s2"&gt;"497f6eca-6276-4993-bfeb-53cbbbba6f08"&lt;/span&gt;,&lt;span class="s2"&gt;"token"&lt;/span&gt;:&lt;span class="s2"&gt;"eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJkYXRhIjogeyJpZCI6ICI0OTdmNmVjYS02Mjc2LTQ5OTMtYmZlYi01M2NiYmJiYTZmMDgiLCAicGVybWlzc2lvbnMiOiBbIm15LXBlcm1pc3Npb24tbmFtZSJdfSwgImlhdCI6IDE3MjU5ODk5NzQsICJpc3MiOiAidm9ydGljbyJ9.vwwgqahgtALckMAzQHWpNDNwhS8E4KAGwNiFcqEZ_04="&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That string is the JWT token that we can use to authenticate the user in the private endpoint. Let's try to access the private endpoint using the token:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:8000/private/info/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Authorization: Bearer eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJkYXRhIjogeyJpZCI6ICI0OTdmNmVjYS02Mjc2LTQ5OTMtYmZlYi01M2NiYmJiYTZmMDgiLCAicGVybWlzc2lvbnMiOiBbIm15LXBlcm1pc3Npb24tbmFtZSJdfSwgImlhdCI6IDE3MjU5ODk5NzQsICJpc3MiOiAidm9ydGljbyJ9.vwwgqahgtALckMAzQHWpNDNwhS8E4KAGwNiFcqEZ_04='&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;which now returns a successful response:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;:&lt;span class="s2"&gt;"JWT protected API"&lt;/span&gt;,&lt;span class="s2"&gt;"description"&lt;/span&gt;:&lt;span class="s2"&gt;"JWT Authentication with Flama 🔥"&lt;/span&gt;,&lt;span class="s2"&gt;"public"&lt;/span&gt;:false&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And that's it! We've successfully implemented JWT authentication in our &lt;code&gt;Flama&lt;/code&gt; application. We now have a public endpoint that can be accessed without authentication, a private endpoint that requires a valid JWT token to be accessed, and a login endpoint that generates a valid JWT token for the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;The privatisation of some endpoints (even all at some instances) is a common requirement in many applications, even more so when dealing with sensitive data as is often the case in Machine Learning APIs which process personal or financial information, to name some examples. In this post, we've covered the fundamentals of token-based authentication for APIs, and how this can be implemented without much of a hassle using the new features introduced in the latest release of &lt;strong&gt;flama&lt;/strong&gt; (introduced in a previous &lt;a href="https://dev.to/vortico/releasing-flama-17-3n78"&gt;post&lt;/a&gt;). Thanks to the &lt;code&gt;JWTComponent&lt;/code&gt; and &lt;code&gt;AuthenticationMiddleware&lt;/code&gt;, we can secure our API endpoints and control the access to them based on the permissions granted to the user, and all this with just a few modifications to our base &lt;strong&gt;unprotected&lt;/strong&gt; application. &lt;/p&gt;

&lt;p&gt;We hope you've found this post useful, and that you're now ready to implement JWT authentication in your own &lt;strong&gt;flama&lt;/strong&gt; applications. If you have any questions or comments, feel free to reach out to us. We're always happy to help!&lt;/p&gt;

&lt;p&gt;Stay tuned for more posts on &lt;strong&gt;flama&lt;/strong&gt; and other exciting topics in the world of AI and software development. Until next time!&lt;/p&gt;

&lt;h2&gt;
  
  
  Support our work
&lt;/h2&gt;

&lt;p&gt;If you like what we do, there is a free and easy way to support our work. Gift us a ⭐ at &lt;a href="https://github.com/vortico/flama" rel="noopener noreferrer"&gt;Flama&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;GitHub ⭐'s mean a world to us, and give us the sweetest fuel to keep working on it to help others on its journey to build robust Machine Learning APIs. &lt;/p&gt;

&lt;p&gt;You can also follow us on &lt;a href="https://x.com/VorticoTech" rel="noopener noreferrer"&gt;𝕏&lt;/a&gt;, where we share our latest news and updates, besides interesting threads on AI, software development, and much more.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://flama.dev/docs/" rel="noopener noreferrer"&gt;Flama documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vortico/flama" rel="noopener noreferrer"&gt;Flama GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/flama/" rel="noopener noreferrer"&gt;Flama PyPI package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  About the authors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vortico.tech/" rel="noopener noreferrer"&gt;Vortico&lt;/a&gt;: We're specialised in software development to help businesses enhance and expand their AI and technology capabilities.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flama</category>
      <category>machinelearning</category>
      <category>api</category>
      <category>python</category>
    </item>
    <item>
      <title>Releasing Flama 1.7</title>
      <dc:creator>Vortico</dc:creator>
      <pubDate>Wed, 04 Sep 2024 09:45:05 +0000</pubDate>
      <link>https://dev.to/vortico/releasing-flama-17-3n78</link>
      <guid>https://dev.to/vortico/releasing-flama-17-3n78</guid>
      <description>&lt;p&gt;We're back from a much-needed and relaxing summer break, and we're ready to kick off the fall with a new release of Flama. Yup, you read that right, we're releasing &lt;a href="https://github.com/vortico/flama/releases/tag/v1.7.1/" rel="noopener noreferrer"&gt;Flama 1.7&lt;/a&gt; 🎉&lt;/p&gt;

&lt;p&gt;This release is a big one, with new features that will make your life much easier when developing and productionalising your ML APIs. The main highlights of this release are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Support for Python 3.12&lt;/strong&gt;: Flama now supports Python 3.12, so you can take advantage of all the new features and improvements that come with the latest version of Python.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Support for Domain Driven Design (DDD)&lt;/strong&gt;: Flama now comes with built-in support for domain-driven design with a new module named &lt;strong&gt;&lt;code&gt;ddd&lt;/code&gt;&lt;/strong&gt;. DDD is a powerful approach that helps you manage the complexity of real-world projects, particularly when dealing with intricate business logic, and complex data models. By focusing on the business domain, DDD ensures that your codebase remains aligned with business needs, making it easier to maintain, extend, and scale over time. The new module &lt;strong&gt;&lt;code&gt;ddd&lt;/code&gt;&lt;/strong&gt; comes with the essential building blocks for you to hit the ground running at full speed. For a deeper understanding on DDD, you can check out the book &lt;a href="https://www.cosmicpython.com/book/preface.html" rel="noopener noreferrer"&gt;Architecture Patterns with Python&lt;/a&gt; by Harry Percival and Bob Gregory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Support for Authentication&lt;/strong&gt;: In this release, we've added support for authentication in Flama. You can now secure your API endpoints with token-based authentication over headers or cookies. This will help you protect your data and ensure that only authorised users can access your API, making it more secure and reliable in a few simple steps.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To better showcase these new features, we'll be publishing a couple of additional posts with highly detailed examples that will guide you through the process of using DDD and authentication with &lt;a href="https://flama.dev/" rel="noopener noreferrer"&gt;Flama&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Stay tuned for more updates and happy coding! 🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://flama.dev/docs/" rel="noopener noreferrer"&gt;Flama documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vortico/flama" rel="noopener noreferrer"&gt;Flama GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/flama/" rel="noopener noreferrer"&gt;Flama PyPI package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  About the authors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vortico.tech/" rel="noopener noreferrer"&gt;Vortico&lt;/a&gt;: We're specialised in software development to help businesses enhance and expand their AI and technology capabilities.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flama</category>
      <category>python</category>
      <category>machinelearning</category>
      <category>domaindriven</category>
    </item>
    <item>
      <title>Introducing Flama for Robust Machine Learning APIs</title>
      <dc:creator>Vortico</dc:creator>
      <pubDate>Mon, 18 Dec 2023 14:31:54 +0000</pubDate>
      <link>https://dev.to/vortico/introducing-flama-for-robust-machine-learning-apis-b3n</link>
      <guid>https://dev.to/vortico/introducing-flama-for-robust-machine-learning-apis-b3n</guid>
      <description>&lt;p&gt;Every time we use an application such as Instagram, Facebook, or Twitter, we are using an application programming interface (API) without even noticing it. APIs are everywhere, and they are the glue that connects the different services that we use every day in a seamless way.&lt;/p&gt;

&lt;p&gt;This is precisely the reason why APIs are so important for software development. APIs allow developers to encapsulate functionality and expose it in a standardised way, so that other systems can consume it without having to know the details of the implementation. This is what we call &lt;em&gt;abstraction&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For many years, APIs were mostly thought for exposing one of the following types of functionality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data manipulation&lt;/strong&gt;: APIs were also used to expose data, i.e. the information that is stored in a database. For example, the information about a user, or the information about a product.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Business logic&lt;/strong&gt;: APIs were also used to expose business logic, i.e. the rules that define how a business operates. For example, the rules that define how a user can buy a product, or the rules that define how a user can register in a system.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There has been a considerable effort in the last few years to try and standardise the way in which these type of APIs are implemented via different frameworks. However, over the last few years, a new type of functionality has become more and more popular: &lt;em&gt;machine learning (ML) models&lt;/em&gt;; and the existing frameworks for building APIs are not well suited for this type of functionality. In this series of posts, we will learn how to build APIs using a &lt;em&gt;Framework for the development of Lightweight Applications and Machine-learning Automation&lt;/em&gt;, also known as &lt;a href="https://flama.dev" rel="noopener noreferrer"&gt;Flama&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flama&lt;/strong&gt; is an open-source Python library which establishes a standard framework for development and deployment of APIs with special focus on ML products. The main aim of &lt;strong&gt;Flama&lt;/strong&gt; is to make ridiculously simple the deployment of ML APIs, simplifying (when possible) the entire process to a single line of code.&lt;/p&gt;

&lt;p&gt;In particular, in this first post we are going to learn how to build a simple API which exposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A simple &lt;em&gt;function&lt;/em&gt; which returns a string, to show how to expose business logic via an API.&lt;/li&gt;
&lt;li&gt;A simple &lt;em&gt;machine learning model&lt;/em&gt; (actually an ML pipeline) which returns a prediction given a certain input, to show how to expose ML models via an API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to do so, we will discuss the following topics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Setting up the development environment &lt;/li&gt;
&lt;li&gt;Creating the project structure&lt;/li&gt;
&lt;li&gt;Installing &lt;code&gt;flama&lt;/code&gt; and extra dependencies for ML&lt;/li&gt;
&lt;li&gt;Creating an ML pipeline with &lt;code&gt;scikit-learn&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Packaging the ML pipeline with &lt;code&gt;flama&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Serving the ML pipeline without code&lt;/li&gt;
&lt;li&gt;Interacting the ML pipeline without server&lt;/li&gt;
&lt;li&gt;Building an ML API using &lt;code&gt;flama&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will finish this post with a summary of the main takeaways, and a list of resources for further reading. Hope you enjoy it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Development environment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Python version
&lt;/h3&gt;

&lt;p&gt;When dealing with software development, reproducibility is key. This is why we encourage you to use Python virtual environments to set up an isolated environment for your project. Virtual environments allow the isolation of dependencies, which plays a crucial role to avoid breaking compatibility between different projects. We cannot cover all the details about virtual environments in this post, but we encourage you to learn more about &lt;a href="https://docs.python.org/3/library/venv.html#module-venv" rel="noopener noreferrer"&gt;venv&lt;/a&gt;, &lt;a href="https://github.com/pyenv/pyenv" rel="noopener noreferrer"&gt;pyenv&lt;/a&gt; or &lt;a href="https://docs.conda.io/en/latest/" rel="noopener noreferrer"&gt;conda&lt;/a&gt; for a better understanding on how to create and manage virtual environments.&lt;/p&gt;

&lt;p&gt;To create and activate a virtual environment with a compatible Python version you can take the following code snippets. Before you start working on a new project, it is very important to activate the corresponding environment. The last command line in both code snippets precisely activate the environment with &lt;strong&gt;pyenv&lt;/strong&gt; or &lt;strong&gt;conda&lt;/strong&gt;, respectively. &lt;/p&gt;

&lt;h4&gt;
  
  
  Creation of virtual environment with pyenv
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pyenv &lt;span class="nb"&gt;install &lt;/span&gt;3.11.5
pyenv virtualenv 3.11.5 flama-dev
pyenv &lt;span class="nb"&gt;local &lt;/span&gt;flama-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Creation of virtual environment with &lt;strong&gt;conda&lt;/strong&gt;
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;conda create &lt;span class="nt"&gt;--name&lt;/span&gt; flama-dev &lt;span class="nv"&gt;python&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3.11.5 &lt;span class="nt"&gt;--yes&lt;/span&gt;
conda activate flama-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Whilst you can use any tool to create and manage your virtual environments, we will be using &lt;strong&gt;pyenv&lt;/strong&gt; throughout this series of posts. This is because &lt;strong&gt;pyenv&lt;/strong&gt; is a lightweight tool which allows us to manage different Python versions and virtual environments in a very simple way, and it works great with &lt;strong&gt;poetry&lt;/strong&gt; (which we will discuss later).&lt;/p&gt;
&lt;h3&gt;
  
  
  Packaging and dependency management
&lt;/h3&gt;

&lt;p&gt;Once you have created and activated your virtual environment, you can start installing the dependencies for your project. Whenever we start a new project, we typically need to install dependencies which are not part of the Python standard library. These dependencies can be manually installed using &lt;code&gt;pip&lt;/code&gt; or &lt;code&gt;conda&lt;/code&gt; (as you probably know already), but that means we have to keep track of the dependencies and their versions manually! This is not only tedious, but also error-prone, because third-party dependencies usually have their own requirements, which eventually can conflict with the requirements of other dependencies. This leads to the so-called &lt;a href="https://en.wikipedia.org/wiki/Dependency_hell" rel="noopener noreferrer"&gt;dependency hell&lt;/a&gt;, which is practically impossible to solve manually. This is where depenedency management tools come into play. These tools allow us to declare the dependencies of our project in a file (or via a simple command line), and they take care of looking for the correct versions of the dependencies and installing them for us, keeping track of the dependencies and their versions automatically. &lt;/p&gt;

&lt;p&gt;We believe that &lt;a href="https://python-poetry.org/" rel="noopener noreferrer"&gt;poetry&lt;/a&gt; is currently the best tool for this purpose, besides of being the most popular one at the moment. This is why we will use &lt;strong&gt;poetry&lt;/strong&gt; to manage the dependencies of our project throughout this series of posts. Poetry allows you to declare the libraries your project depends on, and it will manage (install/update) them for you. Poetry also allows you to package your project into a distributable format and publish it to a repository, such as &lt;a href="https://pypi.org/" rel="noopener noreferrer"&gt;PyPI&lt;/a&gt;. We strongly recommend you to learn more about this tool by reading the official &lt;a href="https://python-poetry.org/docs/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Thus, the first thing we need to do is to install &lt;strong&gt;poetry&lt;/strong&gt; in our active virtual environment. Following the official &lt;a href="https://python-poetry.org/docs/#installing-with-the-official-installer" rel="noopener noreferrer"&gt;instructions&lt;/a&gt;, we can run the following command line:&lt;/p&gt;
&lt;h4&gt;
  
  
  Linux, macOS, Windows (WSL)
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; https://install.python-poetry.org | python3 -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Windows (PowerShell)
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;Invoke-WebRequest &lt;span class="nt"&gt;-Uri&lt;/span&gt; https://install.python-poetry.org &lt;span class="nt"&gt;-UseBasicParsing&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.Content | py -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Once installed, you should follow the official instructions on how to add poetry to your path if it is not already there. You can check this by running the following command line:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poetry &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Project structure
&lt;/h2&gt;

&lt;p&gt;Now that we have our development environment ready, we can start our project. For this, we need to create a new directory where our project will live. We will call this directory &lt;code&gt;flama_demo&lt;/code&gt; for simplicity, but feel free to use any name you prefer. The following command line will create the directory and move you into it, and will initialise a new &lt;strong&gt;poetry&lt;/strong&gt; project:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;flama_demo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;flama_demo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; poetry init &lt;span class="nt"&gt;--python&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"~=3.11.5"&lt;/span&gt; &lt;span class="nt"&gt;--no-interaction&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;touch &lt;/span&gt;README.md &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will create a &lt;code&gt;pyproject.toml&lt;/code&gt; file with the following information:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[tool.poetry]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"flama-demo"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
&lt;span class="py"&gt;authors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"YOUR USERNAME &amp;lt;your.user@email.com&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;readme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"README.md"&lt;/span&gt;

&lt;span class="nn"&gt;[tool.poetry.dependencies]&lt;/span&gt;
&lt;span class="py"&gt;python&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="py"&gt;"~&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.11&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;
&lt;span class="nn"&gt;[build-system]&lt;/span&gt;
&lt;span class="py"&gt;requires&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"poetry-core"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;build-backend&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"poetry.core.masonry.api"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Inspecting the folder &lt;code&gt;flama_demo&lt;/code&gt; you should see the following structure:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
├── README.md
├── pyproject.toml
└── src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With this, we have created a new &lt;strong&gt;poetry&lt;/strong&gt; project with the name &lt;code&gt;flama-demo&lt;/code&gt;, which depends on Python version &lt;code&gt;3.11.5&lt;/code&gt;. We have also created a &lt;code&gt;README.md&lt;/code&gt; file, which is a good practice to document your project. Finally, we have created a &lt;code&gt;src&lt;/code&gt; folder, which is where we will put the source code of our project.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installing dependencies
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Installing flama
&lt;/h3&gt;

&lt;p&gt;Now that we have set up our project folder, we can proceed and add the dependencies of our project. We will start by adding &lt;code&gt;flama&lt;/code&gt; as a dependency of our project. However, and before we do so, we need to know more about the extras that &lt;code&gt;flama&lt;/code&gt; provides. Indeed, when you install &lt;code&gt;flama&lt;/code&gt; you are installing the core functionality of the library, but there are some extra dependencies that you might need depending on the type of functionality you want to expose via your API. For example, &lt;code&gt;flama&lt;/code&gt; provides support for the following typed data schemas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pydantic-docs.helpmanual.io/" rel="noopener noreferrer"&gt;Pydantic&lt;/a&gt;: A library for data validation and settings management with support for type hints.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://typesystem.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;Typesystem&lt;/a&gt;: A comprehensive library for data validation typically used to define typed data schemas which provides data validation and object serialization &amp;amp; deserialization tools.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://marshmallow.readthedocs.io/en/stable/" rel="noopener noreferrer"&gt;Marshmallow&lt;/a&gt;: A library for data validation and serialization/deserialization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Besides, &lt;code&gt;flama&lt;/code&gt; also provides support for SQL databases via &lt;a href="https://www.sqlalchemy.org/" rel="noopener noreferrer"&gt;SQLAlchemy&lt;/a&gt;, an SQL toolkit and Object Relational Mapper that gives application developers the full power and flexibility of SQL. Finally, &lt;code&gt;flama&lt;/code&gt; also provides support for HTTP clients to perform requests via &lt;a href="https://www.python-httpx.org/" rel="noopener noreferrer"&gt;httpx&lt;/a&gt;, a next generation HTTP client for Python.&lt;/p&gt;

&lt;p&gt;These extras (you can check them &lt;a href="https://github.com/vortico/flama/blob/96d11508bacecb892547be56ea13d0dc565c4665/pyproject.toml#L49" rel="noopener noreferrer"&gt;here&lt;/a&gt;) are not installed by default when you install &lt;code&gt;flama&lt;/code&gt;, but can be installed by specifying them as extras. In our current example, we will install &lt;code&gt;flama&lt;/code&gt; with support for all the extras, so we will run the following command line:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poetry add &lt;span class="s2"&gt;"flama[full]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;which will add the following lines to our &lt;code&gt;pyproject.toml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[tool.poetry.dependencies]&lt;/span&gt;
&lt;span class="py"&gt;python&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="py"&gt;"~&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.11&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="py"&gt;flama&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="py"&gt;extras&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"full"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^1.6.0"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To learn more about the different extras contained in &lt;code&gt;flama&lt;/code&gt;, and how to pick up only those you need for a cleaner installation, you can check the &lt;a href="https://flama.dev/docs/getting-started/installation#extras" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Extra dependencies for ML
&lt;/h3&gt;

&lt;p&gt;In addition to the dependencies that &lt;code&gt;flama&lt;/code&gt; provides, we will also need to install some extra dependencies for our project. In particular, we will use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://scikit-learn.org/stable/" rel="noopener noreferrer"&gt;scikit-learn&lt;/a&gt;: A library for machine learning in Python&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://numpy.org/" rel="noopener noreferrer"&gt;numpy&lt;/a&gt;: A library for scientific computing in Python&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pandas.pydata.org/" rel="noopener noreferrer"&gt;pandas&lt;/a&gt;: A library for data analysis in Python&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arrow.apache.org/docs/python/" rel="noopener noreferrer"&gt;pyarrow&lt;/a&gt;: A library for efficient data interchange between Python and other languages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can install these dependencies by running the following command line:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poetry add scikit-learn numpy pandas pyarrow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With this, we should have something like this in our &lt;code&gt;pyproject.toml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[tool.poetry.dependencies]&lt;/span&gt;
&lt;span class="py"&gt;python&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="py"&gt;"~&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.11&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="py"&gt;flama&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="py"&gt;extras&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"full"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^1.6.0"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;scikit-learn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^1.3.2"&lt;/span&gt;
&lt;span class="py"&gt;numpy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^1.26.2"&lt;/span&gt;
&lt;span class="py"&gt;pandas&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^2.1.4"&lt;/span&gt;
&lt;span class="py"&gt;pyarrow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^14.0.1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You don't need to worry if your &lt;code&gt;pyproject.toml&lt;/code&gt; shows different versions for the dependencies, since this is exactly what &lt;strong&gt;poetry&lt;/strong&gt; is for: to find the correct versions of the dependencies at the time of installation (which might be different from the ones shown here) and keep track of them for you.&lt;/p&gt;

&lt;p&gt;With the needed dependencies already added to our project, we can proceed and install them in the active virtual environment by running the following command line:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poetry &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can check everything is correctly installed by running:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poetry run flama &lt;span class="nt"&gt;--version&lt;/span&gt;
Flama 1.6.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the following, we are going to assume you have the environment correctly set up, hence it is up to you to run commands in the active virtual environment via &lt;code&gt;poetry run&lt;/code&gt;, or simply activate the virtual environment and run the commands directly. E.g., in case you want to run the &lt;code&gt;flama&lt;/code&gt; command line, you can do it via activating the virtual environment with &lt;code&gt;poetry shell&lt;/code&gt; and then simply running the command line:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flama &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  ML pipeline with scikit-learn
&lt;/h2&gt;

&lt;p&gt;Since the main goal of this post is to showcase how to serve an ML model via an API (which we'll call ML-API) with &lt;code&gt;flama&lt;/code&gt;, we need an ML model to be served, indeed. We could use any ML model developed with any of the main ML libraries in Python:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://scikit-learn.org/stable/" rel="noopener noreferrer"&gt;scikit-learn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.tensorflow.org/" rel="noopener noreferrer"&gt;TensorFlow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pytorch.org/" rel="noopener noreferrer"&gt;PyTorch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, we think it is a much better idea to quickly develop a simple ML model from scratch, showing how to build robust ML pipelines which not only contain the ML model itself, but also the pre-processing steps much needed later on when we want to make predictions with the model inside the ML-API. Failing in doing so is one of the most common mistakes when building ML-APIs, and typically leads to a lot of headaches when trying to deploy the ML model in production. Why? (you might ask). Well, because the ML model is typically trained after certain cleaning and transformation steps which ensure the data is &lt;em&gt;digestible&lt;/em&gt; by the ML model. Thus, if we only package the ML model (typically yielding a &lt;a href="https://docs.python.org/3/library/pickle.html" rel="noopener noreferrer"&gt;pickle file&lt;/a&gt;) without the pre-processing steps, when we try to make predictions with the model (by loading the corresponding artifact) we would end up with errors, since the data we want to use for making predictions is not in the same format as the data used for training the model. As you can already see, this is a reproducibility nightmare, and we want to avoid it at all costs.&lt;/p&gt;

&lt;p&gt;Fortunately, the main ML libraries in Python provide tools to build ML pipelines to avoid this problem. In this post, we are going to use &lt;strong&gt;scikit-learn&lt;/strong&gt; to build the pipeline we want to serve via our ML-API, but you can use any other library you prefer. &lt;/p&gt;
&lt;h3&gt;
  
  
  The data
&lt;/h3&gt;

&lt;p&gt;The goal of this post is not to build a very complex ML model by itself, but we want to go further than the prototypical &lt;a href="https://scikit-learn.org/stable/auto_examples/datasets/plot_iris_dataset.html" rel="noopener noreferrer"&gt;Iris classification problem&lt;/a&gt;. For this reason, we are going to use a dataset which is a bit more complex, but still simple enough to be able to focus on the ML pipeline and the ML-API. The problem we are going to address has to do with the prediction of &lt;em&gt;customer churn&lt;/em&gt;, i.e. the prediction of whether a customer will leave a company or not, which is a very common problem in the industry. The dataset we are going to use is a &lt;em&gt;public dataset&lt;/em&gt;, which you can download from &lt;a href="https://github.com/vortico/flama-demo/blob/master/data/data.parquet" rel="noopener noreferrer"&gt;here&lt;/a&gt;. For the sake of brevity, we are not going to discuss here the details of the dataset, we will just assume that the data exploration has already been done.&lt;/p&gt;

&lt;p&gt;Before we proceed with the ML pipeline, we are going to prepare the repository for the data and the ML pipeline script. To do so, we can run the following command lines:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; data
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; src/pipeline
&lt;span class="nb"&gt;touch &lt;/span&gt;src/pipeline/__main__.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And, later:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://github.com/vortico/flama-demo/raw/master/data/data.parquet &lt;span class="nt"&gt;-O&lt;/span&gt; data/data.parquet
wget https://github.com/vortico/flama-demo/raw/master/data/artifact.json &lt;span class="nt"&gt;-O&lt;/span&gt; data/artifact.json
wget https://github.com/vortico/flama-demo/raw/master/data/input.json &lt;span class="nt"&gt;-O&lt;/span&gt; data/input.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The project folder should look like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
├── README.md
├── data
│   ├── artifact.json
│   ├── data.parquet
│   └── input.json
├── poetry.lock
├── pyproject.toml
└── src
    └── pipeline
        └── __main__.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  The ML pipeline
&lt;/h3&gt;

&lt;p&gt;Having all the data in place, we can proceed and build the ML pipeline. As we mentioned before, the aim of the post is not the building and training a perfect ML pipeline. Instead, we want to focus our attention on how to structure the pipeline, and the project, with the ultimate goal of packaging the pipeline and serving it via an ML-API. For this reason, we are going to build a very simple ML pipeline, which will consist of the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data loading&lt;/strong&gt;: We will load the data from the &lt;code&gt;data.parquet&lt;/code&gt; file into a &lt;code&gt;pandas.DataFrame&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Train-test split&lt;/strong&gt;: We will split the data into training and test sets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preprocessing numerical and categorical features&lt;/strong&gt;: We will apply some simple transformations to the numerical and categorical features of the dataset.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Training&lt;/strong&gt;: We will train a simple ML model using the training data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluation&lt;/strong&gt;: We will evaluate the performance of the model using the test data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code for this goes in the &lt;code&gt;src/pipeline/__main__.py&lt;/code&gt; file, and it looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.pipeline&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.impute&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SimpleImputer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.preprocessing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StandardScaler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OneHotEncoder&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.compose&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ColumnTransformer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.neural_network&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MLPClassifier&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;roc_auc_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f1_score&lt;/span&gt;

&lt;span class="c1"&gt;# Set random seed for reproducibility:
&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123_456&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Loading data:
&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_parquet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data/data.parquet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&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;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Exited&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;

&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Exited&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;

&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;

&lt;span class="c1"&gt;# Preprocessing numerical features:
&lt;/span&gt;&lt;span class="n"&gt;numeric_transformer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;imputer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SimpleImputer&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;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;median&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scaler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;StandardScaler&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;numeric_features&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_loc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_dtypes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;int64&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;float64&lt;/span&gt;&lt;span class="sh"&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;drop&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RowNumber&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CustomerId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Exited&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;axis&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="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Preprocessing categorical features:
&lt;/span&gt;&lt;span class="n"&gt;categorical_transformer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;imputer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SimpleImputer&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;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;constant&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fill_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;missing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;onehot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OneHotEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handle_unknown&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ignore&lt;/span&gt;&lt;span class="sh"&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;categorical_features&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_loc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_dtypes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&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;drop&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Surname&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;axis&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="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Train-test split
&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;preprocessor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ColumnTransformer&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;numerical&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numeric_transformer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numeric_features&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;categorical&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categorical_transformer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categorical_features&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;# Model train:
&lt;/span&gt;&lt;span class="n"&gt;mlp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MLPClassifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;hidden_layer_sizes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&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;max_iter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;activation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tanh&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;adam&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;123_456&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;preprocessing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;preprocessor&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mlp_classifier&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mlp&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;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Model evaluation:
&lt;/span&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
Model trained successfully:
    * Accuracy: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
    * Predictions: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can run this script by simply running the following command line:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; src.pipeline

Model trained successfully:
    &lt;span class="k"&gt;*&lt;/span&gt; Accuracy: 0.848
    &lt;span class="k"&gt;*&lt;/span&gt; Predictions: &lt;span class="o"&gt;[&lt;/span&gt;0 1 0 ... 0 0 0]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As you can see, the script trains a simple MLP classifier with 3 hidden layers, and it evaluates the performance of the model using the test data. The accuracy of the model is not great, but that's beyond the scope of this post. The important thing here is that we have a simple ML pipeline which we can use to make predictions when new data comes in, and we have tested it by evaluating the performance of the model with the test data.&lt;/p&gt;
&lt;h2&gt;
  
  
  Packaging the ML pipeline: FLM files
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Why packing ML pipelines and models?
&lt;/h3&gt;

&lt;p&gt;Before we proceed, it is worth discussing why we need to pack ML pipelines and models in the first place. There are several reasons for this, but we will focus on the following ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Persistence&lt;/strong&gt;: The ability to persist ML pipelines and models allows us to reuse them, either for further training, or for making predictions on new data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficiency&lt;/strong&gt;: Binary files are compact, efficient, and easy way to store pipelines and models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Portability&lt;/strong&gt;: Having ML pipelines self-contained in a single file allows us to transfer and use them across different environments.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  How to pack ML pipelines?
&lt;/h3&gt;

&lt;p&gt;Let us assume we are where we are after careful experimentation, cross-validation, testing, and so on, and we have found the optimal ML model for our problem. Great job! &lt;/p&gt;

&lt;p&gt;Now, we want to take our model out of our Jupyter Notebook, or Python script, and offer it as a service to make predictions on demand. The first thing we think about is &lt;a href="https://docs.python.org/3/library/pickle.html#:~:text=%E2%80%9CPickling%E2%80%9D%20is%20the%20process%20whereby,back%20into%20an%20object%20hierarchy" rel="noopener noreferrer"&gt;pickling&lt;/a&gt; (i.e., using &lt;code&gt;pickle.dump&lt;/code&gt;) the model, and pass the resulting file to the corresponding team (or colleague) to develop the wrapper API which will have to eventually unpickle (i.e., using &lt;code&gt;pickle.load&lt;/code&gt;) the object, and expose the &lt;code&gt;predict&lt;/code&gt; method. It seems like a very repetitive and boring task, doesn't it?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flama&lt;/strong&gt; comes equipped with a very convenient CLI which does all the boring part for you seamlessly, just with a single line of code. For this, we only need our models to be packaged with the &lt;strong&gt;Flama&lt;/strong&gt; counterparts of pickle's &lt;em&gt;dump&lt;/em&gt; and &lt;em&gt;load&lt;/em&gt; commands, namely: &lt;code&gt;flama.dump&lt;/code&gt; and &lt;code&gt;flama.load&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Dumping the ML pipeline
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Flama&lt;/strong&gt;'s &lt;code&gt;dump&lt;/code&gt; method uses optimal compression with the aim of making the packing process more efficient, and faster. In our case, we can dump the ML pipeline prepared before by adding the following lines to the &lt;code&gt;src/pipeline/__main__.py&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Add these new imports:
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;flama&lt;/span&gt; 

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;roc_auc_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f1_score&lt;/span&gt;

&lt;span class="c1"&gt;# Here the script continues as before...
&lt;/span&gt;
&lt;span class="c1"&gt;# Model dump:
&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;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;e9d4a470-1eb2-423e-8fb3-eaf236158ab3&lt;/span&gt;&lt;span class="sh"&gt;"&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;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data/model.flm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;flama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;path_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;id_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;solver&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;adam&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;random_state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123_456&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_iter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accuracy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;roc_auc_score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;roc_auc_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f1_score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;f1_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_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;extra&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model_author&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Vortico&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model_description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Churn classifier&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model_version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;churn&lt;/span&gt;&lt;span class="sh"&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;artifacts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;artifact.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./data/artifact.json&lt;/span&gt;&lt;span class="sh"&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
Model saved successfully:
    * File: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path_&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
    * Id: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id_&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&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 first two parameters are the ML pipeline object itself, and the path where the resulting file will be stored, respectively. The remaining parameters are optional, and are used to add metadata to the resulting file which might be quite useful for model management purposes. For further details on the use of these parameters, you can check the official &lt;a href="https://flama.dev/docs/machine-learning-api/packaging-models/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At this point, if you run the script again, you should see a new file in the &lt;code&gt;data&lt;/code&gt; folder called &lt;code&gt;model.flm&lt;/code&gt;. This is the file we will use later on to serve the ML pipeline via an ML-API. Indeed, the running of the script should produce the following output:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; src.pipeline

Model trained successfully:
    &lt;span class="k"&gt;*&lt;/span&gt; Accuracy: 0.848
    &lt;span class="k"&gt;*&lt;/span&gt; Predictions: &lt;span class="o"&gt;[&lt;/span&gt;0 1 0 ... 0 0 0]

Model saved successfully:
    &lt;span class="k"&gt;*&lt;/span&gt; File: data/model.flm
    &lt;span class="k"&gt;*&lt;/span&gt; Id: e9d4a470-1eb2-423e-8fb3-eaf236158ab3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  FLM files
&lt;/h3&gt;

&lt;p&gt;OK, so we have the ML pipeline packaged as an FLM file, but what's that? FLM stands for &lt;em&gt;Flama Lightweight Model&lt;/em&gt;. This comes from the fact that, FLM files are a lightweight representation of ML models, which come with useful metadata needed for later purposes, e.g. building a wrapper Flama API containing the model. To know more about FLM files, you can check the official &lt;a href="https://flama.dev/docs/machine-learning-api/packaging-models/#flm-files" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The structure of an FLM file is thought to be as simple as possible, and aims at keeping in a single file all the information needed to load and use the model. The structure of an FLM file is as follows:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;├── model.flm
│   └── model
│       ├── model &lt;span class="o"&gt;(&lt;/span&gt;python object&lt;span class="o"&gt;)&lt;/span&gt;
│       └── meta
│           ├── &lt;span class="nb"&gt;id&lt;/span&gt;
│           ├── timestamp
│           ├── framework
│           ├── model
│           │   ├── obj
│           │   ├── info
│           │   ├── params
│           │   └── metrics
│           └── extra
└── artifacts
    ├── foo.json
    └── bar.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As you can see, the FLM file contains a wide range of information about the pipeline and model. &lt;/p&gt;
&lt;h3&gt;
  
  
  Loading the ML pipeline
&lt;/h3&gt;

&lt;p&gt;We have an FLM file, and we would like to load it to make predictions, and so on. How can we do that? Well, we can use &lt;strong&gt;Flama&lt;/strong&gt;'s &lt;code&gt;load&lt;/code&gt; method, which is the counterpart of &lt;code&gt;dump&lt;/code&gt;. In our case, we can load the ML pipeline by adding the following lines to the &lt;code&gt;src/pipeline/__main__.py&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Here the script continues as before...
&lt;/span&gt;
&lt;span class="c1"&gt;# Load the ML pipeline:
&lt;/span&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;flama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./data/model.flm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
Model loaded successfully:
    * Id: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
    * Trained at: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
    * Metrics: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
    * Author: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;model_author&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you run the script again, you should see the following output:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; src.pipeline

Model trained successfully:
    &lt;span class="k"&gt;*&lt;/span&gt; Accuracy: 0.848
    &lt;span class="k"&gt;*&lt;/span&gt; Predictions: &lt;span class="o"&gt;[&lt;/span&gt;0 1 0 ... 0 0 0]


Model saved successfully:
    &lt;span class="k"&gt;*&lt;/span&gt; File: data/model.flm
    &lt;span class="k"&gt;*&lt;/span&gt; Id: e9d4a470-1eb2-423e-8fb3-eaf236158ab3


Model loaded successfully:
    &lt;span class="k"&gt;*&lt;/span&gt; Id: e9d4a470-1eb2-423e-8fb3-eaf236158ab3
    &lt;span class="k"&gt;*&lt;/span&gt; Trained at: 2023-12-15 17:27:18.983044
    &lt;span class="k"&gt;*&lt;/span&gt; Metrics: &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'accuracy'&lt;/span&gt;: 0.848, &lt;span class="s1"&gt;'roc_auc_score'&lt;/span&gt;: 0.7050107038056138, &lt;span class="s1"&gt;'f1_score'&lt;/span&gt;: 0.5571095571095571&lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt; Author: Vortico
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Hooray! 🥳🥳🥳 We have all the ingredients needed to build our ML API, and we have tested them to make sure everything works as expected. Now, we can proceed and see the magic of &lt;strong&gt;Flama&lt;/strong&gt; in action.&lt;/p&gt;
&lt;h2&gt;
  
  
  Serving the ML pipeline: Flama CLI
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The challenge
&lt;/h3&gt;

&lt;p&gt;Once we have an ML pipeline trained and saved as a binary file, the process of serving the model usually requires addressing the following points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Develop a service wrapping the model&lt;/li&gt;
&lt;li&gt;Tackle the problem of the model lifecycle&lt;/li&gt;
&lt;li&gt;Define an interaction interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a common pattern present in (almost) every ML project, and it lacks a standard to follow so far.&lt;/p&gt;

&lt;p&gt;This challenge can materialise in two different ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Synchronous&lt;/strong&gt;: The client requests a prediction to the server and waits for the response. This can be solved with an HTTP service.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Asynchronous&lt;/strong&gt;: An event occurs and the server communicates the prediction to the client. This can be solved with a streaming service, or even with a websocket.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good news? &lt;strong&gt;Flama&lt;/strong&gt; brings all the tools needed to address both cases.&lt;/p&gt;
&lt;h3&gt;
  
  
  The solution: Flama CLI
&lt;/h3&gt;

&lt;p&gt;As we already saw at the very beginning of this post, &lt;strong&gt;Flama&lt;/strong&gt; comes with the convenient &lt;code&gt;flama&lt;/code&gt; command-line interface (CLI) at your disposal. Indeed, we already used it before when checking if all was correctly installed. In this section, we are going to deep dive a bit more into the CLI. For thi purpose, you only need to run the following command line:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flama &lt;span class="nt"&gt;--help&lt;/span&gt;

Usage: flama &lt;span class="o"&gt;[&lt;/span&gt;OPTIONS] COMMAND &lt;span class="o"&gt;[&lt;/span&gt;ARGS]...

  Fire up your models with Flama 🔥

Options:
  &lt;span class="nt"&gt;--version&lt;/span&gt;  Check the version of your locally installed Flama
  &lt;span class="nt"&gt;--help&lt;/span&gt;     Get &lt;span class="nb"&gt;help &lt;/span&gt;about how to use Flama CLI

Commands:
  model  Interact with an ML model without server.
  run    Run a Flama Application based on a route.
  serve  Serve an ML model file within a Flama Application.
  start  Start a Flama Application based on a config file.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;serve&lt;/code&gt; command
&lt;/h3&gt;

&lt;p&gt;The command serve comes to the rescue of those who are looking for an &lt;strong&gt;instantaneous serving&lt;/strong&gt; of an ML model without having to write an app. This command, as we are about to see, only requires the file with the ML model to be served. The model will have to be saved as a binary file beforehand by using the tools offered by &lt;strong&gt;Flama&lt;/strong&gt;, as we have just seen.&lt;/p&gt;

&lt;p&gt;Without much further ado, let's see how to serve the model we have just trained and saved:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flama serve &lt;span class="s2"&gt;"data/model.flm"&lt;/span&gt;
INFO:     Started server process &lt;span class="o"&gt;[&lt;/span&gt;64069]
INFO:     Waiting &lt;span class="k"&gt;for &lt;/span&gt;application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 &lt;span class="o"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;What just happened? As you can see we have a &lt;strong&gt;Flama&lt;/strong&gt; application up and running on &lt;a href="http://127.0.0.1" rel="noopener noreferrer"&gt;http://127.0.0.1&lt;/a&gt; and listening the port &lt;code&gt;8000&lt;/code&gt;. But, we have not written a single line of code! This is the &lt;strong&gt;codeless approach of Flama to ML models deployment&lt;/strong&gt;, which makes possible the deployment of an already trained-and-tested ML model as an API ready to receive requests and return estimations almost without any effort, as you can see.&lt;/p&gt;

&lt;p&gt;Before we proceed and make some requests to the ML-API, we can click on the link &lt;a href="http://127.0.0.1:8000/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/&lt;/a&gt; (or, simply, open it with your favourite browser) and check what is there waiting for us. You should see something like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"meta"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"e9d4a470-1eb2-423e-8fb3-eaf236158ab3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2023-12-15T17:27:18.983044"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"framework"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"sklearn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"1.3.2"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"obj"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Pipeline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"info"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"memory"&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="nl"&gt;"steps"&lt;/span&gt;&lt;span class="p"&gt;:[[&lt;/span&gt;&lt;span class="s2"&gt;"preprocessing"&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="s2"&gt;"mlp_classifier"&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="nl"&gt;"verbose"&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;"preprocessing"&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="nl"&gt;"mlp_classifier"&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="nl"&gt;"preprocessing__n_jobs"&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="nl"&gt;"preprocessing__remainder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"drop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"preprocessing__sparse_threshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"preprocessing__transformer_weights"&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="nl"&gt;"preprocessing__transformers"&lt;/span&gt;&lt;span class="p"&gt;:[[&lt;/span&gt;&lt;span class="s2"&gt;"numerical"&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;]],[&lt;/span&gt;&lt;span class="s2"&gt;"categorical"&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="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]]],&lt;/span&gt;&lt;span class="nl"&gt;"preprocessing__verbose"&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;"preprocessing__verbose_feature_names_out"&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;"preprocessing__numerical"&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="nl"&gt;"preprocessing__categorical"&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="nl"&gt;"preprocessing__numerical__memory"&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="nl"&gt;"preprocessing__numerical__steps"&lt;/span&gt;&lt;span class="p"&gt;:[[&lt;/span&gt;&lt;span class="s2"&gt;"imputer"&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="s2"&gt;"scaler"&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="nl"&gt;"preprocessing__numerical__verbose"&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;"preprocessing__numerical__imputer"&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="nl"&gt;"preprocessing__numerical__scaler"&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="nl"&gt;"preprocessing__numerical__imputer__add_indicator"&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;"preprocessing__numerical__imputer__copy"&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;"preprocessing__numerical__imputer__fill_value"&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="nl"&gt;"preprocessing__numerical__imputer__keep_empty_features"&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;"preprocessing__numerical__imputer__missing_values"&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="nl"&gt;"preprocessing__numerical__imputer__strategy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"median"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"preprocessing__numerical__scaler__copy"&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;"preprocessing__numerical__scaler__with_mean"&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;"preprocessing__numerical__scaler__with_std"&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;"preprocessing__categorical__memory"&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="nl"&gt;"preprocessing__categorical__steps"&lt;/span&gt;&lt;span class="p"&gt;:[[&lt;/span&gt;&lt;span class="s2"&gt;"imputer"&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="s2"&gt;"onehot"&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="nl"&gt;"preprocessing__categorical__verbose"&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;"preprocessing__categorical__imputer"&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="nl"&gt;"preprocessing__categorical__onehot"&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="nl"&gt;"preprocessing__categorical__imputer__add_indicator"&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;"preprocessing__categorical__imputer__copy"&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;"preprocessing__categorical__imputer__fill_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"missing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"preprocessing__categorical__imputer__keep_empty_features"&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;"preprocessing__categorical__imputer__missing_values"&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="nl"&gt;"preprocessing__categorical__imputer__strategy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"constant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"preprocessing__categorical__onehot__categories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"auto"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"preprocessing__categorical__onehot__drop"&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="nl"&gt;"preprocessing__categorical__onehot__dtype"&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="nl"&gt;"preprocessing__categorical__onehot__feature_name_combiner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"concat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"preprocessing__categorical__onehot__handle_unknown"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"ignore"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"preprocessing__categorical__onehot__max_categories"&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="nl"&gt;"preprocessing__categorical__onehot__min_frequency"&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="nl"&gt;"preprocessing__categorical__onehot__sparse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"deprecated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"preprocessing__categorical__onehot__sparse_output"&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;"mlp_classifier__activation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"tanh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mlp_classifier__alpha"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;0.0001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mlp_classifier__batch_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"auto"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mlp_classifier__beta_1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mlp_classifier__beta_2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;0.999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mlp_classifier__early_stopping"&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;"mlp_classifier__epsilon"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1e-08&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mlp_classifier__hidden_layer_sizes"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&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;"mlp_classifier__learning_rate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"constant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mlp_classifier__learning_rate_init"&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;"mlp_classifier__max_fun"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;15000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mlp_classifier__max_iter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mlp_classifier__momentum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mlp_classifier__n_iter_no_change"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mlp_classifier__nesterovs_momentum"&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;"mlp_classifier__power_t"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mlp_classifier__random_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;123456&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mlp_classifier__shuffle"&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;"mlp_classifier__solver"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"adam"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mlp_classifier__tol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;0.0001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mlp_classifier__validation_fraction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mlp_classifier__verbose"&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;"mlp_classifier__warm_start"&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;"params"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"solver"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"adam"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"random_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;123456&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"max_iter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="nl"&gt;"metrics"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"accuracy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;0.848&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"roc_auc_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;0.7050107038056138&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"f1_score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;0.5571095571095571&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;&lt;span class="nl"&gt;"extra"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"model_author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Vortico"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"model_description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Churn classifier"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"model_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="s2"&gt;"loss"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"churn"&lt;/span&gt;&lt;span class="p"&gt;]}},&lt;/span&gt;&lt;span class="nl"&gt;"artifacts"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"artifact.json"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"/var/folders/h4/vc_99fk53b93ttsv18ss8vhw0000gn/T/tmpomibzc6l/artifacts/artifact.json"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Indeed, we can get the same response by running the command line:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://127.0.0.1:8000/

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"meta"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:&lt;span class="s2"&gt;"e9d4a470-1eb2-423e-8fb3-eaf236158ab3"&lt;/span&gt;,&lt;span class="s2"&gt;"timestamp"&lt;/span&gt;:&lt;span class="s2"&gt;"2023-12-15T17:27:18.983044"&lt;/span&gt;,&lt;span class="s2"&gt;"framework"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"lib"&lt;/span&gt;:&lt;span class="s2"&gt;"sklearn"&lt;/span&gt;,&lt;span class="s2"&gt;"version"&lt;/span&gt;:&lt;span class="s2"&gt;"1.3.2"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"obj"&lt;/span&gt;:&lt;span class="s2"&gt;"Pipeline"&lt;/span&gt;,&lt;span class="s2"&gt;"info"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"memory"&lt;/span&gt;:null,&lt;span class="s2"&gt;"steps"&lt;/span&gt;:[[&lt;span class="s2"&gt;"preprocessing"&lt;/span&gt;,null],[&lt;span class="s2"&gt;"mlp_classifier"&lt;/span&gt;,null]],&lt;span class="s2"&gt;"verbose"&lt;/span&gt;:false,&lt;span class="s2"&gt;"preprocessing"&lt;/span&gt;:null,&lt;span class="s2"&gt;"mlp_classifier"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__n_jobs"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__remainder"&lt;/span&gt;:&lt;span class="s2"&gt;"drop"&lt;/span&gt;,&lt;span class="s2"&gt;"preprocessing__sparse_threshold"&lt;/span&gt;:0.3,&lt;span class="s2"&gt;"preprocessing__transformer_weights"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__transformers"&lt;/span&gt;:[[&lt;span class="s2"&gt;"numerical"&lt;/span&gt;,null,[3,6,7,8,9,10,11,12]],[&lt;span class="s2"&gt;"categorical"&lt;/span&gt;,null,[4,5]]],&lt;span class="s2"&gt;"preprocessing__verbose"&lt;/span&gt;:false,&lt;span class="s2"&gt;"preprocessing__verbose_feature_names_out"&lt;/span&gt;:true,&lt;span class="s2"&gt;"preprocessing__numerical"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__categorical"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__numerical__memory"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__numerical__steps"&lt;/span&gt;:[[&lt;span class="s2"&gt;"imputer"&lt;/span&gt;,null],[&lt;span class="s2"&gt;"scaler"&lt;/span&gt;,null]],&lt;span class="s2"&gt;"preprocessing__numerical__verbose"&lt;/span&gt;:false,&lt;span class="s2"&gt;"preprocessing__numerical__imputer"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__numerical__scaler"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__numerical__imputer__add_indicator"&lt;/span&gt;:false,&lt;span class="s2"&gt;"preprocessing__numerical__imputer__copy"&lt;/span&gt;:true,&lt;span class="s2"&gt;"preprocessing__numerical__imputer__fill_value"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__numerical__imputer__keep_empty_features"&lt;/span&gt;:false,&lt;span class="s2"&gt;"preprocessing__numerical__imputer__missing_values"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__numerical__imputer__strategy"&lt;/span&gt;:&lt;span class="s2"&gt;"median"&lt;/span&gt;,&lt;span class="s2"&gt;"preprocessing__numerical__scaler__copy"&lt;/span&gt;:true,&lt;span class="s2"&gt;"preprocessing__numerical__scaler__with_mean"&lt;/span&gt;:true,&lt;span class="s2"&gt;"preprocessing__numerical__scaler__with_std"&lt;/span&gt;:true,&lt;span class="s2"&gt;"preprocessing__categorical__memory"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__categorical__steps"&lt;/span&gt;:[[&lt;span class="s2"&gt;"imputer"&lt;/span&gt;,null],[&lt;span class="s2"&gt;"onehot"&lt;/span&gt;,null]],&lt;span class="s2"&gt;"preprocessing__categorical__verbose"&lt;/span&gt;:false,&lt;span class="s2"&gt;"preprocessing__categorical__imputer"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__categorical__onehot"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__categorical__imputer__add_indicator"&lt;/span&gt;:false,&lt;span class="s2"&gt;"preprocessing__categorical__imputer__copy"&lt;/span&gt;:true,&lt;span class="s2"&gt;"preprocessing__categorical__imputer__fill_value"&lt;/span&gt;:&lt;span class="s2"&gt;"missing"&lt;/span&gt;,&lt;span class="s2"&gt;"preprocessing__categorical__imputer__keep_empty_features"&lt;/span&gt;:false,&lt;span class="s2"&gt;"preprocessing__categorical__imputer__missing_values"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__categorical__imputer__strategy"&lt;/span&gt;:&lt;span class="s2"&gt;"constant"&lt;/span&gt;,&lt;span class="s2"&gt;"preprocessing__categorical__onehot__categories"&lt;/span&gt;:&lt;span class="s2"&gt;"auto"&lt;/span&gt;,&lt;span class="s2"&gt;"preprocessing__categorical__onehot__drop"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__categorical__onehot__dtype"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__categorical__onehot__feature_name_combiner"&lt;/span&gt;:&lt;span class="s2"&gt;"concat"&lt;/span&gt;,&lt;span class="s2"&gt;"preprocessing__categorical__onehot__handle_unknown"&lt;/span&gt;:&lt;span class="s2"&gt;"ignore"&lt;/span&gt;,&lt;span class="s2"&gt;"preprocessing__categorical__onehot__max_categories"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__categorical__onehot__min_frequency"&lt;/span&gt;:null,&lt;span class="s2"&gt;"preprocessing__categorical__onehot__sparse"&lt;/span&gt;:&lt;span class="s2"&gt;"deprecated"&lt;/span&gt;,&lt;span class="s2"&gt;"preprocessing__categorical__onehot__sparse_output"&lt;/span&gt;:true,&lt;span class="s2"&gt;"mlp_classifier__activation"&lt;/span&gt;:&lt;span class="s2"&gt;"tanh"&lt;/span&gt;,&lt;span class="s2"&gt;"mlp_classifier__alpha"&lt;/span&gt;:0.0001,&lt;span class="s2"&gt;"mlp_classifier__batch_size"&lt;/span&gt;:&lt;span class="s2"&gt;"auto"&lt;/span&gt;,&lt;span class="s2"&gt;"mlp_classifier__beta_1"&lt;/span&gt;:0.9,&lt;span class="s2"&gt;"mlp_classifier__beta_2"&lt;/span&gt;:0.999,&lt;span class="s2"&gt;"mlp_classifier__early_stopping"&lt;/span&gt;:false,&lt;span class="s2"&gt;"mlp_classifier__epsilon"&lt;/span&gt;:1e-08,&lt;span class="s2"&gt;"mlp_classifier__hidden_layer_sizes"&lt;/span&gt;:[8,6,1],&lt;span class="s2"&gt;"mlp_classifier__learning_rate"&lt;/span&gt;:&lt;span class="s2"&gt;"constant"&lt;/span&gt;,&lt;span class="s2"&gt;"mlp_classifier__learning_rate_init"&lt;/span&gt;:0.001,&lt;span class="s2"&gt;"mlp_classifier__max_fun"&lt;/span&gt;:15000,&lt;span class="s2"&gt;"mlp_classifier__max_iter"&lt;/span&gt;:300,&lt;span class="s2"&gt;"mlp_classifier__momentum"&lt;/span&gt;:0.9,&lt;span class="s2"&gt;"mlp_classifier__n_iter_no_change"&lt;/span&gt;:10,&lt;span class="s2"&gt;"mlp_classifier__nesterovs_momentum"&lt;/span&gt;:true,&lt;span class="s2"&gt;"mlp_classifier__power_t"&lt;/span&gt;:0.5,&lt;span class="s2"&gt;"mlp_classifier__random_state"&lt;/span&gt;:123456,&lt;span class="s2"&gt;"mlp_classifier__shuffle"&lt;/span&gt;:true,&lt;span class="s2"&gt;"mlp_classifier__solver"&lt;/span&gt;:&lt;span class="s2"&gt;"adam"&lt;/span&gt;,&lt;span class="s2"&gt;"mlp_classifier__tol"&lt;/span&gt;:0.0001,&lt;span class="s2"&gt;"mlp_classifier__validation_fraction"&lt;/span&gt;:0.1,&lt;span class="s2"&gt;"mlp_classifier__verbose"&lt;/span&gt;:false,&lt;span class="s2"&gt;"mlp_classifier__warm_start"&lt;/span&gt;:false&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="s2"&gt;"params"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"solver"&lt;/span&gt;:&lt;span class="s2"&gt;"adam"&lt;/span&gt;,&lt;span class="s2"&gt;"random_state"&lt;/span&gt;:123456,&lt;span class="s2"&gt;"max_iter"&lt;/span&gt;:300&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="s2"&gt;"metrics"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"accuracy"&lt;/span&gt;:0.848,&lt;span class="s2"&gt;"roc_auc_score"&lt;/span&gt;:0.7050107038056138,&lt;span class="s2"&gt;"f1_score"&lt;/span&gt;:0.5571095571095571&lt;span class="o"&gt;}}&lt;/span&gt;,&lt;span class="s2"&gt;"extra"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"model_author"&lt;/span&gt;:&lt;span class="s2"&gt;"Vortico"&lt;/span&gt;,&lt;span class="s2"&gt;"model_description"&lt;/span&gt;:&lt;span class="s2"&gt;"Churn classifier"&lt;/span&gt;,&lt;span class="s2"&gt;"model_version"&lt;/span&gt;:&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;,&lt;span class="s2"&gt;"tags"&lt;/span&gt;:[&lt;span class="s2"&gt;"loss"&lt;/span&gt;,&lt;span class="s2"&gt;"churn"&lt;/span&gt;&lt;span class="o"&gt;]}}&lt;/span&gt;,&lt;span class="s2"&gt;"artifacts"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"artifact.json"&lt;/span&gt;:&lt;span class="s2"&gt;"/var/folders/h4/vc_99fk53b93ttsv18ss8vhw0000gn/T/tmpomibzc6l/artifacts/artifact.json"&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 response is a JSON object containing all the information about the model, including the metadata we added when saving the model as an FLM file. This is very useful, since it allows us to check the model is the one we want to serve, and also to check the metadata of the model, which might be useful for model management purposes.&lt;/p&gt;
&lt;h4&gt;
  
  
  Auto documentation
&lt;/h4&gt;

&lt;p&gt;Besides, the &lt;strong&gt;serve&lt;/strong&gt; command also provides an interactive documentation of the ML-API which we can access and test by opening the following link in our browser: &lt;code&gt;http://127.0.0.1:8000/docs/&lt;/code&gt;. You should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flbzr4f284rlfmkt8zm3d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flbzr4f284rlfmkt8zm3d.png" alt="Image description" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The documentation is automatically generated by &lt;strong&gt;Flama&lt;/strong&gt; based on the information contained in the FLM file, and on the parameters passed to the &lt;code&gt;serve&lt;/code&gt; command. The list of parameters accepted by the &lt;strong&gt;serve&lt;/strong&gt; command  can be found in the &lt;a href="https://flama.dev/docs/flama-cli/serve/#parameters" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. In short, we can find the following groups of parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Model parameters&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;model-url&lt;/code&gt;: Route of the model (default: &lt;code&gt;/&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;model-name&lt;/code&gt;: Name of the model (default: &lt;code&gt;model&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;App parameters&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;app-debug&lt;/code&gt;: Enable debug mode (default: &lt;code&gt;False&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app-title&lt;/code&gt;: Name of the application (default: &lt;code&gt;Flama&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app-version&lt;/code&gt;: Version of the application (default: &lt;code&gt;0.1.0&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app-description&lt;/code&gt;: Description of the application (default: &lt;code&gt;Fire up with the flame&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app-docs&lt;/code&gt;: Description of the application (default: &lt;code&gt;Fire up with the flame&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app-schema&lt;/code&gt;: Description of the application (default: &lt;code&gt;Fire up with the flame&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The parameter app-debug brings with it useful tools which make the debugging of the code easier, e.g. highly-detailed error messages, and interactive error webpages.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Server parameters&lt;/strong&gt;: All uvicorn options can be passed to the command serve with the format server-, as we discussed for the command run, e.g.:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;server-host&lt;/code&gt;: Bind socket to this host (default: &lt;code&gt;127.0.0.1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;server-port&lt;/code&gt;: Bind socket to this port (default: &lt;code&gt;8000&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Knowing this, we can fine tune a bit our previous command line to make the automatically-generated documentation more appropriate for our ML-API:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flama serve &lt;span class="s2"&gt;"data/model.flm"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--app-title&lt;/span&gt; &lt;span class="s2"&gt;"Churn classifier"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--app-description&lt;/span&gt; &lt;span class="s2"&gt;"Predict whether a customer will leave a company or not"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--app-version&lt;/span&gt; &lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;which should produce the following output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzuaojdpduzea2sxnuqal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzuaojdpduzea2sxnuqal.png" alt="Image description" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We encourage you to play around with the different parameters of the &lt;strong&gt;serve&lt;/strong&gt; command to see how they work and help you customise the ML-API.&lt;/p&gt;
&lt;h4&gt;
  
  
  Making predictions
&lt;/h4&gt;

&lt;p&gt;With the ML-API up and running, we can proceed and make some predictions. For this, we can use the &lt;strong&gt;predict&lt;/strong&gt; endpoint, which is automatically generated by &lt;strong&gt;Flama&lt;/strong&gt;. We can make predictions by sending a &lt;code&gt;POST&lt;/code&gt; request to the endpoint &lt;code&gt;http://127.0.0.1:8000/predict/&lt;/code&gt; (or, directly using the documentation of the &lt;code&gt;predict&lt;/code&gt; endpoint). As an example, we can use the input data which we downloaded before under &lt;code&gt;data/input.json&lt;/code&gt; as follows:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://127.0.0.1:8000/predict/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
  "input": [[10000, 15628319, "Walker", 792, "France", "Female", 28, 4, 130142.79, 1, 1, 0, 38190.78]]
}'&lt;/span&gt;

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"output"&lt;/span&gt;:[0]&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;or, with the documentation page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlew76wna4bfmsmhv6yg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlew76wna4bfmsmhv6yg.png" alt="Image description" width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the response is a JSON object containing the prediction of the model. In this case, the model predicts the customer will not leave the company.&lt;/p&gt;
&lt;h2&gt;
  
  
  Serverless interaction with the model
&lt;/h2&gt;

&lt;p&gt;There is an alternative way to interact with the model stored as FLM file, which is by using the &lt;strong&gt;model&lt;/strong&gt; command of the &lt;strong&gt;Flama&lt;/strong&gt; CLI. This command allows us to interact with the model without having to start a server. Even though the main purpose of this post is to show how to serve an ML model via an API, we think it iws worth mentioning this alternative way of interacting with the model, since it might be useful in some cases. In any case, we plan to discuss this topic in a much more detailed way in future posts, so stay tuned for a more in-depth discussion on this. Also, you can check the official &lt;a href="https://flama.dev/docs/flama-cli/model/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; if you want to deep dive by yourself in the meantime.&lt;/p&gt;
&lt;h3&gt;
  
  
  The &lt;code&gt;model&lt;/code&gt; command
&lt;/h3&gt;

&lt;p&gt;In some cases, we want to interact with the model directly without the overhead of a server, e.g.:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Development &amp;amp; testing&lt;/strong&gt;: We are working with a model locally and want to try it out on some data to quickly see whether everything is working as expected. This is typically what happens when we're in the development stage of the ML lifecycle.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Streaming workflow&lt;/strong&gt;: This is also the case when we want to use a model as part of a larger pipeline where the model acts as a data processor in a stream of data, in which case we want to be able to pipe data into the model and get the output back.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The command &lt;code&gt;model&lt;/code&gt; allows us to interact with models directly from the command line without the need for a server. To inspect the command options, run:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flama model &lt;span class="nt"&gt;--help&lt;/span&gt;

Usage: flama model &lt;span class="o"&gt;[&lt;/span&gt;OPTIONS] FLAMA_MODEL_PATH COMMAND &lt;span class="o"&gt;[&lt;/span&gt;ARGS]...

  Interact with an ML model without server.

  This &lt;span class="nb"&gt;command &lt;/span&gt;is used to directly interact with an ML model without the need
  of a server. This &lt;span class="nb"&gt;command &lt;/span&gt;can be used to perform any operation that is
  supported by the model, such as inspect, or predict. &amp;lt;FLAMA_MODEL_PATH&amp;gt; is
  the path of the model to be used, e.g. &lt;span class="s1"&gt;'path/to/model.flm'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; This can be
  passed directly as argument of the &lt;span class="nb"&gt;command &lt;/span&gt;line, or by environment variable.

Options:
  &lt;span class="nt"&gt;--help&lt;/span&gt;  Show this message and exit.

Commands:
  inspect  Inspect an ML model.
  predict  Make a prediction using an ML model.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We can check readily, by simply running:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flama model &lt;span class="s2"&gt;"data/model.flm"&lt;/span&gt; inspect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;that the &lt;code&gt;inspect&lt;/code&gt; subcommand gives us exactly the same information we got when we made a request to the root endpoint of the ML-API. &lt;/p&gt;

&lt;p&gt;Very likely, we are much more interested in the &lt;code&gt;predict&lt;/code&gt; subcommand, which allows us to make predictions using the model. We can use an input file to be passed to the model, as we did manually before with the &lt;code&gt;/predict/&lt;/code&gt; path of the ML-API. To do this, we only need to run:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flama model data/model.flm predict &lt;span class="nt"&gt;--file&lt;/span&gt; data/input.json &lt;span class="nt"&gt;--pretty&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;
    0
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The flag &lt;code&gt;--pretty&lt;/code&gt; is used to print the output in a more human-readable way. The main advantage of this approach is that we can make predictions on a bunch of input data by simply passing the input file path. This is useful for local development and quick checks.&lt;/p&gt;

&lt;p&gt;The previous command line is completely equivalent to the following one, which is more suitable for streaming services, or &lt;em&gt;pipe&lt;/em&gt; several models together:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'[[10000, 15628319, "Walker", 792, "France", "Female", 28, 4, 130142.79, 1, 1, 0, 38190.78]]'&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
    flama model data/model.flm predict &lt;span class="nt"&gt;--pretty&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;
    0
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This, as just mentioned, is pretty useful if we want to chain several models, and make the output of one the output of the next in chain. E.g., imagine we have &lt;em&gt;n&lt;/em&gt; models in files: &lt;code&gt;model_1.flm&lt;/code&gt;, ..., &lt;code&gt;model_n.flm&lt;/code&gt;, then we can pipe them as follows:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'[[&amp;lt;INPUT_DATA&amp;gt;]]'&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
    flama model model_1.flm predict | &lt;span class="se"&gt;\&lt;/span&gt;
    flama model model_2.flm predict | &lt;span class="se"&gt;\&lt;/span&gt;
    ...
    flama model model_n.flm predict
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Custom ML APIs with Flama
&lt;/h2&gt;

&lt;p&gt;So far, we have seen how to serve an ML pipeline with &lt;strong&gt;Flama&lt;/strong&gt; CLI in a very simple way, without the need of writing a single line of code. This is a very powerful feature we cannot stress enough, not only because it allows to quickly have an ML-API up and running, but also (and more importantly) because it establishes a &lt;em&gt;standard of communication&lt;/em&gt; between those who develop, train and test the ML pipeline; and those who have to put it into production. This is a very important point, since it allows to reduce the friction between the different teams involved in the ML lifecycle, and to make the process of putting ML pipelines into production much more efficient.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why custom ML APIs?
&lt;/h3&gt;

&lt;p&gt;Despite all the good things we have seen so far, there are cases where the ML-API provided by the &lt;strong&gt;Flama&lt;/strong&gt; CLI is not enough, and we need to develop a custom ML-API. This is the case, for instance, when we need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Further functionality&lt;/strong&gt;: The ML-API provided by the &lt;strong&gt;Flama&lt;/strong&gt; CLI comes with a set of endpoints which are enough for most cases. However, there might be some cases where we need to add further functionality to the ML-API, e.g. we might need to add a new endpoint to the ML-API to perform some specific task.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine control&lt;/strong&gt;: The ML-API provided by the &lt;strong&gt;Flama&lt;/strong&gt; CLI is a "one size fits all" solution, which means that it is not possible to fine tune the behaviour of the ML-API. For instance, we might need to add some startup or shutdown events to the ML-API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning by doing&lt;/strong&gt;: We might want to learn how to develop an ML-API from scratch, and &lt;strong&gt;Flama&lt;/strong&gt; provides the perfect framework to do so.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Flama Application
&lt;/h3&gt;

&lt;p&gt;At the very beginning of this post we mentioned that &lt;strong&gt;Flama&lt;/strong&gt; is a framework for the development of &lt;em&gt;lightweight applications&lt;/em&gt;. Indeed, when we run the &lt;strong&gt;serve&lt;/strong&gt; command before, a &lt;strong&gt;Flama&lt;/strong&gt; application was created, equipped with the set of endpoints we saw before (i.e., &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;/docs/&lt;/code&gt;, &lt;code&gt;/schema/&lt;/code&gt;, and &lt;code&gt;/predict/&lt;/code&gt;), and run under the hood, without you ever noticing. Specifically: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/&lt;/code&gt; is the root endpoint of the ML-API, which returns the inspection of the model (as we saw before with &lt;code&gt;flama model inspect&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/docs/&lt;/code&gt; is the endpoint which provides the automatically-generated documentation of the ML-API.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/predict/&lt;/code&gt; is the endpoint which allows us to make predictions using the corresponding &lt;code&gt;predict()&lt;/code&gt; method of the ML pipeline. This endpoint is in charge of unpacking the FLM file, and calling the &lt;code&gt;predict()&lt;/code&gt; method.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we want to develop a custom ML-API, we need to start by creating a &lt;strong&gt;Flama&lt;/strong&gt; application, and we will do so taking some inspiration from the official &lt;a href="https://flama.dev/docs/machine-learning-api/add-models/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. For this, we will create a new folder called &lt;code&gt;src/api&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;src/api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and will add a new file called &lt;code&gt;src/api/app.py&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flama&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flama&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Flama ML&lt;/span&gt;&lt;span class="sh"&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;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Machine learning API using Flama 🔥&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/docs/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    tags:
        - Home
    summary:
        Returns warming message
    description:
        The function returns a hello message
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello 🔥&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And we can run the application by simply running:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flama run src.api.app:app

INFO:     Started server process &lt;span class="o"&gt;[&lt;/span&gt;68609]
INFO:     Waiting &lt;span class="k"&gt;for &lt;/span&gt;application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 &lt;span class="o"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To know more about the &lt;strong&gt;run&lt;/strong&gt; command of the &lt;strong&gt;Flama&lt;/strong&gt; CLI, please have a look at the &lt;a href="https://flama.dev/docs/flama-cli/run/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If we want to run the application as a Python script, we can add the following lines to the file &lt;code&gt;src/api/__main__.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;flama&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;flama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flama_app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src.api.app:app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server_host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server_port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server_reload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and run the application by simply running:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; src.api

INFO:     Started server process &lt;span class="o"&gt;[&lt;/span&gt;69083]
INFO:     Waiting &lt;span class="k"&gt;for &lt;/span&gt;application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 &lt;span class="o"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Wonderful! 🥳🥳🥳 We have our &lt;strong&gt;Flama&lt;/strong&gt; application up and running, with a custom endpoint which returns a warming message.&lt;br&gt;
Now, we can proceed and add the ML pipeline we trained before to the application.&lt;/p&gt;

&lt;p&gt;In case you did not notice, the fact that we used the parameter &lt;code&gt;server_reload=True&lt;/code&gt; with &lt;code&gt;flama.run&lt;/code&gt; in the &lt;code&gt;__main__.py&lt;/code&gt; file activated the &lt;em&gt;hot reloading&lt;/em&gt; feature of &lt;strong&gt;Flama&lt;/strong&gt;, which means that the application will be reloaded every time we make a change to the code. This is very useful for development purposes, since it allows us to make changes to the code without having to restart the application every time.&lt;/p&gt;
&lt;h4&gt;
  
  
  Add the ML resource
&lt;/h4&gt;

&lt;p&gt;Let us assume we want our ML-API to satisfy the following requirements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The churn pipeline must be exposed under the route &lt;code&gt;/churn/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;We want to have exposed the &lt;code&gt;predict&lt;/code&gt; method of the pipeline as we already had before&lt;/li&gt;
&lt;li&gt;We want to have a new endpoint which returns the estimation of financial loss due to churn of a given customer (more details below).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the first two requirements, we are going to use three built-in ingredients of &lt;strong&gt;Flama&lt;/strong&gt; which we haven't seen yet, namely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ModelResource&lt;/code&gt;: This is a built-in class which derives from the most fundamental resource class &lt;code&gt;BaseResource&lt;/code&gt;. These classes can be seen as the &lt;em&gt;interface&lt;/em&gt; that any custom model class will need to adhere to. In case you're not familiar with the interface design pattern, it can be very succinctly described as &lt;em&gt;a blueprint for designing classes&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ModelResourceType&lt;/code&gt;: This is a built-in &lt;a href="https://docs.python.org/3/reference/datamodel.html?highlight=metaclass#metaclasses" rel="noopener noreferrer"&gt;metaclass&lt;/a&gt; which derives from the most fundamental resource type metaclass &lt;code&gt;ResourceType&lt;/code&gt;. The concept of metaclass is a bit less intuitive, and some highly recognised &lt;em&gt;pythonists&lt;/em&gt; recommend to avoid them at all costs. We agree with such a claim, which is why you won't need to worry about them, apart from having to import the ModelResourceType, and use it as an argument when building your custom model class.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;resource_method&lt;/code&gt; decorator: This is a built-in &lt;a href="https://docs.python.org/3/glossary.html#term-decorator" rel="noopener noreferrer"&gt;decorator&lt;/a&gt; which allows us to convert a given class method into an app endpoint, with the following main arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;path&lt;/code&gt;: Route path through which we'll be able to call the decorated method, e.g. &lt;code&gt;/predict/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;methods&lt;/code&gt;: HTTP methods associated with the endpoint, e.g. &lt;code&gt;GET&lt;/code&gt; or &lt;code&gt;POST&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt;: Name which uniquely determines the route being added to the app.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's add the ML pipeline to the application by adding the following lines to the file &lt;code&gt;src/api/app.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Add these imports:
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ModelResource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ModelResourceType&lt;/span&gt;

&lt;span class="c1"&gt;# Here the script continues as before...
&lt;/span&gt;
&lt;span class="c1"&gt;# Add the ML pipeline:
&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChurnModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ModelResource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metaclass&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ModelResourceType&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;churn_model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;verbose_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Churn model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;model_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data/model.flm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_model_resource&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;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/churn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ChurnModel&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, we have created a new class called &lt;code&gt;ChurnModel&lt;/code&gt; which derives from &lt;code&gt;ModelResource&lt;/code&gt;, and we have added it to the application by using the &lt;code&gt;add_model_resource&lt;/code&gt; method of the &lt;code&gt;models&lt;/code&gt; attribute of the application. This method can take several arguments, but we are mostly interested in the following ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;path&lt;/code&gt;: Route path where the method &lt;strong&gt;predict&lt;/strong&gt; will be placed, e.g. &lt;code&gt;path="/churn/"&lt;/code&gt; means that the method &lt;strong&gt;predict&lt;/strong&gt; will be placed under the route &lt;code&gt;/churn/predict/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;resource&lt;/code&gt;: Resource class to be added to the application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Given that the class &lt;code&gt;ChurnModel&lt;/code&gt; inherits from &lt;code&gt;ModelResource&lt;/code&gt; and uses &lt;code&gt;ModelResourceType&lt;/code&gt; as metaclass, it will have the following methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/&lt;/code&gt;: Returns the inspection of the model.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/predict/&lt;/code&gt;: Returns the prediction of the model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can check this by running the application again:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flama run src.api.app:app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; src.api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and opening the documentation page at &lt;code&gt;http://127.0.0.1:8000/docs/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With the previous code, we are already serving the ML pipeline as we wanted, and satisfying the requirements (1) and (2).&lt;br&gt;
What about the custom endpoint we wanted to add?&lt;/p&gt;
&lt;h4&gt;
  
  
  Add a custom endpoint to the Churn model resource
&lt;/h4&gt;

&lt;p&gt;It seems we have everything almost ready to just add the custom endpoint we were told to add. Let us imagine our data-science colleague has developed a formula to estimate the financial loss due to the churn of a given customer, which looks like:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;loss[$]=P(churn)×(N×Income[$]−OPEX[$])\text{loss}[\$] = \mathbb{P}({\text{churn}}) \times \left(N \times \text{Income}[\$] - \text{OPEX}[\$]\right) &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;loss&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;[&lt;/span&gt;&lt;span class="mord"&gt;$&lt;/span&gt;&lt;span class="mclose"&gt;]&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathbb"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;churn&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="minner"&gt;&lt;span class="mopen delimcenter"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;N&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Income&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;[&lt;/span&gt;&lt;span class="mord"&gt;$&lt;/span&gt;&lt;span class="mclose"&gt;]&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;OPEX&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;[&lt;/span&gt;&lt;span class="mord"&gt;$&lt;/span&gt;&lt;span class="mclose"&gt;]&lt;/span&gt;&lt;span class="mclose delimcenter"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;



&lt;p&gt;where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Average income&lt;/code&gt; (
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Income[$]\text{Income}[\$]&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;Income&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;[&lt;/span&gt;&lt;span class="mord"&gt;$&lt;/span&gt;&lt;span class="mclose"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
): Estimated average annual income of the customer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Churn probability&lt;/code&gt; (
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;P(churn)\mathbb{P}({\text{churn}})&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathbb"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;churn&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
): Probability of churn of the customer which we get from the ML pipeline (i.e., the output of the &lt;code&gt;predict&lt;/code&gt; method).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Agents per client&lt;/code&gt; (
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;NN&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;N&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
): Number of agents assigned to the customer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Operational cost&lt;/code&gt; (
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;OPEX[$]\text{OPEX}[\$]&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;OPEX&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;[&lt;/span&gt;&lt;span class="mord"&gt;$&lt;/span&gt;&lt;span class="mclose"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
): Operational cost of the company&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Out of the four parameters of the formula, we only have the first two: the estimated annual income of the customer (which will be passed as the ninth element of the input data), and the churn probability (which we get from the &lt;code&gt;predict&lt;/code&gt; method of the ML pipeline). The other two parameters are not part of the input, but are parameters which were known/computed during the training stage, and were stored together with the ML pipeline as part of the FLM file, under the &lt;code&gt;artifacts&lt;/code&gt; attribute of the &lt;code&gt;dump&lt;/code&gt; method of the pipeline (see, &lt;code&gt;artifacts={"artifact.json": "./data/artifact.json"}&lt;/code&gt;). This is very useful, since it allows us to use such information which was computed during the training stage, and use it in the production stage without having to recompute it again, or having to introduce it manually in the code of the ML-API (typically as constants, or in a configuration file).&lt;/p&gt;

&lt;p&gt;We can add the custom endpoint to the &lt;code&gt;ChurnModel&lt;/code&gt; class by adding the following lines to the file &lt;code&gt;src/api/app.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Add these imports:
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ModelResource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ModelResourceType&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flama.resources&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;resource_method&lt;/span&gt;

&lt;span class="c1"&gt;# Here the script continues as before...
&lt;/span&gt;
&lt;span class="c1"&gt;# Add the ML pipeline:
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChurnModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ModelResource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metaclass&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ModelResourceType&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;churn_model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;verbose_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Churn model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;model_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data/model.flm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="nd"&gt;@resource_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/loss/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model-predict-loss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;predict_loss&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;self&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;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MLModelInput&lt;/span&gt;&lt;span class="p"&gt;]&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;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MLModelOutput&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        tags:
            - Churn model
        summary:
            Get loss from churn
        description:
            Computes the loss amount estimated according to the model parameters
            provided in the model artifacts, with loss being the product of the
            churn probability and the estimated salary of the client minus the
            operational cost of the company.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;artifacts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;artifact.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&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;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;proba&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict_proba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&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;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;proba&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agents_per_client&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;operational_cost&lt;/span&gt;&lt;span class="sh"&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;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MLModelOutput&lt;/span&gt;&lt;span class="p"&gt;]({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;()})&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_model_resource&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;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/churn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ChurnModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After adding the new code to the application source file, &lt;strong&gt;Flama&lt;/strong&gt; will reload the application automatically, and you should see the new endpoint &lt;code&gt;/churn/loss/&lt;/code&gt; in the documentation page. To test it, you can either use the documentation page, or run the following command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://127.0.0.1:8000/churn/loss/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
  "input": [[10000, 15628319, "Walker", 792, "France", "Female", 28, 4, 130142.79, 1, 1, 0, 38190.78]]
}'&lt;/span&gt;

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"output"&lt;/span&gt;:[5212.2945950392195]&lt;span class="o"&gt;}&lt;/span&gt;%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great news! 🥳🥳🥳 We have successfully added a custom endpoint to the ML-API, and we have tested it to make sure it works as expected. In this case, we have an estimated loss of &lt;code&gt;$5212.29&lt;/code&gt; due to the churn of the customer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;In this post we have seen how to build and serve a robust ML pipeline with &lt;strong&gt;Flama&lt;/strong&gt;. We have seen how to train and save an ML pipeline, and how to serve it as an API with the &lt;strong&gt;Flama&lt;/strong&gt; CLI. We have also seen how to interact with the model without the need of a server, and how to develop a custom ML-API with &lt;strong&gt;Flama&lt;/strong&gt;. Finally, we have also shown how to add custom endpoints to the ML-API, and how to use the information stored in FLM files, e.g. to compute the output of a custom endpoint. &lt;/p&gt;

&lt;p&gt;We certainly believe that &lt;strong&gt;Flama&lt;/strong&gt; is a very powerful tool which can help you to put your ML pipelines into production in a very efficient way. We encourage you to try it out, and to give us feedback on how to improve it. We are looking forward to hearing from you! 🤗&lt;/p&gt;

&lt;p&gt;Finally, we would like to thank you for reading this post, and we hope you enjoyed it. If you have any questions, please do not hesitate to contact us. We will be more than happy to help you! 🤓&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://flama.dev/docs/" rel="noopener noreferrer"&gt;Flama documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vortico/flama" rel="noopener noreferrer"&gt;Flama GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/flama/" rel="noopener noreferrer"&gt;Flama PyPI package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  About the authors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vortico.tech/" rel="noopener noreferrer"&gt;Vortico&lt;/a&gt;: We are specialised in software development to helps businesses enhance and expand their AI and technology capabilities.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>machinelearning</category>
      <category>flama</category>
      <category>tutorial</category>
      <category>api</category>
    </item>
  </channel>
</rss>
