<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>https://shopyo.readthedocs.io/en/latest/</id>
  <title>Shopyo Blog</title>
  <updated>2026-05-30T16:42:53.744206+00:00</updated>
  <link href="https://shopyo.readthedocs.io/en/latest/"/>
  <link href="https://shopyo.readthedocs.io/en/latest/blog/atom.xml" rel="self"/>
  <generator uri="https://ablog.readthedocs.io/" version="0.11.8">ABlog</generator>
  <subtitle>Building large Flask apps without losing your sanity</subtitle>
  <entry>
    <id>https://shopyo.readthedocs.io/en/latest/blog/why-flask-blueprints-arent-enough.html</id>
    <title>Why Flask Blueprints Aren’t Enough for Large Apps</title>
    <updated>2026-05-28T00:00:00+00:00</updated>
    <author>
      <name>Shopyo Team</name>
    </author>
    <content type="html">&lt;section id="why-flask-blueprints-aren-t-enough-for-large-apps"&gt;

&lt;p&gt;Flask blueprints are the recommended way to organize large Flask applications. They’re a solid tool — but they’re not a complete solution. As your application grows beyond a certain size, blueprints alone leave critical gaps that turn your codebase into technical debt.&lt;/p&gt;
&lt;p&gt;This post explains what blueprints do well, where they fall short, and what a complete modular architecture looks like.&lt;/p&gt;
&lt;section id="what-blueprints-do-well"&gt;
&lt;h2&gt;What Blueprints Do Well&lt;/h2&gt;
&lt;p&gt;Blueprints solve the problem of route organization. Instead of one massive &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;app.py&lt;/span&gt;&lt;/code&gt; with 500 route decorators, you split routes into logical groups:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# auth/views.py&lt;/span&gt;
&lt;span class="n"&gt;auth_bp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Blueprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;auth&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@auth_bp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/login&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="nd"&gt;@auth_bp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/register&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This is a meaningful improvement. It’s the first step every Flask developer should take. But it’s only the first step.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="where-blueprints-fall-short"&gt;
&lt;h2&gt;Where Blueprints Fall Short&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. No model isolation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Blueprints only organize views. Your models still live in a single &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;models.py&lt;/span&gt;&lt;/code&gt; or are scattered across files with no clear ownership. A module that owns its views should also own its models, forms, and templates.&lt;/p&gt;
&lt;div class="highlight-text notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;# What most blueprint-based apps look like:
auth/
├── views.py    # routes
models.py        # all models, including auth models
forms.py         # all forms, including auth forms
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Auth models shouldn’t live next to billing models any more than auth routes should live next to billing routes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. No template encapsulation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Blueprints can have a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;template_folder&lt;/span&gt;&lt;/code&gt;, but there’s no convention for how templates should be organized. Without enforced structure, templates end up in a flat directory with hundreds of files and unclear ownership.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. No auto-discovery&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Every blueprint must be manually imported and registered in your app factory:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;auth.views&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;auth_bp&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;billing.views&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;billing_bp&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dashboard.views&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dashboard_bp&lt;/span&gt;
&lt;span class="c1"&gt;# ... 20 more imports&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register_blueprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth_bp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register_blueprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;billing_bp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register_blueprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dashboard_bp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# ... 20 more registrations&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Forget to import one? Your route silently doesn’t exist. This is fragile and doesn’t scale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. No enforcement of module boundaries&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Nothing stops &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;billing/views.py&lt;/span&gt;&lt;/code&gt; from importing &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;auth/models.py&lt;/span&gt;&lt;/code&gt; directly. Over time, your modules become tightly coupled. You can’t test, refactor, or remove a module without fear.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. No built-in module assets&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Serving static files for a module (CSS, JS, images) requires manual path configuration. There’s no standard way to say “these assets belong to this module.”&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-modular-architecture-blueprints-boundaries"&gt;
&lt;h2&gt;The Modular Architecture: Blueprints + Boundaries&lt;/h2&gt;
&lt;p&gt;The solution isn’t to abandon blueprints — it’s to add the missing layers around them. A true modular architecture adds:&lt;/p&gt;
&lt;table class="docutils align-default"&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head"&gt;&lt;p&gt;Concern&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Blueprints Only&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Modular Architecture&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;Views&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅ Organized&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅ Organized&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;Models&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;❌ Global&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅ Per-module&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;Forms&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;❌ Global&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅ Per-module&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;Templates&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;❌ Flat&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅ Per-module, namespaced&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;Static assets&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;❌ Manual&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅ Per-module, auto-served&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;Auto-discovery&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;❌ Manual imports&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅ Drop-in registration&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;Boundary enforcement&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;❌ None&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅ Convention + tooling&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;Tests&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;❌ Centralized&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅ Per-module&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/section&gt;
&lt;section id="how-shopyo-bridges-the-gap"&gt;
&lt;h2&gt;How Shopyo Bridges the Gap&lt;/h2&gt;
&lt;p&gt;Shopyo was built specifically to solve these problems. Every Shopyo module is a self-contained unit with its own views, models, forms, templates, tests, and static assets:&lt;/p&gt;
&lt;div class="highlight-text notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;modules/billing/
├── __init__.py
├── view.py        # routes (blueprint)
├── models.py      # billing models only
├── forms.py       # billing forms only
├── global.py      # template variables, configs
├── info.json      # module metadata
├── static/        # billing-specific CSS/JS
├── templates/
│   └── billing/   # namespaced templates
└── tests/
    ├── test_models.py
    └── test_views.py
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Modules are auto-discovered — you just drop them in the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;modules/&lt;/span&gt;&lt;/code&gt; folder. The blueprint is registered automatically. Static assets are served transparently in both development and production. Module boundaries are enforced by convention (no cross-module imports) and supported by the framework.&lt;/p&gt;
&lt;p&gt;This is what blueprints should have been from the start: a complete unit of functionality, not just a router with a name tag.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="next-steps-for-modular-flask"&gt;
&lt;h2&gt;Next Steps for Modular Flask&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="usage.html"&gt;&lt;span class="doc"&gt;Build Large Flask Apps Without Losing Your Sanity&lt;/span&gt;&lt;/a&gt; — Build your first modular Flask app&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="architecture.html"&gt;&lt;span class="doc"&gt;Architecture&lt;/span&gt;&lt;/a&gt; — How Shopyo’s module system works&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="modules.html"&gt;&lt;span class="doc"&gt;Modules/Apps&lt;/span&gt;&lt;/a&gt; — Creating and managing modules&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://shopyo.readthedocs.io/en/latest/blog/why-flask-blueprints-arent-enough.html"/>
    <summary>Flask blueprints are the recommended way to organize large Flask applications. They’re a solid tool — but they’re not a complete solution. As your application grows beyond a certain size, blueprints alone leave critical gaps that turn your codebase into technical debt.</summary>
    <category term="architecture" label="architecture"/>
    <category term="best-practices" label="best-practices"/>
    <category term="blueprints" label="blueprints"/>
    <category term="flask" label="flask"/>
    <published>2026-05-28T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://shopyo.readthedocs.io/en/latest/blog/how-to-structure-a-flask-app.html</id>
    <title>How to Structure a Flask App That Will Scale to 100k Users</title>
    <updated>2026-05-28T00:00:00+00:00</updated>
    <author>
      <name>Shopyo Team</name>
    </author>
    <content type="html">&lt;section id="how-to-structure-a-flask-app-that-will-scale-to-100k-users"&gt;

&lt;p&gt;You started with a single &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;app.py&lt;/span&gt;&lt;/code&gt;. It felt clean. Then you added routes. Then a database. Then authentication. By the time you hit 3,000 lines, you couldn’t find anything. By 10,000 lines, you were afraid to touch it.&lt;/p&gt;
&lt;p&gt;This is the #1 problem Flask developers face — and there’s almost no good documentation on how to solve it. This guide walks you through a proven architecture that scales from a weekend project to 100,000 users.&lt;/p&gt;
&lt;section id="the-problem-flask-s-flexibility-is-a-double-edged-sword"&gt;
&lt;h2&gt;The Problem: Flask’s Flexibility Is a Double-Edged Sword&lt;/h2&gt;
&lt;p&gt;Flask’s greatest strength — minimalism — becomes its greatest weakness as your app grows. The framework gives you blueprints and tells you “good luck.” There’s no enforced structure, no recommended layout for large apps, and no built-in way to organize features into isolated units.&lt;/p&gt;
&lt;p&gt;The result? Most large Flask projects end up looking like this:&lt;/p&gt;
&lt;div class="highlight-text notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;myapp/
├── app.py           # 5,000 lines
├── models.py        # 2,000 lines
├── forms.py         # 1,000 lines
├── helpers.py       # 800 lines
└── templates/       # 50+ files, no structure
     ├── login.html
     ├── dashboard.html
     ├── admin.html
     └── ...
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This doesn’t scale. You can’t find anything. Every change risks breaking something unrelated. New developers on the team spend weeks learning the undocumented structure.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-solution-modular-architecture"&gt;
&lt;h2&gt;The Solution: Modular Architecture&lt;/h2&gt;
&lt;p&gt;Instead of organizing by &lt;em&gt;file type&lt;/em&gt; (views, models, forms), organize by &lt;em&gt;feature&lt;/em&gt; (auth, billing, dashboard, blog). Each feature is a self-contained module with its own views, models, forms, templates, and tests.&lt;/p&gt;
&lt;div class="highlight-text notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;myapp/
├── modules/
│   ├── auth/
│   │   ├── view.py
│   │   ├── models.py
│   │   ├── forms.py
│   │   ├── tests/
│   │   └── templates/
│   │       └── auth/
│   ├── billing/
│   │   ├── view.py
│   │   ├── models.py
│   │   ├── forms.py
│   │   ├── tests/
│   │   └── templates/
│   │       └── billing/
│   └── dashboard/
│       ├── view.py
│       ├── models.py
│       ├── tests/
│       └── templates/
│           └── dashboard/
├── app.py
├── config.py
└── requirements.txt
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Each module is a Flask blueprint that registers itself. No module imports another module’s internals. Communication happens through events and well-defined interfaces.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="why-this-scales"&gt;
&lt;h2&gt;Why This Scales&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Isolation&lt;/strong&gt; — A bug in &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;billing/&lt;/span&gt;&lt;/code&gt; can’t crash &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;auth/&lt;/span&gt;&lt;/code&gt;. You can rewrite an entire module without touching the rest of the app.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Discoverability&lt;/strong&gt; — Need to change how login works? Open &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;modules/auth/view.py&lt;/span&gt;&lt;/code&gt;. It’s obvious where everything lives.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Testability&lt;/strong&gt; — Each module has its own &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;tests/&lt;/span&gt;&lt;/code&gt; directory. Tests are fast because they only load the module they need.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Team scaling&lt;/strong&gt; — Five developers can work on five different modules simultaneously with zero merge conflicts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reusability&lt;/strong&gt; — Need auth in your next project? Copy the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;auth/&lt;/span&gt;&lt;/code&gt; module. It’s self-contained.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;section id="the-honest-trade-off"&gt;
&lt;h2&gt;The Honest Trade-Off&lt;/h2&gt;
&lt;p&gt;This architecture requires more upfront structure than a single &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;app.py&lt;/span&gt;&lt;/code&gt;. You need to think about module boundaries before you start coding. For a 100-line API, this is overkill. For anything that will grow beyond a few thousand lines, it’s the difference between a maintainable codebase and a nightmare.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="getting-started-without-the-boilerplate"&gt;
&lt;h2&gt;Getting Started Without the Boilerplate&lt;/h2&gt;
&lt;p&gt;Setting up this architecture manually takes about an hour. You need to:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Create the folder structure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set up Flask blueprints for each module&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wire up auto-discovery so modules register themselves&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure static file serving for each module&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set up tests for each module&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add authentication, an admin panel, and user management&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Or you can use a framework that does all of this for you.&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;shopyo
shopyo&lt;span class="w"&gt; &lt;/span&gt;new&lt;span class="w"&gt; &lt;/span&gt;myapp
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;myapp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;shopyo&lt;span class="w"&gt; &lt;/span&gt;initialise&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;flask&lt;span class="w"&gt; &lt;/span&gt;run
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This gives you the architecture above — with auth, admin dashboard, auto-discovery, and scaffolding — running in 30 seconds. Every module you create with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;shopyo&lt;/span&gt; &lt;span class="pre"&gt;startapp&lt;/span&gt;&lt;/code&gt; follows the same structure automatically.&lt;/p&gt;
&lt;p&gt;The key insight: &lt;strong&gt;your project structure should be a feature of your framework, not a decision you make every time you start a project.&lt;/strong&gt; Shopyo enforces the modular architecture described in this guide so you don’t have to think about it.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="further-reading"&gt;
&lt;h2&gt;Further Reading&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="usage.html"&gt;&lt;span class="doc"&gt;Build Large Flask Apps Without Losing Your Sanity&lt;/span&gt;&lt;/a&gt; — Full guide to building apps with Shopyo&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="architecture.html"&gt;&lt;span class="doc"&gt;Architecture&lt;/span&gt;&lt;/a&gt; — Deep dive into Shopyo’s modular design&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="modules.html"&gt;&lt;span class="doc"&gt;Modules/Apps&lt;/span&gt;&lt;/a&gt; — How modules and boxes work&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://shopyo.readthedocs.io/en/latest/blog/how-to-structure-a-flask-app.html"/>
    <summary>You started with a single app.py. It felt clean. Then you added routes. Then a database. Then authentication. By the time you hit 3,000 lines, you couldn’t find anything. By 10,000 lines, you were afraid to touch it.</summary>
    <category term="architecture" label="architecture"/>
    <category term="flask" label="flask"/>
    <category term="scaling" label="scaling"/>
    <category term="tutorial" label="tutorial"/>
    <published>2026-05-28T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://shopyo.readthedocs.io/en/latest/blog/how-to-add-auth-to-flask.html</id>
    <title>How to Add Auth to Flask Without Going Insane</title>
    <updated>2026-05-28T00:00:00+00:00</updated>
    <author>
      <name>Shopyo Team</name>
    </author>
    <content type="html">&lt;section id="how-to-add-auth-to-flask-without-going-insane"&gt;

&lt;p&gt;Authentication is the most-reimplemented feature in the Flask ecosystem. Every Flask developer has written a login system at least once. Most have written it three or four times, across different projects, with slightly different bugs each time.&lt;/p&gt;
&lt;p&gt;This post walks through what a production-grade auth system needs — and how to get it without building it from scratch.&lt;/p&gt;
&lt;section id="the-minimum-viable-auth-stack"&gt;
&lt;h2&gt;The Minimum Viable Auth Stack&lt;/h2&gt;
&lt;p&gt;A production-ready auth system needs:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User registration&lt;/strong&gt; with email verification&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Login/logout&lt;/strong&gt; with session management&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Password reset&lt;/strong&gt; with secure tokens&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Password complexity&lt;/strong&gt; enforcement (length, character classes)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rate limiting&lt;/strong&gt; on login and registration endpoints&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CSRF protection&lt;/strong&gt; on all forms&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;API token authentication&lt;/strong&gt; for programmatic access&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Role-based access control&lt;/strong&gt; for permissions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Session hardening&lt;/strong&gt; (HttpOnly, SameSite, Secure cookies)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s look at what it takes to build this from scratch.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="building-auth-with-flask-extensions"&gt;
&lt;h2&gt;Building Auth with Flask Extensions&lt;/h2&gt;
&lt;p&gt;The standard approach is wiring together several extensions:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;flask-login&lt;span class="w"&gt; &lt;/span&gt;flask-wtf&lt;span class="w"&gt; &lt;/span&gt;itsdangerous&lt;span class="w"&gt; &lt;/span&gt;bcrypt&lt;span class="w"&gt; &lt;/span&gt;email-validator
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Then implementing models, forms, views, email-sending, token generation, and rate limiting manually. For a typical app, this is &lt;strong&gt;300-500 lines of code&lt;/strong&gt; spread across 6-8 files. And you still need to:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Write email templates for verification and password reset&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement rate limiting with Flask-Limiter&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add password complexity validation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Secure session cookies in &lt;cite&gt;config.py&lt;/cite&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write the HTML templates for login, register, password reset&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handle edge cases (expired tokens, already-verified users, brute force attempts)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most Flask auth implementations have at least one of these common bugs:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Timing-attack-vulnerable token comparison&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Session fixation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No account lockout&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Logging passwords in error messages&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Missing CSRF on login forms&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Predictable password reset tokens&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="the-shopyo-approach"&gt;
&lt;h2&gt;The Shopyo Approach&lt;/h2&gt;
&lt;p&gt;Shopyo’s auth module (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;shopyo_auth&lt;/span&gt;&lt;/code&gt;) gives you all of the above in one install:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;shopyo
shopyo&lt;span class="w"&gt; &lt;/span&gt;new&lt;span class="w"&gt; &lt;/span&gt;myapp
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;myapp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;shopyo&lt;span class="w"&gt; &lt;/span&gt;initialise&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;flask&lt;span class="w"&gt; &lt;/span&gt;run
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This gives you a working auth system at &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;/auth/login&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;/auth/register&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;/auth/reset-password&lt;/span&gt;&lt;/code&gt; — with all of the following built in:&lt;/p&gt;
&lt;table class="docutils align-default"&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head"&gt;&lt;p&gt;Feature&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Status&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;Email/password registration&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;Email verification&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;Password reset flow&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;Password complexity (min 12 chars, mixed case, digits, symbols)&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅ (default)&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;Rate limiting (per-endpoint)&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅ (default)&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;Session cookies (HttpOnly, SameSite=Lax, Secure in prod)&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;CSRF protection&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅ (global)&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;API token auth (PBKDF2-HMAC hashed)&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;Role-based permissions&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅ (policy engine)&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;Persistent “remember me”&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;Custom login/register templates&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;✅&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;You also get the full test suite — the auth module has 11 test files covering models, forms, roles, tokens, rate limiting, policy, events, and password complexity.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="configuring-auth"&gt;
&lt;h2&gt;Configuring Auth&lt;/h2&gt;
&lt;p&gt;Auth behavior is controlled by environment variables and config keys:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# config.py&lt;/span&gt;
&lt;span class="n"&gt;SHOPYO_AUTH_RATE_LIMIT_LOGIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;5 per minute&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;SHOPYO_AUTH_RATE_LIMIT_REGISTER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;2 per hour&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;SHOPYO_AUTH_PASSWORD_COMPLEXITY_ENABLED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;span class="n"&gt;SHOPYO_AUTH_MIN_PASSWORD_LENGTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;
&lt;span class="n"&gt;SHOPYO_AUTH_SEED_ADMIN_EMAIL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;admin@example.com&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;SHOPYO_AUTH_SEED_ADMIN_PASSWORD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;  &lt;span class="c1"&gt;# must be set via env&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The setup is intentionally secure-by-default. Rate limiting and password complexity are enabled out of the box. Hardcoded secret defaults are removed in v5 — everything comes from environment variables in production.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="why-this-matters"&gt;
&lt;h2&gt;Why This Matters&lt;/h2&gt;
&lt;p&gt;Authentication is not a differentiator for your product. It’s table stakes. Every hour you spend debugging password reset tokens is an hour you’re not building features your users actually care about.&lt;/p&gt;
&lt;p&gt;Using a pre-built auth system isn’t “lazy” — it’s the responsible choice. The auth module in Shopyo has been tested across multiple Python versions (3.7–3.13), operating systems (Linux, macOS, Windows), and production deployments (Maurilearn.com, FlaskCon.com). It has 71%+ test coverage, a security policy, and active maintenance.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="next-steps-for-shopyo-auth"&gt;
&lt;h2&gt;Next Steps for Shopyo Auth&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="auth_tutorial.html"&gt;&lt;span class="doc"&gt;Authentication &amp;amp; Authorization&lt;/span&gt;&lt;/a&gt; — Deep dive into Shopyo’s auth system&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="policy_tutorial.html"&gt;&lt;span class="doc"&gt;Policy Engine Tutorial&lt;/span&gt;&lt;/a&gt; — Role-based access control with the policy engine&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="quickstart.html"&gt;&lt;span class="doc"&gt;Quickstart&lt;/span&gt;&lt;/a&gt; — Get Shopyo running in 10 minutes&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://shopyo.readthedocs.io/en/latest/blog/how-to-add-auth-to-flask.html"/>
    <summary>Authentication is the most-reimplemented feature in the Flask ecosystem. Every Flask developer has written a login system at least once. Most have written it three or four times, across different projects, with slightly different bugs each time.</summary>
    <category term="auth" label="auth"/>
    <category term="authentication" label="authentication"/>
    <category term="flask" label="flask"/>
    <category term="security" label="security"/>
    <category term="tutorial" label="tutorial"/>
    <published>2026-05-28T00:00:00+00:00</published>
  </entry>
</feed>
