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 handlersmodels.py- Database modelsforms.py- WTFormsinfo.json- Module metadatatests/- Unit and functional testsstatic/- CSS, JS, imagestemplates/- 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__billing/
│ ├── subscriptions/
│ ├── invoices/
│ └── payments/
└── box__blog/
├── posts/
└── comments/
This structure keeps your codebase organized at any scale.
Quick Installation¶
Shopyo requires Python 3.8+
pip3 install shopyo
pip install shopyo
Project Generation¶
The fastest way to start:
shopyo new myproject --demo
This command:
Creates project structure
Copies demo modules (auth, dashboard, settings)
Initializes SQLite database
Creates admin user (admin@domain.com / Pass1234!@#$)
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.dborpostgresql://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 inProductionConfigonly; 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_ACCESSpermission receive a403 Forbidden.DefaultModelViewclasses in the admin require bothis_authenticatedandis_adminon theUsermodel.
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-CSRFTokenheader, orThe
csrf_tokenform field, orThe
csrf_tokenkey 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.