Advanced Routing, Handlers, Controllers, and Include in Esmerald¶
Esmerald offers a highly flexible routing system, allowing you to structure your application in a modular way. This guide covers essential concepts such as routers, routes, handlers, API views, and especially Include, which is one of the most powerful features in Esmerald for modular routing and reusability.
By combining these components, you can create scalable, organized applications that are easy to maintain.
🚪 Routers in Esmerald¶
A router is a mechanism for grouping routes that share common functionality, such as applying middleware or settings across related routes. Routers help modularize your application by encapsulating related routes into distinct units.
Example: Defining and Using Routers¶
from esmerald import Esmerald, Router, Gateway, JSONResponse, get
# Create a router for user-related endpoints
user_router = Router()
@user_router.get("/profile/{user_id}")
async def get_user_profile(user_id: int) -> JSONResponse:
return JSONResponse({"user_id": user_id, "message": "User profile fetched"})
# Create an application with the router
app = Esmerald()
app.add_router(user_router)
Explanation:¶
- The
user_router
groups routes related to user profiles, making it easy to manage. - The
@user_router.get("/profile/{user_id}")
decorator maps the/profile/{user_id}
path to theget_user_profile
function. - The
app
includes theuser_router
and connects the handler viaGateway
.
🛤️ Routes in Esmerald¶
Routes define the actual HTTP paths in your application. They are the endpoints that users or clients interact with. In Esmerald, routes are associated with handler functions that process requests and return responses.
Example: Defining Routes¶
from esmerald import Esmerald, Gateway, JSONResponse, post
@post("/create")
async def create_user() -> JSONResponse:
return JSONResponse({"message": "User created successfully"})
app = Esmerald(routes=[Gateway(handler=create_user)])
Explanation:¶
- The
@post("/create")
decorator maps the/create
path to thecreate_user
handler. - The
Gateway(handler=create_user)
connects the route to the handler, ensuring that requests to/create
are processed by thecreate_user
function.
Use Cases:¶
- Handling different HTTP methods (GET, POST, PUT, DELETE).
- Creating RESTful APIs for CRUD operations.
- Mapping dynamic routes with path parameters.
🧰 Handlers in Esmerald¶
Handlers are functions or classes that process incoming requests. They take in request data (like path parameters, body data, cookies, or headers) and return an appropriate response.
Example: Basic Handler Function¶
from esmerald import Esmerald, Gateway, JSONResponse, get
@get("/hello")
async def hello_world() -> JSONResponse:
return JSONResponse({"message": "Hello, world!"})
app = Esmerald(routes=[Gateway(handler=hello_world)])
Explanation:¶
- The
hello_world
function handles the/hello
route and returns aJSONResponse
containing a greeting message. - The
@get("/hello")
decorator registers the route, while theGateway(handler=hello_world)
connects the route to the handler.
📦 Controllers in Esmerald¶
Controllers or Controllers are class-based views that allow you to group multiple routes under one class. This makes it easier to share logic and group related routes, improving the maintainability of your application.
Example: API View with Multiple Routes¶
from esmerald import Esmerald, Gateway, JSONResponse, post, get, Controller
class UserController(Controller):
@get("/user/{user_id}")
async def get_user(self, user_id: int) -> JSONResponse:
return JSONResponse({"user_id": user_id, "message": "User details fetched"})
@post("/create")
async def create_user(self) -> JSONResponse:
return JSONResponse({"message": "User created successfully"})
app = Esmerald(routes=[Gateway(handler=UserController)])
Explanation:¶
- The
UserController
class groups related user routes under one class. - Each method in the
UserController
class is mapped to a route, either using@get
or@post
.
🔄 Include in Esmerald¶
One of the most powerful features of Esmerald is Include. This feature allows you to include other routes, routers, API views within your application, include external apps (Django, Flask, FastAPI), effectively allowing for modular routing. It helps in breaking down your application into smaller, manageable pieces.
Example: Using Include
to Modularize Routes¶
from esmerald import Esmerald, Router, Gateway, Include, JSONResponse, get
@get("/profile/{user_id}")
async def get_user_profile(user_id: int) -> JSONResponse:
return JSONResponse({"user_id": user_id, "message": "User profile fetched"})
# Include the user
app = Esmerald(routes=[Include(routes=[Gateway(handler=get_user_profile)])])
Explanation:¶
- The
user_router
defines a group of routes for user-related endpoints. - The
Include(routes=[Gateway(handler=get_user_profile)])
includes the@get
into the main application, making its routes available.
Use Cases for Include
:¶
- Modularizing large applications by breaking them down into smaller routers.
- Reusing common routes across different parts of the application or in multiple projects.
- Improving maintainability by isolating concerns (e.g., user management, authentication).
Or if you want to include an external app, for example in Flask:
from flask import Flask, escape, request
from esmerald import Esmerald, Gateway, Include, Request, get
from esmerald.middleware.wsgi import WSGIMiddleware
flask_app = Flask(__name__)
@flask_app.route("/")
def flask_main():
name = request.args.get("name", "Esmerald")
return f"Hello, {escape(name)} from your Flask integrated!"
@get("/home/{name:str}")
async def home(request: Request) -> dict:
name = request.path_params["name"]
return {"name": escape(name)}
app = Esmerald(
routes=[
Gateway(handler=home),
Include("/flask", WSGIMiddleware(flask_app)),
]
)
📂 Combining Routers, Routes, Handlers, Controllers, and Include¶
In Esmerald, you can combine routers, routes, handlers and Controllers to create flexible and maintainable applications. You can use Include to modularize your application into different components, allowing for better code organization.
Example: Complex Application Using All Features¶
from esmerald import Esmerald, Gateway, JSONResponse, Router, APIView, post, get, Controller
from esmerald.datastructures import Webhook
@user_router.get("/status")
async def status() -> JSONResponse:
return JSONResponse({"status": "OK"})
@get("/include-status")
async def include_status() -> JSONResponse:
return JSONResponse({"status": "OK"})
# Define an API view for users
class UserController(APIView):
@get("/user/{user_id}")
async def get_user(self, user_id: int) -> JSONResponse:
return JSONResponse({"user_id": user_id, "message": "User found"})
@post("/create")
async def create_user(self) -> JSONResponse:
return JSONResponse({"message": "User created successfully"})
# Create app with all components
app = Esmerald(
routes=[
Include('/internal', routes=[Gateway(handler=include_status)]), # Include include_status
Gateway(handler=UserController), # Include API View
]
)
app.add_router(user_router)
Explanation:¶
- The
user_router
groups related user routes (like/status
). - The
UserController
class-based view handles user-related routes. - The
Include('/internal', routes=[Gateway(handler=include_status)])
modularizes other routes.
📑 Summary¶
- Routers: Group related routes and apply common middleware or settings.
- Routes: Define URL paths and map them to handler functions.
- Handlers: Functions that process incoming requests and return responses.
- Controllers: Class-based handlers for organizing and sharing logic across related routes.
- Include: Modularize routes, routers, or API views and include them in your main app for better organization and reusability.
Esmerald’s routing system allows for highly flexible and modular web application design. You can group related routes, use class-based views for shared logic, and handle external webhooks with ease.
The Include
feature is particularly useful for organizing your code and reusing common
routes across multiple applications.
What's Next?¶
Now that you're comfortable structuring your app with routes and includes, it's time to learn about middlewares.
👉 Continue to middlewares to handle things like logging, CORS, sessions, and more.