<?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: Valentine Sean Chanengeta</title>
    <description>The latest articles on DEV Community by Valentine Sean Chanengeta (@valentinesean22).</description>
    <link>https://dev.to/valentinesean22</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%2F635749%2Fc8f878e3-ffe2-410d-b27a-95cd65dc7ca4.jpg</url>
      <title>DEV Community: Valentine Sean Chanengeta</title>
      <link>https://dev.to/valentinesean22</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/valentinesean22"/>
    <language>en</language>
    <item>
      <title>Flask Neon Kit: The extension which automatically generates CRUD endpoints</title>
      <dc:creator>Valentine Sean Chanengeta</dc:creator>
      <pubDate>Mon, 02 Sep 2024 06:30:43 +0000</pubDate>
      <link>https://dev.to/valentinesean22/flask-neon-kit-the-extension-which-automatically-generates-crud-endpoints-7oi</link>
      <guid>https://dev.to/valentinesean22/flask-neon-kit-the-extension-which-automatically-generates-crud-endpoints-7oi</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/neon"&gt;Neon Open Source Starter Kit Challenge &lt;/a&gt;: Ultimate Starter Kit&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Kit
&lt;/h2&gt;

&lt;p&gt;The flask-neon-kit is a Flask extension which generates CRUD endpoints out of the box from defined models of the Neon Postgres database. It empowers developers with resources' CRUD endpoints by just defining database models and instantiating the kit. This solution is built upon the foundation of the &lt;a href="https://flask-sqlalchemy.palletsprojects.com/en/3.1.x/" rel="noopener noreferrer"&gt;Flask-SQLAlchemy&lt;/a&gt;. This initiative was driven by the tedious nature of manually writing CRUD logic for every Flask application entity. It was also motivated by &lt;a href="https://pypi.org/project/flask-mongo-crud/" rel="noopener noreferrer"&gt;flask-mongo-crud&lt;/a&gt;, the package I am working on that automatically generates CRUD endpoints for MongoDB models.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features of the solution are:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Automatically generates CRUD endpoints from defined model.&lt;/li&gt;
&lt;li&gt;Allows to customize app base URL as well as each model’s url prefix.&lt;/li&gt;
&lt;li&gt;Allows to paginate when getting many entities from database table.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites for using the kit:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Familiarity with implementing Flask Applications.&lt;/li&gt;
&lt;li&gt;Familiarity with integrating Flask Applications with Postgres database using Flask-SQLAlchemy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;flask-neon-kit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Folder Structure of Basic Application
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project_root
|    __init__.py
|    config.py
|    app.py
|
|____models
     |    professor_subject.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Empty &lt;strong&gt;init&lt;/strong&gt;.py file required in project root directory.&lt;/li&gt;
&lt;li&gt;ROOT_URL can be configured in application's configurations and it is &lt;em&gt;optional&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;If configured, it is used by all generated endpoints.&lt;/li&gt;
&lt;li&gt;Code snippet in &lt;strong&gt;Basic Application&lt;/strong&gt; section shows how to configure ROOT_URL.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Database Configuration (config.py):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DbConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;SQLALCHEMY_DATABASE_URI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;NEON-POSTGRES-CONNECTION-STRING&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Models:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Models directory is required in the project root directory:&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If custom name Models directory is not defined, as:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MODELS_DIRECTORY&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;CUSTOM_NAME&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;ul&gt;
&lt;li&gt;then, default “models” directory will be used.&lt;/li&gt;
&lt;li&gt;This is where models files are defined.&lt;/li&gt;
&lt;li&gt;Inside these files declare models classes and their configurations such as:

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;model_url_prefix [OPTIONAL]&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;and those supported by Flask SQLAlchemy&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;model_url_prefix&lt;/em&gt; allows to have unique URLs for each and every model.&lt;/li&gt;
&lt;li&gt;If this configuration is not defined, default configuration will be used.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Model Class Code Snippet (professor_subject.py):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProfessorSubject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;model_url_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;/professor-subject-test&lt;/span&gt;&lt;span class="sh"&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;db&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="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Integer&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;autoincrement&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;professor_first_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&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="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;professor_last_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&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="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;subject_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&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="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="c1"&gt;# These are entity fields
&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;professor_first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;professor_last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subject_name&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;professor_first_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;professor_first_name&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;professor_last_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;professor_last_name&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;subject_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subject_name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Basic Application
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Code Snippet (app.py):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask_sqlalchemy&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;.config&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DbConfig&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask_neon_kit&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FlaskNeonKit&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;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# If models directory is not defined, default "models" directory will be used
&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;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MODELS_DIRECTORY&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;CUSTOM_NAME&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="c1"&gt;# If root URL is not defined, generated endpoints will not have a root URL
&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;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ROOT_URL&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/flask-neon-kit/v1&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;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DbConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Database related part
&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SQLAlchemy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_app&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;flask_neon_kit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FlaskNeonKit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;flask_neon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_app&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;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&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="n"&gt;app&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;debug&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;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Generated Endpoints Examples and HTTP Methods:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;HTTP Methods supported are POST, GET, PUT and DELETE.&lt;/li&gt;
&lt;li&gt;The following generated endpoints will be using model snippet defined earlier in &lt;strong&gt;Configuration&lt;/strong&gt; as well &lt;strong&gt;Basic Application&lt;/strong&gt; sections.&lt;/li&gt;
&lt;li&gt;These endpoints are after application base URL:
&lt;code&gt;&amp;lt;IP_ADDRESS&amp;gt;:&amp;lt;PORT_NUMBER&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Basic complete URL looks like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;IP_ADDRESS&amp;gt;:&amp;lt;PORT_NUMBER&amp;gt;/&amp;lt;ROOT_URL&amp;gt;/&amp;lt;MODEL_URL_PREFIX&amp;gt;/&amp;lt;RESOURCE_NAME&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;IP_ADDRESS&amp;gt;:&amp;lt;PORT_NUMBER&amp;gt;/&amp;lt;ROOT_URL&amp;gt;/&amp;lt;MODEL_URL_PREFIX&amp;gt;/&amp;lt;RESOURCE_NAME&amp;gt;/&amp;lt;RESOURCE_IDENTIFIER&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;RESOURCE_NAME is automatically generated, and a developer can not customize it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;RESOURCE_IDENTIFIER should be the entity ID.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Given the generated URL:&lt;br&gt;
&lt;code&gt;localhost:5000/flask-neon-kit/v1/professor-subject-test/professor-subject/2&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"/professor-subject" is the RESOURCE_NAME.&lt;/li&gt;
&lt;li&gt;"/2" is the RESOURCE_IDENTIFIER.&lt;/li&gt;
&lt;li&gt;In case ROOT_URL is not specified:

&lt;ul&gt;
&lt;li&gt;In the above URL, ROOT_URL is &lt;code&gt;/flask-neon-kit/v1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The URL without ROOT_URL is &lt;code&gt;/professor-subject-test/professor-subject&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;In case MODEL_URL_PREFIX is not specified:

&lt;ul&gt;
&lt;li&gt;In the above URL, MODEL_URL_PREFIX is &lt;code&gt;/professor-subject-test&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The URL without MODEL_URL_PREFIX is &lt;code&gt;/flask-neon-kit/v1/professor-subject&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;In case both ROOT_URL and MODEL_URL_PREFIX are not specified:

&lt;ul&gt;
&lt;li&gt;The URL without both is &lt;code&gt;/professor-subject&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;It contains only the RESOURCE_NAME.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  POST
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Saves new entity into the database&lt;/li&gt;
&lt;li&gt;Only saves one entity at a time.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Supports only JSON data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;JSON Request Payload Example:&lt;br&gt;
&lt;/p&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;"professor_first_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;"Foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"professor_last_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;"Bar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"subject_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;"Comp Science"&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;/li&gt;
&lt;li&gt;
&lt;p&gt;JSON Response Payload Example:&lt;br&gt;
&lt;/p&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;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"professor-subject created successfully"&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;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  GET
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Retrieves entities from the database.&lt;/li&gt;
&lt;li&gt;Can retrieve only one entity or a list of entities.&lt;/li&gt;
&lt;li&gt;To retrieve only one entity, include RESOURCE_IDENTIFIER in the URL.&lt;/li&gt;
&lt;li&gt;This RESOURCE_IDENTIFIER will be used to identify that one entity.&lt;/li&gt;
&lt;li&gt;If RESOURCE_IDENTIFIER is not provided, list of entities will be retrieved.&lt;/li&gt;
&lt;li&gt;Developer can opt for pagination when retrieving many entities.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To paginate, provide the following query parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;pagination&lt;/strong&gt; = &lt;em&gt;true&lt;/em&gt;. If "false", all entities will be retrieved from the database table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;limit&lt;/strong&gt; = "number of entities" per page. Should be integer greater than 0. If not provided and pagination is true, default value of 5 will be used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;page&lt;/strong&gt; = "page number". Should be integer greater than 0. If not provided and pagination is true, default value of 1 will be used.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;JSON Response Payload Example for Paginated Entities:&lt;br&gt;
&lt;/p&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"current_page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"data"&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;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="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"professor_first_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;"professor_last_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;"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;"subject_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;"Math"&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;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="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"professor_first_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;"Foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"professor_last_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;"Bar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"subject_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;"Comp Science"&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;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"has_next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"has_pagination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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;"has_prev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"professor-subject retrieved successfully"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&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;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  PUT
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Updates one entity at a time.&lt;/li&gt;
&lt;li&gt;That entity is identified by RESOURCE_IDENTIFIER in the URL.&lt;/li&gt;
&lt;li&gt;If there is no such entity in the database, " not found" response will be returned.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  DELETE
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Deletes one entity at a time.&lt;/li&gt;
&lt;li&gt;That entity is identified by RESOURCE_IDENTIFIER in the URL.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If there is no such entity in the database, the endpoint will return a payload:&lt;br&gt;
&lt;/p&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;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;RESOURCE_NAME&amp;gt; not found"&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;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Link to Kit
&lt;/h2&gt;


&lt;h3&gt;
  
  
  GitHub:
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ValentineSean" rel="noopener noreferrer"&gt;
        ValentineSean
      &lt;/a&gt; / &lt;a href="https://github.com/ValentineSean/flask-neon-kit" rel="noopener noreferrer"&gt;
        flask-neon-kit
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Flask Neon Kit Docs&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Automatically generates CRUD endpoints from defined model.&lt;/li&gt;
&lt;li&gt;Allows to customize app base URL as well as each model's url prefix.&lt;/li&gt;
&lt;li&gt;Allows to paginate when getting many entities from database table.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;pip install flask-neon-kit&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Configuration&lt;/h2&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Empty &lt;strong&gt;init&lt;/strong&gt;.py file required in project root directory.&lt;/li&gt;
&lt;li&gt;ROOT_URL can be configured in application's configurations and it is &lt;em&gt;optional&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;If configured, it is used by all generated endpoints.&lt;/li&gt;
&lt;li&gt;Code snippet in &lt;strong&gt;Basic Application&lt;/strong&gt; section shows how to configure ROOT_URL.&lt;/li&gt;
&lt;li&gt;Models
&lt;ul&gt;
&lt;li&gt;Models directory is required in the project root directory:&lt;/li&gt;
&lt;li&gt;If custom name Models directory is not defined, as
&lt;div class="highlight highlight-source-python notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-s1"&gt;app&lt;/span&gt;.&lt;span class="pl-s1"&gt;config&lt;/span&gt;[&lt;span class="pl-v"&gt;MODELS_DIRECTORY&lt;/span&gt;] &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s"&gt;"&amp;lt;CUSTOM_NAME&amp;gt;"&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;then, default “models” directory will be used.&lt;/li&gt;
&lt;li&gt;This is where models files are defined.&lt;/li&gt;
&lt;li&gt;Inside these files declare models classes and their configurations such as
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;model_url_prefix [OPTIONAL]&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;and those supported by Flask SQLAlchemy&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;em&gt;model_url_prefix&lt;/em&gt; allows to have unique URLs for each and…&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ValentineSean/flask-neon-kit" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;The kit was also uploaded on &lt;a href="https://pypi.org/project/flask-neon-kit/" rel="noopener noreferrer"&gt; Python Package Index (PyPI)&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Journey
&lt;/h2&gt;

&lt;p&gt;Main objective of this solution is to minimize the tedious nature of manually writing common CRUD logic for every Flask application entity. There is also a plan to integrate some cool Neon features like data branches management in the future. This will enable developers to switch among their data branches to work with different versions of their data whilst in their application development environment. This should have been integrated in this version but seems Neon API currently has some limitations to support such features. I learned a lot during the course, and the concept of data branches is one of the features I am looking forward into exploring in the future.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>neonchallenge</category>
      <category>postgres</category>
      <category>flask</category>
    </item>
    <item>
      <title>Flask-SocketIO Load Balancing</title>
      <dc:creator>Valentine Sean Chanengeta</dc:creator>
      <pubDate>Thu, 28 Dec 2023 23:10:01 +0000</pubDate>
      <link>https://dev.to/valentinesean22/flask-socketio-load-balancing-19dk</link>
      <guid>https://dev.to/valentinesean22/flask-socketio-load-balancing-19dk</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://flask-socketio.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;Flask-SocketIO&lt;/a&gt; is a Python library which enables bi-directional communications between a Flask written server and SocketIO clients. Starting with version 2.0, it can support multiple server instances behind a load balancer to spread the client connections among them. This gives the application the ability to scale so that it can support very large numbers of concurrent clients.&lt;/p&gt;

&lt;p&gt;This article will layout how to deploy a Flask-SocketIO  application with &lt;a href="https://docs.docker.com/get-started/overview/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; as well as &lt;a href="https://flask-socketio.readthedocs.io/en/latest/deployment.html#using-nginx-as-a-websocket-reverse-proxy" rel="noopener noreferrer"&gt;Nginx&lt;/a&gt; for load balancing and &lt;a href="https://flask-socketio.readthedocs.io/en/latest/deployment.html#using-multiple-workers" rel="noopener noreferrer"&gt;Redis Message Queue&lt;/a&gt;. Nginx will forward distributed client requests to Flask server instances. Since each of these instances are connected to only a subset of the clients, Redis Message Queue is used to coordinate broadcasting. Our instances will be deployed as Docker containers.&lt;/p&gt;

&lt;p&gt;The following are steps to deploy the application:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Socket.IO Server Event&lt;/li&gt;
&lt;li&gt;Redis Configuration &lt;/li&gt;
&lt;li&gt;Docker Configuration&lt;/li&gt;
&lt;li&gt;Nginx Configuration&lt;/li&gt;
&lt;li&gt;Deployment Demo&lt;/li&gt;
&lt;li&gt;Application Demo&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Assumptions
&lt;/h2&gt;

&lt;p&gt;Before diving in, this article assumes readers to have basic understanding of Docker, Docker Compose, Redis Message Queue especially with Pub/Sub interface as well as Flask and Nginx.&lt;/p&gt;

&lt;h2&gt;
  
  
  SocketIO Server Event
&lt;/h2&gt;

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

&lt;span class="nd"&gt;@socketio.on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;send-message&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;send_message&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;sender&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="nf"&gt;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;username&lt;/span&gt;&lt;span class="sh"&gt;"&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;sender&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;text_message&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="nf"&gt;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;text_message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;message&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;sender&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text_message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;socketio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;receive-message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;In the above code snippet, Flask-SocketIO server is listening to the event &lt;strong&gt;send-message&lt;/strong&gt;. When a client emits &lt;strong&gt;send-message&lt;/strong&gt; event, the server will receive it with data, execute and then emits &lt;strong&gt;receive-message&lt;/strong&gt; event which will be received by all clients listening to this event.&lt;/p&gt;
&lt;h2&gt;
  
  
  Redis Configuration
&lt;/h2&gt;

&lt;p&gt;This component uses &lt;a href="https://redis.io/docs/interact/pubsub/" rel="noopener noreferrer"&gt;Redis Pub/Sub&lt;/a&gt; which has publishers (senders) and subscribers (receivers). Messages are categorized into channels without knowledge of any receivers subscribed to that channel. Subscribers express interest in one or more channels and only receive messages that are of interest. These subscribers have no knowledge of those channels' publishers. Flask-SocketIO provides this Redis feature out of the box. The following snippet shows &lt;strong&gt;message_queue&lt;/strong&gt; argument passed to SocketIO constructor to enable the server connect to the Redis message queue. The argument's value is a Redis server URL.&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;socketio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_app&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;cors_allowed_origins&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;http://localhost:6001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;message_queue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis://flask-socketio-load-balancing-redis:6379&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;In &lt;a href="https://flask-socketio.readthedocs.io/en/latest/deployment.html#using-multiple-workers" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt;, it is recommended to apply monkey patching at the top of the main script, even above imports. Since this application is using gevent, we apply monkey patching as follows:&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;gevent&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;monkey&lt;/span&gt;
&lt;span class="n"&gt;monkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;patch_all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Docker Configuration
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Dockerfile
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.10-slim-buster&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt requirements.txt&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; gunicorn -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app -b 0.0.0.0:5003&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;This Dockerfile code shows that the application will be running on Gunicorn server.&lt;/p&gt;
&lt;h3&gt;
  
  
  Docker Compose
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;flask-socketio-load-balancing-redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flask-socketio-load-balancing-redis&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;6379"&lt;/span&gt;

  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;flask-socketio-load-balancing-redis&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5003"&lt;/span&gt;

  &lt;span class="na"&gt;flask-socketio-load-balancing-nginx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flask-socketio-load-balancing-nginx&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./nginx.conf:/etc/nginx/nginx.conf:ro&lt;/span&gt;

    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5003:5003"&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Nginx Configuration
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;

&lt;span class="n"&gt;server&lt;/span&gt; {
    &lt;span class="n"&gt;listen&lt;/span&gt; &lt;span class="m"&gt;5003&lt;/span&gt;;

    &lt;span class="n"&gt;location&lt;/span&gt; / {
        &lt;span class="n"&gt;proxy_pass&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;://&lt;span class="n"&gt;backend_instances&lt;/span&gt;;
        &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;-&lt;span class="n"&gt;Forwarded&lt;/span&gt;-&lt;span class="n"&gt;For&lt;/span&gt; $&lt;span class="n"&gt;remote_addr&lt;/span&gt;;
        &lt;span class="n"&gt;proxy_http_version&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;.&lt;span class="m"&gt;1&lt;/span&gt;;
        &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;Upgrade&lt;/span&gt; $&lt;span class="n"&gt;http_upgrade&lt;/span&gt;;
        &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;Connection&lt;/span&gt; &lt;span class="s2"&gt;"Upgrade"&lt;/span&gt;;
        &lt;span class="n"&gt;proxy_set_header&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt; $&lt;span class="n"&gt;host&lt;/span&gt;;
    }
}

&lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="n"&gt;backend_instances&lt;/span&gt; {
    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;:&lt;span class="m"&gt;5003&lt;/span&gt;;
}


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

&lt;/div&gt;
&lt;p&gt;This configuration enables Nginx load balancer to forward client requests to our multiple flask server instances. The &lt;strong&gt;upstream&lt;/strong&gt; group defines server addresses Nginx will forward requests to. In the above snippet, we defined only one server, api:5003 (Flask API), which is our docker compose &lt;strong&gt;api&lt;/strong&gt; service name. Since Nginx and Flask API are connected to same docker compose network, Nginx can directly communicate with Flask API using its service name and container internal port (5003). Nginx will be able to forward client requests to different API instances using different &lt;a href="https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/" rel="noopener noreferrer"&gt;load balancing algorithms&lt;/a&gt;. Docker compose enables to run multiple instances (docker containers) from one service using the flag &lt;em&gt;--scale [SERVICE_NAME]=[NUMBER_OF_INSTANCES]&lt;/em&gt;. Here we will run our containers as follows:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

 docker-compose up -d --force-recreate --build --scale api=3 --remove-orphans


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Docker Containers
&lt;/h2&gt;

&lt;p&gt;After running our containers as above, we will have 5 containers (Redis Server, 3 Flask API Instances and Nginx Server) as shown in the image 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%2F0q61bbkpxs0zjlcu7hgc.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%2F0q61bbkpxs0zjlcu7hgc.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Application Demo
&lt;/h2&gt;

&lt;p&gt;I presented the application at Flask Conn before, it is available on Youtube:&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/UoFlcvTnE1E"&gt;
&lt;/iframe&gt;
&lt;/p&gt;
&lt;h2&gt;
  
  
  GitHub (Flask Server):
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ValentineSean" rel="noopener noreferrer"&gt;
        ValentineSean
      &lt;/a&gt; / &lt;a href="https://github.com/ValentineSean/flask-socketio-load-balancing" rel="noopener noreferrer"&gt;
        flask-socketio-load-balancing
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



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

&lt;p&gt;This article showed how to deploy multiple instances of Flask-SocketIO server using docker. These instances will be running as docker containers. Nginx will be responsible for forwarding client requests to these instances and Redis will be broadcasting server events among all instances.&lt;/p&gt;

</description>
      <category>flasksocketio</category>
      <category>nginx</category>
      <category>redisqueue</category>
      <category>docker</category>
    </item>
    <item>
      <title>Is a portfolio website necessary for a junior backend developer and how can one showcase their skills as a backender?</title>
      <dc:creator>Valentine Sean Chanengeta</dc:creator>
      <pubDate>Fri, 20 May 2022 01:28:24 +0000</pubDate>
      <link>https://dev.to/valentinesean22/is-a-portfolio-website-necessary-for-a-junior-backend-developer-and-how-can-one-showcase-their-skills-as-a-backender-1nli</link>
      <guid>https://dev.to/valentinesean22/is-a-portfolio-website-necessary-for-a-junior-backend-developer-and-how-can-one-showcase-their-skills-as-a-backender-1nli</guid>
      <description></description>
      <category>backend</category>
      <category>portfolio</category>
      <category>beginners</category>
    </item>
    <item>
      <title>VSChool Web App with Flask, Vue, Mongo, Deepgram and Dropbox</title>
      <dc:creator>Valentine Sean Chanengeta</dc:creator>
      <pubDate>Tue, 12 Apr 2022 00:04:04 +0000</pubDate>
      <link>https://dev.to/valentinesean22/vschool-web-app-with-flask-vue-mongo-deepgram-and-dropbox-52o7</link>
      <guid>https://dev.to/valentinesean22/vschool-web-app-with-flask-vue-mongo-deepgram-and-dropbox-52o7</guid>
      <description>&lt;p&gt;— &lt;/p&gt;

&lt;h3&gt;
  
  
  Overview of My Submission
&lt;/h3&gt;

&lt;p&gt;Due to Covid19 global pandemic many tasks are now done online including educational tasks such as content delivery. Many schools now have some of the content delivered through platforms like Google Classroom, Microsoft Teams, etc. with teachers/instructors deliver content to students in different formats such as audio, videos or texts.&lt;/p&gt;

&lt;p&gt;One of the challenges I noticed is that some of teachers' pre-recorded content might have some parts which are not clear to their audience. This might cause students to contact their teachers for clarity or ignore those parts which may result as a distraction.&lt;/p&gt;

&lt;p&gt;This challenge can be solved using Deep gram's powerful weapons which was VSChool's motivation. VSChool web app is a simple platform which allows users to upload an audio file, generate transcriptions which are used to create subtitles for the video that can be played by users.&lt;/p&gt;

&lt;h2&gt;
  
  
  System Flow
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;user can create a subject and topics&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;subject have many topics&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;user can also upload an audio (MP3) for each and every topic&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the system will generate transcriptions and a subtitles file (WEBVTT), the complete process will be explained later&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;it will then upload both MP3 and VTT files to Dropbox&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;it will get MP3 and VTT shared links and save them in the database&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;web app will fetch topics with shared links and render respective media&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MP3 is rendered as a video with its subtitles are obtained from VTT file&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;hence, users will watch a video with subtitles generated by the system&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The process of generating subtitles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;app sends a POST request to a RESTful API with an MP3 audio and topic_id in the payload&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;API will rename the audio file to a topic_id as a new file name&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;it will also create a new directory named after a topic_id and stored in the media_files directory&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the audio file will be saved in this new created directory&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the audio file will then sent to Deepgram for transcription with utterances set to True&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;JSON response from Deepgram is written in a WEBVTT file for video subtitles&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;VTT file is named after topic_id too&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;it is then saved in a directory created earlier (the one named after topic_id)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the directory is then compressed to a ZIP file, in same parent folder&lt;br&gt;
-then the original directory is deleted to only left with a ZIP file&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ZIP file is uploaded to Dropbox&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dropbox destination folder is configured to automatically extract new ZIP files to become folders&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the system will sleep for 10 seconds to wait for Dropbox to extract ZIP files&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;it will then send two requests to create shared links for MP3 and VTT files in new folders in Dropbox&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the system will save these to created shared links to the respective topic&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;these shared links will then be used by the App to play the video with subtitles&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Set up the environment
&lt;/h2&gt;

&lt;p&gt;API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add '.env' file with credentials as follows:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DEEPGRAM_API_KEY=""&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DROPBOX_ACCESS_TOKEN = ""&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DROPBOX_APP_KEY = ""&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DATABASEUSER=""&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DATABASEPASSWORD=""&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CLUSTER=""&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DATABASE=""&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;HOST=""&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;JWT_SECRET_KEY = ""&lt;br&gt;
Please fill blank "" with respective values&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create Python virtual environment in project's folder using command 'python -m venv myenv'&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Activate virtual environment using command 'ENV_NAME\scripts\activate'&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install required libraries in 'requirements.txt' file using command 'pip install -r requirements.txt'&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Start the app using command 'flask run'&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;App:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Prerequisites &lt;a class="mentioned-user" href="https://dev.to/vue"&gt;@vue&lt;/a&gt;/cli 4.5.12 and npm&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run command 'npm install' to install all required libraries&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run command 'npm run serve' to start the app&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo Video
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://youtu.be/_93XB16LWUI" rel="noopener noreferrer"&gt;https://youtu.be/_93XB16LWUI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Submission Category:
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Accessibility Advocates
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Link to Code on GitHub
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Vue App Link: &lt;a href="https://github.com/ValentineSean/v-school-app" rel="noopener noreferrer"&gt;https://github.com/ValentineSean/v-school-app&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;-Flask API Link: &lt;a href="https://github.com/ValentineSean/v-school-api" rel="noopener noreferrer"&gt;https://github.com/ValentineSean/v-school-api&lt;/a&gt;&lt;/p&gt;

</description>
      <category>hackwithdg</category>
      <category>flask</category>
      <category>vue</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>Product Catalog Web App with Flask, Vue and Mongo</title>
      <dc:creator>Valentine Sean Chanengeta</dc:creator>
      <pubDate>Fri, 14 Jan 2022 00:23:36 +0000</pubDate>
      <link>https://dev.to/valentinesean22/product-catalog-web-app-with-flask-vue-and-mongo-3723</link>
      <guid>https://dev.to/valentinesean22/product-catalog-web-app-with-flask-vue-and-mongo-3723</guid>
      <description>&lt;h3&gt;
  
  
  Overview of My Submission
&lt;/h3&gt;

&lt;p&gt;Product Catalog web app is a simple catalog where user named Supplier will create products and Customer will browse these products on the platform.&lt;/p&gt;

&lt;p&gt;The product has few information such as product name, category of the product, supplier of the product, to mention only 3.&lt;/p&gt;

&lt;p&gt;Customer can search based on product name, category name or first name of the supplier (this is done by Flask with Atlas Search). After receiving search results, they can also filter those search results (This is done by Vue in the browser).&lt;/p&gt;

&lt;p&gt;ROLES SUMMARY: Supplier -&amp;gt; (creates product); Customer -&amp;gt; (search among products, rate the product)&lt;/p&gt;

&lt;p&gt;I built the app using Flask, Vue and MongoDB. Vue is responsible for displaying data stored in MongoDB to users and Flask is serving as an interface between MongoDB and Vue. Products' images are stored in Cloudinary using its public API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;E-Commerce Creation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Link to Code
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Flask API link: &lt;a href="https://github.com/ValentineSean/product-catalog-api" rel="noopener noreferrer"&gt;https://github.com/ValentineSean/product-catalog-api&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Vue App link: &lt;a href="https://github.com/ValentineSean/product-catalog-app" rel="noopener noreferrer"&gt;https://github.com/ValentineSean/product-catalog-app&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;App demo Youtube link: &lt;a href="https://youtu.be/DPZ_p9JU5SU" rel="noopener noreferrer"&gt;https://youtu.be/DPZ_p9JU5SU&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;App link:  &lt;a href="https://product-catalog-vsc.netlify.app/" rel="noopener noreferrer"&gt;https://product-catalog-vsc.netlify.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;suppliers credentials: ["&lt;a href="mailto:go@productcatalog.net"&gt;go@productcatalog.net&lt;/a&gt;", "&lt;a href="mailto:john@productcatalog.net"&gt;john@productcatalog.net&lt;/a&gt;"]&lt;/li&gt;
&lt;li&gt;customers credentials: ["&lt;a href="mailto:foo@productcatalog.net"&gt;foo@productcatalog.net&lt;/a&gt;", "&lt;a href="mailto:java@productcatalog.net"&gt;java@productcatalog.net&lt;/a&gt;"]&lt;/li&gt;
&lt;li&gt;all passwords are "123456"&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Database structure and indexes images:
&lt;/h3&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%2Fjn7gzsdgksc8xwe2gko4.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%2Fjn7gzsdgksc8xwe2gko4.png" alt="Product collection"&gt;&lt;/a&gt;&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%2Fbaqv9x5k7fdgsd61zszo.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%2Fbaqv9x5k7fdgsd61zszo.png" alt="Category collection"&gt;&lt;/a&gt;&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%2F82j3dh7ndqcr8t4aagwn.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%2F82j3dh7ndqcr8t4aagwn.png" alt="User collection"&gt;&lt;/a&gt;&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%2Fnhm4f8bm4ojh6se3bw9i.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%2Fnhm4f8bm4ojh6se3bw9i.png" alt="Supplier search index"&gt;&lt;/a&gt;&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%2F600cqrkemrfqlikeqk7z.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%2F600cqrkemrfqlikeqk7z.png" alt="Category search index"&gt;&lt;/a&gt;&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%2F2opl85y75oghiome5ppy.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%2F2opl85y75oghiome5ppy.png" alt="Product search index"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>atlashackathon</category>
      <category>flask</category>
      <category>vue</category>
      <category>mongodb</category>
    </item>
  </channel>
</rss>
