🛤️ Routes

Routes are the access points to the application. In Lila, routes are defined in the routes directory (by default, but you can place them wherever you want) and imported into main.py for use. Routes can be configured to handle HTTP requests, API methods, and more.

In addition to JSONResponse you can use HTMLResponse, RedirectResponse and PlainTextResponse, or StreamingResponse, to transmit data in real-time (useful for streaming video/audio or large responses).

routes/api.py

# Import from lila.core JSONResponse.
from lila.core.responses import JSONResponse 

# Manages routes for API endpoints.
from lila.core.routing import Router  
# Initializes the router instance to handle API app.routes.
router = Router()

# Defines a simple API route that supports the GET method.
@router.route(path='/api', methods=['GET'])
async def api(request: Request):
    """Api function"""
    # English: Returns a simple JSON response for API verification.
    return JSONResponse({'api': True})   
            

🔗 Get URL Parameters

In this function, we receive a parameter via the URL using {param}. If the parameter is not provided, it defaults to 'default'. The response is a JSON containing the received value.

Receiving GET Parameters

@router.route(path='/url_with_param/{param}', methods=['GET']) 
async def param(request: Request):  
    param = request.path_params.get('param', 'default') 
    return JSONResponse({"received_param": param})
            

Or you can also do it like this:

Receiving GET Query Parameters
 
@router.route(path='/url_with_query', methods=['GET','POST']) 
async def query_param(request: Request):  
    query_param = request.query_params.get('query_param', 'default_value')
    return JSONResponse({"received_param": query_param})
            
Automatic Sanitization
 
# All POST/PUT/PATCH routes automatically sanitize the body
# Query parameters are also checked for XSS patterns
@router.post('/secure-route', model=MyModel)
async def secure_handler(request: Request):
    # request.state.data is already sanitized
    data = request.state.data
    return JSONResponse({"status": "safe"})
            
Basic Imports

from lila.core.responses import JSONResponse  # Simplifies sending JSON responses.
from lila.core.routing import Router  # Manages API app.routes.
from lila.core.request import Request  # Handles HTTP requests in the application.
from pydantic import EmailStr, BaseModel  # Validates and parses data models for input validation.
from lila.core.middleware import validate_token

router = Router()
            

🛡️ Middleware and Decorators Usage

Middlewares allow intercepting requests before they reach the main logic of the API. In this example, we use @validate_token to validate a JWT token in the request header.

Middleware Validation

@router.route(path='/api/token', methods=['GET','POST'])
@validate_token  # Middleware to validate JWT token.
async def api_token(request: Request):
    return JSONResponse({'api': True})
            

✅ Data Validation with Pydantic

Pydantic allows defining data models that automatically validate user input. Additionally, specifying a model in the route generates automatic documentation in /docs.

Validation with Pydantic

from pydantic import EmailStr, BaseModel
from lila.core.responses import JSONResponse

class ExampleModel(BaseModel):
    email: EmailStr  # Ensures a valid email.
    password: str  # String for password.

@router.route(path='/api/example', methods=['POST'], model=ExampleModel)
async def login(request: Request):
    # Validation is automatic! 
    # Validated data is injected into request.state.data
    user_data = request.state.data
    return JSONResponse({"email": user_data.email, "status": "validated"})
            

📚 Automatic Documentation Generation

Thanks to the integration with Pydantic, the API documentation is generated automatically and is accessible from /docs. You can also generate an OpenAPI JSON file for external tools.

Documentation Generation

router.swagger_ui()  # Enables Swagger UI for API documentation.
router.openapi_json()  # Generates OpenAPI JSON for external tools.
            

📦 Importing Routes in main.py

To use the routes defined in the router, you need to obtain them with router.get_routes() and import them into main.py.

Route Import

routes = router.get_routes()  # Retrieves all defined app.routes.
            

Route Prefixes and HTTP Method Shortcuts

Routes can now have a prefix, which allows grouping multiple endpoints under a common path. Additionally, you can define routes directly with HTTP methods as functions: get, post, put, delete.

routes/api_v2.py

# English: Initialize the router instance for managing API routes.
router = Router(prefix="v1/api")

# English: Define a simple API route that supports GET method.
@router.get("/")
async def api(request: Request):
    """Api function"""  # use doc for description http://127.0.0.1:8000/openapi.json and http://127.0.0.1:8000/docs
    # English: Returns a simple JSON response for API verification.
    return JSONResponse({"api": True})

# Español: Define una ruta de API que soporta los métodos GET y POST.
@router.route(path="/token", methods=["GET", "POST"])
# English: Middleware to validate the JWT Token.
@validate_token
async def api_token(request: Request):
    """Api Token function"""  # use doc for description http://127.0.0.1:8000/openapi.json and http://127.0.0.1:8000/docs
    print(get_user_by_token(request=request))
    return JSONResponse({"api": True})
        

Using prefixes helps organize your API under a common version path (e.g., v1/api), while method-specific decorators (@router.get, @router.post, etc.) make route definitions cleaner and more intuitive.

HTTP Method Examples with Prefix and Pydantic Models

Routes now support the HTTP methods get, post, put, and delete directly. You can also attach Pydantic models for automatic input validation and documentation generation.

routes/v1_api_example.py

from lila.core.responses import JSONResponse
from lila.core.routing import Router
from lila.core.request import Request
from pydantic import BaseModel

router = Router(prefix="v1/api")

# ---------------- GET Example ----------------
@router.get("/items/{item_id}")
async def get_item(request: Request):
    """Fetch an item by ID."""
    item_id = request.path_params.get("item_id")
    return JSONResponse({"item_id": item_id, "status": "fetched"})

# ---------------- POST Example ----------------
class CreateItemModel(BaseModel):
    name: str
    description: str

@router.post("/items", model=CreateItemModel)
async def create_item(request: Request):
    """Create a new item."""
    # Data is already validated and available in request.state.data
    input_data = request.state.data
    return JSONResponse({"name": input_data.name, "description": input_data.description})

# ---------------- PUT Example ----------------
class UpdateItemModel(BaseModel):
    name: str
    description: str

@router.put("/items/{item_id}", model=UpdateItemModel)
async def update_item(request: Request):
    """Update an existing item by ID."""
    item_id = request.path_params.get("item_id")
    # Data is already validated and available in request.state.data
    input_data = request.state.data
    return JSONResponse({"item_id": item_id, "updated_name": input_data.name})

# ---------------- DELETE Example ----------------
@router.delete("/items/{item_id}")
async def delete_item(request: Request):
    """Delete an item by ID."""
    item_id = request.path_params.get("item_id")
    return JSONResponse({"item_id": item_id, "status": "deleted"})
        

These examples demonstrate how to:

Documentation is automatically generated at /docs and /openapi.json.