Content Index
We are going to talk about route types or the ways we have to define our routes. For that, I am going to compare Flask, FastAPI, Django, and Laravel frameworks, which are the ones I actually work with. Basically, these are divided into two types.
- Flask and FastAPI: In the case of Flask, we have blueprints, which are decorators that we place directly on the endpoint. On the other hand, FastAPI —which was inspired by Flask for also being a microframework— takes this to the next level. It uses decorators to define its endpoints, but it also allows specifying whether methods are synchronous or asynchronous and allows including validations directly in the route definition. In this case, FastAPI is considered a more modern framework because it allows grouping parameters in a more structured way than Flask.
# routes/products.py
from flask import Blueprint
products_bp = Blueprint('products', __name__, url_prefix='/products')
@products_bp.route('/<int:product_id>', methods=['GET'])
def get_product(product_id):
return {"id": product_id, "name": "Laptop"}
# app.py (Registration)
from flask import Flask
from routes.products import products_bp
app = Flask(__name__)
app.register_blueprint(products_bp)
#--------------FASTAPI
# routers/products.py
from fastapi import APIRouter
router = APIRouter(prefix="/products", tags=["Products"])
@router.get("/{product_id}")
async def get_product(product_id: int):
return {"id": product_id, "name": "Laptop"}
# main.py (Registration)
from fastapi import FastAPI
from routers.products import router as products_router
app = FastAPI()
app.include_router(products_router)- Django and Laravel: Django separates the routes file from the views logic. The implementation is located in files named
urls.py, which work as modules within each application. Although the definition is similar to Flask and FastAPI (URI, method, and name), the disruptive thing here is that we handle a separate file so as not to "clutter" the endpoint. Laravel takes this to the next level with a much more structured organization, allowing chainings, prefixes, middlewares, controllers, and multiple grouping ways. # products/urls.py (Inside the 'products' app) from django.urls import path from .views import get_product urlpatterns = [ path('<int:product_id>/', get_product, name='product-detail'), ] # config/urls.py (Global routes file) from django.urls import path, include urlpatterns = [ path('products/', include('products.urls')), ]
In Laravel, although we can define more, we mainly use routes for the web, for the console, and for REST APIs. What I like about this scheme is that, when the application is medium or large, having all the routes in a single file makes reading difficult. In this model, we have a central place to organize them, unlike Flask or FastAPI, where to find a route you have to check where it was specifically defined. The small disadvantage is that, obviously, we end up with more files (for example, in Django you would have a routes file per module or application).
// routes/api.php
use App\Http\Controllers\ProductController;
use Illuminate\Support\Facades\Route;
// Option 1: Route group with prefix
Route::prefix('products')->group(function () {
Route::get('{id}', [ProductController::class, 'show']);
});
// Option 2: Controller Route Group (Even cleaner in Laravel 11/12)
Route::controller(ProductController::class)->prefix('products')->group(function () {
Route::get('{id}', 'show');
});Advantages and Disadvantages
The main problem we have with routes in frameworks like FastAPI or Flask is that readability becomes difficult if we want to quickly go to the definition of an endpoint. Since the structure is based on decorators, it is hard to remember where a specific route was defined without navigating through the entire module structure. In these cases, even in the official FastAPI extension, an additional tool is required to facilitate quick navigation, something that in Django or Laravel already comes "free" due to its architecture.
We can summarize the decorator-based approach (whether through Blueprints or a Routes-like object) with the following characteristics:
- Advantages: Code proximity. The function and the route live together, making it easier to understand the context of the endpoint. Additionally, it allows having typing and automatic documentation cleanly.
- Disadvantages/Risks: Disorder and coupling. By not having a rigid structure, it is easy to break conventions in long projects, such as parameter names or route structures, which hinders the onboarding of new developers.
On the other hand, the centralized and decoupled approach (like Laravel's) works as an additional layer of separation of concerns. Rules, logic, and structure are in separate files.
- Advantages: It is much easier to maintain. You can change a URL or quickly analyze what routes exist to avoid duplications. For large teams, having a rigid structure where everything is in a single place greatly facilitates the management of many routes.
- Disadvantages: Sometimes, file names in the folder structure can turn into a novel. For example, in an organized structure for Laravel, you could have files like
post.list,post.save, orpost.form. The problem with this approach is that, by using such generic names, the initial reading is clear because the pattern repeats, but it forces you to navigate between many files to find what you are looking for. If you want to locate the course listing, you have to look for thelist.blade.phpfile and navigate manually because there is no direct way to reach the component.
This model has its pros and cons: it is more logical and the files are easier to organize, but it requires writing more lines of code and creates navigation friction. In contrast, Laravel offers an additional advantage by allowing the management of multiple route files per module (web, API, console, etc.), allowing specific routes for each case.
// Contact
Route::livewire('contact', 'pages::dashboard.contact.list')->name('contact-list');
Route::livewire('contact-services', 'pages::dashboard.contact.service-list')->name('contact-service-list');Laravel uses a scheme on "steroids" that differentiates it from other frameworks like Django. By having more expressive methods, such as resource-type routes, they can be centralized and managed completely from a single place without having to define controllers in another layer if they are used directly from endpoints. The main disadvantage here is the strong dependency on the framework; if you decide to change technology, you will have to analyze and migrate this entire block.
Route::group(['prefix' => 'libros'], function () {
Route::get('', [PostController::class, 'books'])->name('web-book-list');
// Route::get('{post_url_clean}', [App\Http\Controllers\Courses\BookController::class, 'show'])->name('web-book-show');
Route::get('{post:url_clean}', [PostController::class, 'show_book'])->name('web-book-show');
});On the other hand, Laravel routes can be a mess if not well organized, as they allow multiple definitions and structures like redirects or callbacks. If not managed correctly, routes become kilometers long and hard to read, losing the advantages of the original scheme. If you want to dive deeper into these technologies, you can visit my academy platform to see step-by-step organized tutorials and access the source code.