Router¶
The Router is the main object that links the whole Esmerald to the Gateway, WebSocketGateway and handlers.
Router class¶
The router class is composed by many attributes that are, by default, populated within the application. However, Esmerald also allows to add extra custom routers, or alternatively, you can add a ChildEsmerald app.
from pydantic import BaseModel
from esmerald import Esmerald, Gateway, post
class User(BaseModel):
name: str
email: str
@post("/create")
def create(data: User) -> User: ...
app = Esmerald(routes=[Gateway(handler=create)])
The main Router
class is instantiated within the Esmerald
application with the given routes and the application
starts.
Info
When adding another router to the application, custom routers and
ChildEsmerald are available as a way of doing it. One is more limited than
the other, in this case, custom routers are more limited than ChildEsmerald
.
Parameters¶
All the parameters and defaults are available in the Router Reference.
Warning
The response_class
, response_cookies
, response_headers
, tags
and include_in_schema
are not used
when add_route is used, only when using ChildEsmerald app.
Custom Router¶
Let's assume there are specific customer submodules inside a customers
dedicated file.
There are three ways of separating the routes within the application, using Include,
a ChildEsmerald or by creating another router. Let's focus on the latter.
from pydantic import BaseModel
from esmerald import Gateway, JSONResponse, Router, get, post
class Address(BaseModel):
address_line: str
street: str
post_code: str
class Customer(BaseModel):
name: str
email: str
address: Address
@post("/")
def create(data: Customer) -> JSONResponse:
return JSONResponse({"created": True})
@get("/{customer_id:int}")
async def get_customer(customer_id: int) -> JSONResponse:
return JSONResponse({"created": True})
router = Router(
path="/customers",
routes=[
Gateway("/", handler=get_customer),
Gateway("/create", handler=create),
],
)
Above, you created the /application/apps/routers/customers.py
with all the information you need. It does not need
to be in one file, you can have a entirely separate package just to manage the customer, it is up to you.
Now you need to add the new custom router into the main application.
from apps.routers.customers import router as customers_router
from esmerald import Esmerald
app = Esmerald()
app.add_router(customers_router)
This simple and your router is added to the main Esmerald application.
Child Esmerald Application¶
What is this? We call it ChildEsmerald
but in fact is simply Esmerald but under a different name mostly for
visualisation purposes and for the sake of organisation.
Check
Using ChildEsmerald
or Esmerald
is exactly the same thing, it is only if you prefer to create a
sub application
and you prefer to use a different class instead of Esmerald
to make it more organised.
When organising routes, using the Router
class itself can be a bit limiting because there are certain attributes
that when used within an instance or a Router
to be passed to add_route they will not be picked up.
Example:
response_class
response_cookies
response_headers
tags
include_in_schema
This is not a limitation or a bug, in fact it is intentional as we want to preserve the integrity of the application.
How does it work¶
Let's use the same example used in the custom routers with the customers specific routes and rules.
from pydantic import BaseModel
from esmerald import ChildEsmerald, Gateway, JSONResponse, get, post
class Address(BaseModel):
address_line: str
street: str
post_code: str
class Customer(BaseModel):
name: str
email: str
address: Address
@post("/")
def create(data: Customer) -> JSONResponse:
return JSONResponse({"created": True})
@get("/{customer_id:int}")
async def get_customer(customer_id: int) -> JSONResponse:
return JSONResponse({"created": True})
router = ChildEsmerald(
routes=[
Gateway("/", handler=get_customer),
Gateway("/create", handler=create),
],
include_in_schema=...,
response_class=...,
response_headers=...,
response_cookies=...,
)
Since the ChildEsmerald
is a representation of an Esmerald class, we can pass
the otherwise limited parameters in the custom router and all the parameters available to
Esmerald.
You can add as many ChildEsmerald
as you desire, there are no limits.
Now in the main application:
from apps.routers.customers import router as customers_router
from esmerald import Esmerald, Include
app = Esmerald(routes=[Include("/customers", app=customers_router)])
- Adding nested applications
from apps.routers.clients import router as clients_router
from apps.routers.customers import router as customers_router
from apps.routers.restrict import router as restrict_router
from esmerald import Esmerald, Include
app = Esmerald(
routes=[
Include("/customers", app=customers_router),
Include(
"/api/v1",
routes=[
Include("/clients", clients_router),
Include("/restrict", routes=[Include("/access", restrict_router)]),
],
),
]
)
The example above shows that you could even add the same application within nested includes, and for each
include, you can add specific unique permissions, middlewares,
exception handlers and dependencies, which are available on each
instance of the Include
. The options are endless.
Note
In terms of organisation, ChildEsmerald
has a clean approach to the isolation of responsabilities and allows
treating every individual module separately and simply adding it in to the main application
in the form of Include.
Tip
Treat the ChildEsmerald
as an independent Esmerald
instance.
Check
When adding a ChildEsmerald
or Esmerald
application, don't forget to add the unique path to the base
Include
, this way you can assure the routes are found properly.
Utils¶
The Router
object has some available functionalities that can be useful.
add_route()¶
from esmerald import Esmerald
app = Esmerald()
app.add_route(
handler=...,
dependencies=...,
exception_handlers=...,
permissions=...,
middleware=...,
name=...,
interceptors=...,
include_in_schema=...,
)
Parameters¶
- name - Name of the route.
- include_in_schema - If route should be added to the OpenAPI Schema
- handler - A HTTP handler.
- permissions - A list of permissions to serve the application incoming requests (HTTP and Websockets).
- middleware - A list of middleware to run for every request. The middlewares of a Include will be checked from top-down.
- interceptors - A list of interceptors or Lilya Middleware, as they are both converted internally. Read more about Python Protocols.
- dependencies - A dictionary of string and Inject instances enable application level dependency injection.
- exception_handlers - A dictionary of exception types (or custom exceptions) and the handler
functions on an application top level. Exception handler callables should be of the form of
handler(request, exc) -> response
and may be be either standard functions, or async functions.
add_websocket_route()¶
from esmerald import Esmerald
app = Esmerald()
app.add_websocket_route(
handler=...,
dependencies=...,
exception_handlers=...,
permissions=...,
middleware=...,
interceptors=...,
name=...,
)
Parameters¶
- name - Name of the route.
- Websocket handler - A websocket handler.
- permissions - A list of permissions to serve the application incoming requests (HTTP and Websockets).
- interceptors - A list of interceptors.
- middleware - A list of middleware to run for every request. The middlewares of a Include will be checked from top-down. or Lilya Middleware as they are both converted internally. Read more about Python Protocols.
- dependencies - A dictionary of string and Inject instances enable application level dependency injection.
- exception_handlers - A dictionary of exception types (or custom exceptions) and the handler
functions on an application top level. Exception handler callables should be of the form of
handler(request, exc) -> response
and may be be either standard functions, or async functions.
add_child_esmerald()¶
from esmerald import ChildEsmerald, Esmerald, Gateway, get
@get()
async def home() -> str:
return "home"
child = ChildEsmerald(routes=[Gateway(handler=home, name="my-apiview")])
app = Esmerald()
app.add_child_esmerald(
path="/child",
child=child,
name=...,
middleware=...,
dependencies=...,
exception_handlers=...,
interceptors=...,
permissions=...,
include_in_schema=...,
deprecated=...,
security=...,
)
Parameters¶
- path - The path for the child esmerald.
- child - The ChildEsmerald instance.
- name - Name of the route.
- Websocket handler - A websocket handler.
- permissions - A list of permissions to serve the application incoming requests (HTTP and Websockets).
- interceptors - A list of interceptors.
- middleware - A list of middleware to run for every request. The middlewares of a Include will be checked from top-down. or Lilya Middleware as they are both converted internally. Read more about Python Protocols.
- dependencies - A dictionary of string and Inject instances enable application level dependency injection.
- exception_handlers - A dictionary of exception types (or custom exceptions) and the handler
functions on an application top level. Exception handler callables should be of the form of
handler(request, exc) -> response
and may be be either standard functions, or async functions. - include_in_schema - Boolean if this ChildEsmerald should be included in the schema.
- deprecated - Boolean if this ChildEsmerald should be marked as deprecated.