APIRouter vs Flask Blueprint vs Django URLconf As your Python web application grows, keeping all routes in a single file becomes unmanageable. Each major Python web framework provides a solution for modular route organization:
FastAPI : APIRouter
Flask : Blueprint
Django : URL includes and apps
Let’s explore each approach and see how they compare.
The Problem: Monolithic Routes Imagine having 50+ endpoints in a single file:
@app.get("/users/" ) @app.get("/users/{id}" ) @app.post("/users/" ) @app.get("/products/" ) @app.get("/products/{id}" ) @app.post("/orders/" )
All three frameworks solve this with modular route organization.
FastAPI: APIRouter FastAPI’s APIRouter works exactly like the main FastAPI app but allows you to define routes in separate modules.
Basic Structure app/ ├── main.py ├── routers/ │ ├── __init__.py │ ├── users.py │ └── products.py
Creating a Router from fastapi import APIRouter, HTTPExceptionrouter = APIRouter( prefix="/users" , tags=["users" ], responses={404 : {"description" : "Not found" }}, ) @router.get("/" ) async def list_users (): return [{"id" : 1 , "name" : "Alice" }, {"id" : 2 , "name" : "Bob" }] @router.get("/{user_id}" ) async def get_user (user_id: int ): return {"id" : user_id, "name" : "Alice" } @router.post("/" ) async def create_user (name: str ): return {"id" : 3 , "name" : name}
Including Routers from fastapi import FastAPIfrom routers import users, productsapp = FastAPI(title="My API" ) app.include_router(users.router) app.include_router(products.router) app.include_router( admin.router, prefix="/admin" , tags=["admin" ], )
Key Features
Prefix : All routes get a common URL prefix
Tags : Groups endpoints in OpenAPI docs
Dependencies : Apply dependencies to all routes in router
Responses : Define common responses
from fastapi import Dependsrouter = APIRouter( prefix="/items" , tags=["items" ], dependencies=[Depends(verify_token)], )
Flask: Blueprint Flask’s Blueprint predates APIRouter and served as its inspiration. It provides similar functionality for organizing routes.
Basic Structure app/ ├── __init__.py ├── users/ │ ├── __init__.py │ └── routes.py └── products/ ├── __init__.py └── routes.py
Creating a Blueprint from flask import Blueprint, jsonifyusers_bp = Blueprint('users' , __name__, url_prefix='/users' ) @users_bp.route('/' ) def list_users (): return jsonify([{"id" : 1 , "name" : "Alice" }]) @users_bp.route('/<int:user_id>' ) def get_user (user_id ): return jsonify({"id" : user_id, "name" : "Alice" }) @users_bp.route('/' , methods=['POST' ] ) def create_user (): return jsonify({"id" : 3 , "name" : "New User" }), 201
Registering Blueprints from flask import Flaskdef create_app (): app = Flask(__name__) from .users.routes import users_bp from .products.routes import products_bp app.register_blueprint(users_bp) app.register_blueprint(products_bp) app.register_blueprint(admin_bp, url_prefix='/admin' ) return app
Blueprint Features
URL Prefix : Common prefix for all routes
Static Files : Blueprint-specific static folder
Templates : Blueprint-specific template folder
Error Handlers : Blueprint-scoped error handling
users_bp = Blueprint( 'users' , __name__, url_prefix='/users' , template_folder='templates' , static_folder='static' , ) @users_bp.errorhandler(404 ) def not_found (error ): return jsonify({"error" : "User not found" }), 404
Django: URLconf and Apps Django takes a different approach with its app-based architecture. Each app contains its own urls.py which is included in the main URLconf.
Basic Structure project/ ├── project/ │ ├── __init__.py │ ├── settings.py │ └── urls.py # Main URLconf ├── users/ │ ├── __init__.py │ ├── urls.py # App URLs │ └── views.py └── products/ ├── __init__.py ├── urls.py └── views.py
Creating App URLs from django.http import JsonResponsefrom django.views import Viewdef list_users (request ): return JsonResponse([{"id" : 1 , "name" : "Alice" }], safe=False ) def get_user (request, user_id ): return JsonResponse({"id" : user_id, "name" : "Alice" }) class UserCreateView (View ): def post (self, request ): return JsonResponse({"id" : 3 , "name" : "New User" }, status=201 )
from django.urls import pathfrom . import viewsapp_name = 'users' urlpatterns = [ path('' , views.list_users, name='list' ), path('<int:user_id>/' , views.get_user, name='detail' ), path('create/' , views.UserCreateView.as_view(), name='create' ), ]
Including App URLs from django.urls import path, includeurlpatterns = [ path('users/' , include('users.urls' )), path('products/' , include('products.urls' )), path('api/v1/' , include([ path('users/' , include('users.urls' )), path('products/' , include('products.urls' )), ])), ]
Django REST Framework Routers For REST APIs, Django REST Framework provides automatic URL routing:
from rest_framework import viewsetsfrom .models import Userfrom .serializers import UserSerializerclass UserViewSet (viewsets.ModelViewSet): queryset = User.objects.all () serializer_class = UserSerializer
from rest_framework.routers import DefaultRouterfrom .views import UserViewSetrouter = DefaultRouter() router.register(r'users' , UserViewSet) urlpatterns = router.urls
This automatically creates:
GET /users/ - List
POST /users/ - Create
GET /users/{id}/ - Retrieve
PUT /users/{id}/ - Update
DELETE /users/{id}/ - Delete
Side-by-Side Comparison
Feature
FastAPI APIRouter
Flask Blueprint
Django URLconf
URL Prefix
prefix="/users"
url_prefix='/users'
path('users/', include(...))
Grouping
tags=["users"]
Blueprint name
app_name namespace
Dependencies
dependencies=[...]
@bp.before_request
Middleware/decorators
Auto Documentation
Yes (OpenAPI)
No (needs extensions)
DRF has browsable API
Nested Routers
Via include
Via nested blueprints
Via nested includes
Static Files
No
Yes
Via app static folders
Templates
No
Yes
Via app template folders
Practical Example: User Management Module FastAPI from fastapi import APIRouter, Depends, HTTPExceptionfrom sqlalchemy.orm import Sessionfrom ..database import get_dbfrom .. import schemas, crudrouter = APIRouter(prefix="/users" , tags=["users" ]) @router.get("/" , response_model=list [schemas.User] ) def list_users (skip: int = 0 , limit: int = 100 , db: Session = Depends(get_db ) ): return crud.get_users(db, skip=skip, limit=limit) @router.post("/" , response_model=schemas.User, status_code=201 ) def create_user (user: schemas.UserCreate, db: Session = Depends(get_db ) ): db_user = crud.get_user_by_email(db, email=user.email) if db_user: raise HTTPException(status_code=400 , detail="Email already registered" ) return crud.create_user(db=db, user=user)
Flask from flask import Blueprint, request, jsonifyfrom ..models import User, dbusers_bp = Blueprint('users' , __name__, url_prefix='/users' ) @users_bp.route('/' ) def list_users (): skip = request.args.get('skip' , 0 , type =int ) limit = request.args.get('limit' , 100 , type =int ) users = User.query.offset(skip).limit(limit).all () return jsonify([u.to_dict() for u in users]) @users_bp.route('/' , methods=['POST' ] ) def create_user (): data = request.get_json() if User.query.filter_by(email=data['email' ]).first(): return jsonify({"error" : "Email already registered" }), 400 user = User(**data) db.session.add(user) db.session.commit() return jsonify(user.to_dict()), 201
Django REST Framework from rest_framework import viewsets, statusfrom rest_framework.response import Responsefrom .models import Userfrom .serializers import UserSerializerclass UserViewSet (viewsets.ModelViewSet): queryset = User.objects.all () serializer_class = UserSerializer def create (self, request ): if User.objects.filter (email=request.data.get('email' )).exists(): return Response( {"error" : "Email already registered" }, status=status.HTTP_400_BAD_REQUEST ) return super ().create(request)
When to Use Each Choose FastAPI APIRouter when:
Building modern async APIs
Need automatic OpenAPI documentation
Want type hints and validation
Performance is critical
Choose Flask Blueprint when:
Building traditional web apps with templates
Need blueprint-specific static files
Prefer simplicity and flexibility
Have existing Flask ecosystem
Choose Django URLconf when:
Building full-featured web applications
Need Django’s ORM and admin
Want DRF’s powerful features
Enterprise/large team projects
Conclusion All three approaches solve the same problem: organizing routes in a modular, maintainable way. The choice depends on your framework preference and project requirements:
FastAPI’s APIRouter is the most modern, with excellent async support and auto-documentation
Flask’s Blueprint is battle-tested and flexible, great for traditional web apps
Django’s URLconf integrates with Django’s app architecture, ideal for full-stack applications
Whichever you choose, the key principle remains: keep related routes together and separate concerns into logical modules .
Further Reading