Build Large Flask Apps Without Losing Your Sanity#

Shopyo is a modular Flask framework designed for maintainability, extensibility, and real-world scale.

Why developers choose Shopyo:

  • Modular app architecture with enforced boundaries

  • Built-in admin & authentication system

  • Plugin-based module system

  • Structured project layout that scales

  • Production-ready foundations

# Install
pip install shopyo

# Create project with demo
shopyo new myproject --demo

That’s it. Your Flask app is running at http://localhost:5000

The Problem: Flask Doesn’t Scale Cleanly#

Your Flask app starts small. Then features pile on. Suddenly:

  • Blueprint chaos - Blueprints everywhere, no clear organization

  • Repeated auth setups - Every project means rewriting login, registration, password reset

  • Admin reimplementation - Building admin panels from scratch repeatedly

  • Tight coupling - Modules importing each other directly, creating spaghetti

  • No modular enforcement - Nothing prevents your app from becoming a monolith

  • Hard-to-reuse components - Copy-paste between projects instead of modular code

You’re not building an app. You’re building technical debt.

The Solution: Modular Flask Done Right#

Shopyo enforces modular boundaries so your Flask app doesn’t collapse under its own weight.

What is a Shopyo module?

A self-contained unit with:

  • view.py - HTTP handlers

  • models.py - Database models

  • forms.py - WTForms

  • info.json - Module metadata

  • tests/ - Unit and functional tests

  • static/ - CSS, JS, images

  • templates/ - Jinja2 templates

Modules are isolated. They can’t import each other directly. Communication happens through events.

What is a Box?

A box groups related modules:

modules/
├── box__ecommerce/
│   ├── products/
│   ├── orders/
│   └── customers/
└── box__blog/
    ├── posts/
    └── comments/

This structure keeps your codebase organized at any scale.

Quick Installation#

Shopyo requires Python 3.8+

pip3 install shopyo

Project Generation#

The fastest way to start:

shopyo new myproject --demo

This command:

  1. Creates project structure

  2. Copies demo modules (auth, dashboard, settings)

  3. Initializes SQLite database

  4. Creates admin user (admin@domain.com / Pass1234!@#$)

  5. Starts development server

Manual Setup#

Want more control?

mkdir blog
cd blog
shopyo new              # Create project
shopyo env              # Generate .env
shopyo initialise      # Setup database
flask run --debug       # Start server

For the home page it will say Shopyo is now running! which will be the home page of your app (see blog/blog/modules/www/view.py and blog/static/themes/front/blogus/index.html). To access the dashboard, go to http://localhost:5000/auth/login and login with email admin@domain.com and password Pass1234!@#$

See new for more details.

Environment Variables#

Shopyo can be configured using environment variables. This is especially useful for production deployments.

Warning

The ProductionConfig requires SECRET_KEY and SQLALCHEMY_DATABASE_URI to be set via environment variables. If either is missing, the application will refuse to boot with a RuntimeError. Hardcoded defaults are not allowed in production.

Essential Variables#

  • SECRET_KEY (required in production): A long random string used to secure session cookies and other crypto needs.

  • SQLALCHEMY_DATABASE_URI (required in production): The database connection URI (e.g., sqlite:///shopyo.db or postgresql://user:Pass1234!@#$@localhost/dbname).

  • PASSWORD_SALT: Salt for token signing. Auto-generated as a random 64-char hex string if not set.

Session Security#

By default, Shopyo applies the following session cookie hardening flags:

  • SESSION_COOKIE_HTTPONLY = True — prevents JavaScript access to the session cookie (mitigates XSS-based session theft).

  • SESSION_COOKIE_SAMESITE = "Lax" — prevents the cookie from being sent in cross-site requests (mitigates CSRF).

  • SESSION_COOKIE_SECURE = True — only sent over HTTPS (set in ProductionConfig only; development/testing use HTTP).

These defaults are set in BaseConfig and inherited by all environments. You can override them in your app’s config:

class MyConfig(ProductionConfig):
    SESSION_COOKIE_SAMESITE = "Strict"  # even stricter cross-site policy

Admin Panel Security#

Shopyo’s admin panel uses the policy engine (see Policy Engine Tutorial) to gate access:

  • Unauthenticated users are redirected to the login page.

  • Authenticated users without ADMIN_PANEL_ACCESS permission receive a 403 Forbidden.

  • DefaultModelView classes in the admin require both is_authenticated and is_admin on the User model.

The DefaultModelView.is_accessible() method has been hardened to fix a previously unreachable guard: the old not current_user.is_authenticated and current_user.is_admin condition could never be true (AnonymousUser.is_admin always returns False), so unauthenticated users silently bypassed the check. The fix splits the guard into a proper unauthenticated redirect followed by a policy-based authorization check.

Cross-Site Request Forgery (CSRF) Protection#

CSRF protection is handled globally by Flask-WTF CSRFProtect, which is initialised in shopyo/init.py and protects all unsafe HTTP methods (POST, PUT, PATCH, DELETE) on form-based routes.

For HTML forms — include the CSRF token in every form:

<form method="POST">
    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
    ...
</form>

Flask-WTF forms automatically include a hidden CSRF field when using form.hidden_tag().

For API clients — send the CSRF token via:

  • The X-CSRFToken header, or

  • The csrf_token form field, or

  • The csrf_token key in a JSON request body

To get the CSRF token programmatically:

from shopyo.api.security import generate_csrf_token, validate_csrf_token

# Obtain the current token
token = generate_csrf_token()

# Validate a token submitted by a client
if validate_csrf_token(submitted_token):
    ...

The legacy @csrf_protect decorator has been removed — it was redundant with Flask-WTF’s global CSRFProtect middleware.

Email Configuration#

If you have email confirmation enabled, you must configure the following:

  • MAIL_SERVER: SMTP server address.

  • MAIL_PORT: SMTP server port.

  • MAIL_USERNAME: Username for the email server.

  • MAIL_PASSWORD: Password for the email server.

  • MAIL_DEFAULT_SENDER: Email address used as the sender.

Seeding Configuration#

These variables are used during the shopyo initialise command to create the initial admin user:

  • SHOPYO_AUTH_SEED_ADMIN_EMAIL: Email for the seed admin user (no default — seeding is skipped if unset).

  • SHOPYO_AUTH_SEED_ADMIN_PASSWORD: Password for the seed admin user (no default — seeding is skipped if unset).

Why Not Plain Flask?#

Feature

Plain Flask

Flask + Blueprints

Shopyo

Modular Enforcement

⚠️ Manual

Plugin System

Built-in Auth

Admin UI

Structured Scaling

⚠️

FAQ#

Is Shopyo production-ready?#

Yes. Shopyo powers live applications including Maurilearn.com (eLearning platform), Linkolearn.com (Learn By links), and FlaskCon.com (Conference software).

How does Shopyo compare to Django?#

Shopyo gives you Django’s modularity with Flask’s flexibility. You get structure without the magic. Use only what you need.

Can I use Shopyo without the admin?#

Yes. Every module is optional. Delete what you don’t need.

Is it compatible with Flask extensions?#

Absolutely. Shopyo is Flask. All Flask extensions work.

How do modules communicate?#

Modules communicate through Shopyo’s event system. Import get_module from shopyo.api.module to dispatch and listen for events.

Can I deploy with Gunicorn?#

Yes. Shopyo works with any WSGI server:

gunicorn "app:create_app('production')" -w 4

How does Shopyo handle migrations?#

Shopyo uses Flask-Migrate. Run flask db init and flask db migrate as usual.

Is it suitable for microservices?#

Yes. Each Shopyo module can be extracted into a separate service while maintaining the same development experience.

Does it enforce database structure?#

No. Shopyo uses SQLAlchemy. You define your models however you want.

Can I build APIs only?#

Yes. Create modules without templates. Use Flask-RESTX or any API library.