Flask Project Structure Patterns (Using Blueprints)
Flask Project Structure Patterns (Using Blueprints)
When building a Flask application, the way you organize your files and modules can greatly affect
maintainability and scalability. Flask is flexible about project structure, but certain patterns have emerged
as best practices – especially if you aim for production-ready code and want to impress recruiters with a
professional layout. Below, we discuss common structure options (from simple to advanced), their pros/
cons, use cases (REST APIs vs web apps), and examples using Blueprints.
myapp/
├── app.py # All routes and app setup here (Flask instance created
globally)
├── templates/ # Jinja2 templates (if a web app)
│ └── home.html
└── static/ # Static assets (CSS, JS, images) for web apps
Use Case: This flat structure is acceptable for small, single-purpose applications or learning exercises.
Flask’s documentation even shows simple apps in one file for quick starts.
Pros:
• Simplicity: Easy to set up and understand for a beginner. All code in one place – just run one file to
start the app.
• Quick Prototyping: Great for throwaway demos or very basic APIs; minimal boilerplate.
Cons:
• Poor Scalability: As the app grows (more routes, models, etc.), one file or a few files become large
and messy, which is hard to maintain 1 . Features and concerns get intermixed.
• Difficult Maintenance: Finding or updating code is harder when logic isn’t separated. It’s easy to
introduce bugs when everything is tightly coupled.
• Not Test-Friendly: Often uses a global app instance. A global app variable makes it tricky to run
isolated tests or use different configurations, since all tests share the same app state 2 .
• Not Impressive for Larger Projects: Recruiters or other developers may view a single-file large app
as unstructured or “amateur” for non-trivial projects. It suggests the project wasn’t designed with
growth in mind.
1
Example: A simple “Hello World” Flask app or a basic CRUD API with one or two routes might use this
structure. However, Flask’s own docs recommend restructuring as soon as the app grows: “writing the code
for a large application in one file will quickly become messy and hard to manage” 1 . In practice, any app
beyond a toy example should move to a modular structure.
Blueprints in Flask allow you to break your app into components or sub-applications. Each Blueprint can
have its own routes, view functions, static files, and templates, all encapsulated in a separate module or
package 3 4 . You register these blueprints with the main Flask app. This modular approach means each
part of the application has its code isolated, making it easier to manage and reuse functionality 4 5 . For
example, you might have an auth blueprint for authentication routes, a blog blueprint for blog post routes,
etc., each with its own logic.
An application factory is a function (often called create_app ) that creates and configures the Flask
application instance on the fly instead of at import time. This pattern avoids using a single global app
object. Instead, you initialize extensions (database, login manager, etc.) within this factory and register
blueprints there. Using a factory makes it easy to create multiple instances of the app with different settings
(useful for testing vs production) 2 . Miguel Grinberg (author of Flask Mega-Tutorial) notes that not using a
global app but an app factory is a “better solution” to allow testing with different configs and to avoid tests
interfering with each other 2 .
Why Blueprints & Factory are Professional: This pattern is common in industry and open-source Flask
projects. It shows you can structure code for growth. In fact, a well-organized project structure makes code
management easier and facilitates collaboration among developers 6 . Recruiters or senior engineers
reviewing your code will recognize the Blueprint + factory architecture as a sign that you understand
scalable design.
There isn’t a single “official” structure, but the following layout (and slight variations of it) are considered
good practice for a Flask project:
myapp/
├── app/ # Application package
│ ├── __init__.py # App factory (create_app), blueprint registration,
2
config setup
│ ├── extensions.py # Initialize extensions (db, login_manager, etc.)
│ ├── models.py # Database models (or a models/ package)
│ ├── config.py # (Optional) App-specific config classes (alt: use
project-level config.py)
│ ├── auth/ # Blueprint package for authentication features
│ │ ├── __init__.py # Creates Blueprint instance (e.g. auth_bp)
│ │ ├── routes.py # Routes/endpoints for auth (login, signup, etc.)
│ │ └── forms.py # Forms or other logic (if needed for this
blueprint)
│ ├── blog/ # Another feature blueprint (e.g. blog posts)
│ │ ├── __init__.py # Creates Blueprint instance (e.g. blog_bp)
│ │ └── routes.py # Routes for blog posts
│ ├── templates/ # Jinja2 templates (organized by blueprint)
│ │ ├── base.html # Base layout
│ │ ├── auth/ # Templates for auth blueprint
│ │ │ └── login.html
│ │ └── blog/ # Templates for blog blueprint
│ │ └── post_list.html
│ ├── static/ # Static files (CSS, JS, images)
│ └── ... (other blueprints or modules as needed)
├── migrations/ # Database migration scripts (from Flask-Migrate)
├── tests/ # Automated tests (if present, to validate
functionality)
├── config.py # Configuration (for different environments, e.g.
Dev, Prod)
├── requirements.txt # Python dependencies
└── run.py (or wsgi.py) # Application entry point to run the Flask app
• The app/ package contains the application code. The presence of app/__init__.py makes it a
package and typically houses the create_app() factory. In the factory, you would import and
register each Blueprint: for example, app.register_blueprint(auth_bp, url_prefix="/
auth") for the auth routes, etc. This file also loads configuration (possibly via
app.config.from_object(…) ), and initializes extensions by calling their .init_app(app)
methods.
• An extensions.py is often used to create instances of Flask extensions (database, login manager,
encryption, etc.) without attaching them to the app immediately. For instance, db =
SQLAlchemy() , login_manager = LoginManager() , bcrypt = Bcrypt() . In
create_app() , you then call db.init_app(app) , login_manager.init_app(app) , etc. This
avoids circular imports and keeps the app factory clean 7 . It’s not mandatory, but a common
pattern.
• Each feature or component (e.g. auth , blog above) is organized into a Blueprint package. The
blueprint’s __init__.py creates a Blueprint object (e.g. auth_bp = Blueprint('auth',
__name__, template_folder='auth', url_prefix='/auth') ) and may import related routes
3
or other code. The routes.py (or views.py ) within defines the actual route functions, using
@auth_bp.route(...) decorators instead of @app.route . This encapsulation means you can
enable/disable or modify that part of the app in isolation. As Miguel Grinberg points out, the
blueprints feature of Flask helps achieve a more practical organization that makes it easier to reuse code
4 – for example, you could potentially pull out the entire auth folder to use in another project.
• The templates directory is organized by blueprint name (as subfolders) in this example. Flask by
default looks in a central templates/ folder, so a convention is to namespace templates by feature
(e.g. auth/login.html ). Alternatively, Flask Blueprints can be configured with their own template
folders 8 – meaning you could store template files inside each blueprint package and specify
Blueprint(..., template_folder='templates') . The approach you choose is personal
preference; keeping all templates under one directory with subfolders, or within each blueprint’s
folder, are both supported 8 .
• Static files (if any) can also be similarly namespaced. By default all files in app/static/ are
served at /static/<filename> . If you prefer, each blueprint can have its own static folder by
specifying static_folder in the Blueprint constructor.
• Models: In the above layout, we show a single models.py for simplicity. In larger projects, you
might break models into multiple files or a models/ package (for example, models/user.py ,
models/post.py , etc., as in some tutorials 9 ). The models can be imported in the app or within
blueprints as needed. All models typically share a single database instance ( db ) from
extensions.py .
• Configuration: The project’s configuration can be managed in a config.py (at the root or inside
the app package). A common practice is to define configuration classes for different environments
(Development, Testing, Production) and use an environment variable to choose one. For example,
you might have class ProdConfig(Config): ... and class DevConfig(Config):
DEBUG=True , etc., and then do app.config.from_object(ProdConfig) in the factory for
production. This separation ensures secrets like database URIs or API keys aren’t hard-coded and can
vary by deployment environment 10 11 .
• Migrations: When using Flask-Migrate (which uses Alembic under the hood), running flask db
init will create a migrations/ folder (usually at the project root by default). This folder contains
versioned migration scripts for database schema changes 12 . In a professional project, you check
this into version control so that others (or your deployment) can apply the same DB schema.
• Tests: A dedicated tests/ directory (parallel to app/) is typical for larger projects. This holds test
modules (using pytest or unittest ) that verify your routes and logic. Including tests is a great
way to show a production-ready mindset (and definitely impresses recruiters, as it shows you value
correctness and maintainability) 13 14 .
Example Structure (Web App): Suppose we are building a blog website with user authentication. We might
have an auth blueprint (for login/logout, registration, password hashing with Flask-Bcrypt, user model
with Flask-Login integration) and a blog blueprint (for creating and viewing posts). The file layout could
resemble:
myblog/
├── app/
│ ├── __init__.py # create_app sets up Flask, DB, registers
blueprints
│ ├── extensions.py # db = SQLAlchemy(), migrate = Migrate(), login_mgr
4
= LoginManager(), etc.
│ ├── models.py # User, Post model classes (using db.Model)
│ ├── auth/
│ │ ├── __init__.py # auth_bp blueprint creation and registration of
routes
│ │ ├── routes.py # routes: /login, /logout, /register
│ │ └── forms.py # WTForms classes for login/register forms
│ ├── blog/
│ │ ├── __init__.py # blog_bp blueprint
│ │ └── routes.py # routes: /posts, /post/<id>, etc.
│ ├── templates/
│ │ ├── auth/ # auth-related templates
│ │ │ └── login.html
│ │ ├── blog/ # blog-related templates
│ │ │ └── post_list.html
│ │ └── base.html # base layout template
│ └── static/
│ └── style.css
├── migrations/
├── tests/
├── config.py # Config classes (with SECRET_KEY, DATABASE_URI,
etc.)
├── requirements.txt
└── run.py # perhaps to run `app = create_app()` if using
`flask run`
# app/__init__.py
from flask import Flask
from app.extensions import db, migrate, login_mgr, bcrypt
from app.config import ProdConfig
def create_app(config_class=ProdConfig):
app = Flask(__name__)
app.config.from_object(config_class)
# Initialize Flask extensions
db.init_app(app)
migrate.init_app(app, db)
login_mgr.init_app(app)
bcrypt.init_app(app)
# Register blueprints
from app.auth import auth_bp
from app.blog import blog_bp
app.register_blueprint(auth_bp, url_prefix="/auth")
5
app.register_blueprint(blog_bp, url_prefix="/blog")
return app
And in app/auth/__init__.py :
# app/auth/__init__.py
from flask import Blueprint
auth_bp = Blueprint('auth', __name__, template_folder='auth_templates')
from app.auth import routes # import routes to register them with the blueprint
# app/auth/routes.py
from flask import render_template, redirect, url_for
from app.auth import auth_bp
from app.extensions import db, login_mgr, bcrypt
# (assume User model and LoginForm imported as well)
This is just illustrative – the key idea is that each blueprint’s code is self-contained and the app factory
ties them together. Blueprints make it “especially useful for large applications” by enabling modular design
15 .
Example Structure (REST API): If you are building a pure RESTful API (no server-side HTML), the concept is
similar minus the templates and static files. You might still use Blueprints for logical groupings of endpoints
(e.g. users , orders blueprints in an e-commerce API). Instead of Jinja2 templates, you might have
schema definitions (using something like Marshmallow or Pydantic for validating/serializing JSON). For
instance, one recommended API structure looks like:
project/
├── app/
│ ├── __init__.py # create_app, register API blueprints
│ ├── models/ # e.g. user.py, order.py (SQLAlchemy models)
│ ├── routes/ # e.g. user_routes.py, order_routes.py (Blueprints
define API endpoints)
│ ├── schemas/ # e.g. user_schema.py, order_schema.py (Marshmallow
schemas for serialization)
│ ├── services/ # e.g. user_service.py (business logic separate from
routes)
6
│ └── tests/ # test cases for API endpoints
├── config.py
├── requirements.txt
└── run.py
In this layout, each blueprint corresponds to a resource or feature (users, orders, etc.), but code is further
separated by responsibility: routes (for request handling), models (database layer), schemas (data
validation/serialization), and services (business logic) 16 17 . For example, you might have a
user_routes.py with a user_bp = Blueprint('user', __name__) that defines routes like GET /
users and POST /users , and those route functions call functions in user_service.py to interact
with User model or perform logic 18 19 . This layered approach keeps your route functions lean and
your business logic testable outside of the request context.
Pros: This blueprint-based structure is readable, maintainable, and scalable 20 . Each feature of the app is
a self-contained component, which makes it easier for multiple developers to work on different parts of the
project without stepping on each other’s toes. New features can be added by creating new blueprint
modules, a process one Reddit user described as “if you want a new function you just make a new folder
and place code in routes… and do it all over again” (i.e. it scales cleanly with project growth) 21 . New team
members can quickly find where things live (e.g. all user-related code is in the user blueprint) 22 5 . Code
reuse is improved too – for example, you could reuse the entire auth blueprint in another Flask app with
minimal changes.
Cons: There are very few downsides to this approach, but consider:
• Initial Complexity: Setting up an app factory and multiple files is more work upfront. For absolute
beginners or very small apps, it might feel like over-engineering. There’s a learning curve to
understanding application context, blueprint registration, etc.
• Overhead for Tiny Apps: For a one-off micro-service with just one or two routes, having a full
structure with blueprints might be unnecessary. In such cases, the simpler structure could suffice.
However, if there’s any chance the project will grow or be maintained long-term, the blueprint
structure pays off.
• Flexibility Means Variety: Flask doesn’t enforce a single pattern, so different projects may structure
things slightly differently (by feature vs by layer, different naming conventions, etc.). Ensure your
team or potential employer has a shared understanding of the structure. But fundamentally, as long
as it’s logical and consistent, that’s what matters – there is “no one correct answer” to structuring, just
common best practices 23 .
Within the blueprint approach, you might encounter two styles of organizing files:
7
code reuse, clear boundaries. Cons: You might have to look in multiple places to see all models or all
routes in the system, since they are spread by feature (though IDEs and search make this minor).
• Layer-based (Tier) Structure: Separate by role/responsibility. For example, put all models in a
models/ directory, all route definitions in a routes/ directory, all schema definitions in
schemas/ , etc., grouping by the type of component. Within each, you still likely separate by feature
(perhaps via naming or submodules). The example REST API structure above follows this style, where
user_routes.py , user_model.py , user_schema.py live in different folders 16 26 . Pros:
Easier to see at a glance all models or all API endpoints; developers specializing in one layer (e.g.
database) can navigate quickly. Cons: The code for one feature is not in one place – you have to
jump between folders (though naming can help link them, like user_* prefix). This can be slightly
less intuitive when adding a new feature (need to add files in multiple folders).
Both styles are used in practice, and you can even hybridize them (e.g. group most things by feature, but
still have a centralized models.py ). The key is to stay organized and consistent. Whichever you choose,
using Blueprints ensures you can register or modify each feature module cleanly.
• Separate Configurations for Environments: Don’t hard-code settings like secret keys or database
URLs in your code. Use a config file or classes for dev/test/prod, with actual sensitive values coming
from environment variables 10 . For instance, have a DevelopmentConfig vs
ProductionConfig with appropriate debug flags 27 . This shows you understand deployment
settings and security.
• Use Flask Extensions Wisely: Instead of writing everything from scratch, production apps use
extensions like Flask-SQLAlchemy for the ORM, Flask-Migrate for migrations, Flask-Login for
authentication, Flask-Bcrypt for hashing passwords, etc. In your structure, initializing these in a
central place (e.g. extensions.py ) and integrating them via blueprints (e.g. using
login_required from Flask-Login on certain routes) shows you can leverage the Flask ecosystem
28 . For example, the user authentication blueprint would use flask_login.LoginManager (set
up in app factory) to manage logins, and store hashed passwords using flask_bcrypt.Bcrypt .
• Testing and Quality: Include a tests/ directory with unit or integration tests for your critical
functionality. This not only prevents regressions but also is something many recruiters love to see (it
demonstrates diligence). A simple test of a route (using Flask’s test client) is often enough to convey
the idea 13 29 .
• Documentation & Readability: A clear README and in-code documentation (docstrings, comments)
along with the structured layout help others (or recruiters reviewing your code sample) navigate
your project. Using intuitive naming (e.g. auth/routes.py , user_service.py ) and following
conventions will reflect well.
• Deployment Setup: In production, Flask apps are typically run with a WSGI server (Gunicorn, uWSGI)
behind Nginx, etc., not the built-in dev server 30 . While this is more about deployment than
structure, having a wsgi.py or a clear entry point (like the run.py calling create_app() ) is
part of a professional setup. It might not impress a non-technical recruiter, but it shows engineering
foresight to those in the know.
8
• No “code smells”: A recruiter or senior engineer will appreciate not seeing common pitfalls like
massive functions, deeply nested logic in route handlers, or duplication across the code. By
structuring with blueprints and possibly a service layer, you demonstrate separation of concerns
(database logic in models, request logic in routes, etc.). This organization inherently encourages
cleaner, shorter functions which are easier to read and test.
If your goal is to impress recruiters and build a deployable app, the clear winner is the Blueprint-based
modular structure with an application factory. This pattern is widely used in industry and considered
best practice for non-trivial Flask applications 2 15 . It yields a codebase that is maintainable and scalable,
which are qualities any hiring team looks for. By contrast, a monolithic single-file app is only acceptable for
the tiniest projects; it would raise concerns if used for a larger project because it indicates limited
understanding of structuring larger applications.
In summary, organize your Flask projects thoughtfully. For a small one-off script you can be loose, but
for any app of medium complexity or higher, use a package structure with blueprints. This will make your
application “readable, maintainable, [and] scalable” 20 . It facilitates teamwork and future growth, and it
demonstrates to recruiters that you write production-quality code. Each project is unique, but if you follow
these patterns and adapt them to your needs, you’ll be on the right track to a professional Flask project that
stands out.
Sources:
• Miguel Grinberg, Flask Mega-Tutorial – “A Better Application Structure” (on using Blueprints and
factories) 2 4 31
• DigitalOcean Tutorial – Structuring a Large Flask Application (blueprint-based project layout and
rationale) 1 22
• Dev.to (G. Rajput) – Best Practices for Flask 2025 (example of REST API project structure with
blueprints) 32 33
• Joël S. N. – 10 Best Practices for Flask (emphasizing organized structure, environment config, and
blueprints) 6 15
9
1 5 7 9 22 How To Structure a Large Flask Application with Flask Blueprints and Flask-SQLAlchemy |
DigitalOcean
https://www.digitalocean.com/community/tutorials/how-to-structure-a-large-flask-application-with-flask-blueprints-and-flask-
sqlalchemy
10