Module aws_lambda_powertools.event_handler.middlewares.base

Expand source code
from abc import ABC, abstractmethod
from typing import Generic

from aws_lambda_powertools.event_handler.api_gateway import Response
from aws_lambda_powertools.event_handler.types import EventHandlerInstance
from aws_lambda_powertools.shared.types import Protocol


class NextMiddleware(Protocol):
    def __call__(self, app: EventHandlerInstance) -> Response:
        """Protocol for callback regardless of next_middleware(app), get_response(app) etc"""
        ...

    def __name__(self) -> str:  # noqa A003
        """Protocol for name of the Middleware"""
        ...


class BaseMiddlewareHandler(Generic[EventHandlerInstance], ABC):
    """Base implementation for Middlewares to run code before and after in a chain.


    This is the middleware handler function where middleware logic is implemented.
    The next middleware handler is represented by `next_middleware`, returning a Response object.

    Examples
    --------

    **Correlation ID Middleware**

    ```python
    import requests

    from aws_lambda_powertools import Logger
    from aws_lambda_powertools.event_handler import APIGatewayRestResolver, Response
    from aws_lambda_powertools.event_handler.middlewares import BaseMiddlewareHandler, NextMiddleware

    app = APIGatewayRestResolver()
    logger = Logger()


    class CorrelationIdMiddleware(BaseMiddlewareHandler):
        def __init__(self, header: str):
            super().__init__()
            self.header = header

        def handler(self, app: APIGatewayRestResolver, next_middleware: NextMiddleware) -> Response:
            # BEFORE logic
            request_id = app.current_event.request_context.request_id
            correlation_id = app.current_event.get_header_value(
                name=self.header,
                default_value=request_id,
            )

            # Call next middleware or route handler ('/todos')
            response = next_middleware(app)

            # AFTER logic
            response.headers[self.header] = correlation_id

            return response


    @app.get("/todos", middlewares=[CorrelationIdMiddleware(header="x-correlation-id")])
    def get_todos():
        todos: requests.Response = requests.get("https://jsonplaceholder.typicode.com/todos")
        todos.raise_for_status()

        # for brevity, we'll limit to the first 10 only
        return {"todos": todos.json()[:10]}


    @logger.inject_lambda_context
    def lambda_handler(event, context):
        return app.resolve(event, context)

    ```

    """

    @abstractmethod
    def handler(self, app: EventHandlerInstance, next_middleware: NextMiddleware) -> Response:
        """
        The Middleware Handler

        Parameters
        ----------
        app: EventHandlerInstance
            An instance of an Event Handler that implements ApiGatewayResolver
        next_middleware: NextMiddleware
            The next middleware handler in the chain

        Returns
        -------
        Response
            The response from the next middleware handler in the chain

        """
        raise NotImplementedError()

    @property
    def __name__(self) -> str:  # noqa A003
        return str(self.__class__.__name__)

    def __call__(self, app: EventHandlerInstance, next_middleware: NextMiddleware) -> Response:
        """
        The Middleware handler function.

        Parameters
        ----------
        app: ApiGatewayResolver
            An instance of an Event Handler that implements ApiGatewayResolver
        next_middleware: NextMiddleware
            The next middleware handler in the chain

        Returns
        -------
        Response
            The response from the next middleware handler in the chain
        """
        return self.handler(app, next_middleware)

Classes

class BaseMiddlewareHandler

Base implementation for Middlewares to run code before and after in a chain.

This is the middleware handler function where middleware logic is implemented. The next middleware handler is represented by next_middleware, returning a Response object.

Examples

Correlation ID Middleware

import requests

from aws_lambda_powertools import Logger
from aws_lambda_powertools.event_handler import APIGatewayRestResolver, Response
from aws_lambda_powertools.event_handler.middlewares import BaseMiddlewareHandler, NextMiddleware

app = APIGatewayRestResolver()
logger = Logger()


class CorrelationIdMiddleware(BaseMiddlewareHandler):
    def __init__(self, header: str):
        super().__init__()
        self.header = header

    def handler(self, app: APIGatewayRestResolver, next_middleware: NextMiddleware) -> Response:
        # BEFORE logic
        request_id = app.current_event.request_context.request_id
        correlation_id = app.current_event.get_header_value(
            name=self.header,
            default_value=request_id,
        )

        # Call next middleware or route handler ('/todos')
        response = next_middleware(app)

        # AFTER logic
        response.headers[self.header] = correlation_id

        return response


@app.get("/todos", middlewares=[CorrelationIdMiddleware(header="x-correlation-id")])
def get_todos():
    todos: requests.Response = requests.get("https://jsonplaceholder.typicode.com/todos")
    todos.raise_for_status()

    # for brevity, we'll limit to the first 10 only
    return {"todos": todos.json()[:10]}


@logger.inject_lambda_context
def lambda_handler(event, context):
    return app.resolve(event, context)

Expand source code
class BaseMiddlewareHandler(Generic[EventHandlerInstance], ABC):
    """Base implementation for Middlewares to run code before and after in a chain.


    This is the middleware handler function where middleware logic is implemented.
    The next middleware handler is represented by `next_middleware`, returning a Response object.

    Examples
    --------

    **Correlation ID Middleware**

    ```python
    import requests

    from aws_lambda_powertools import Logger
    from aws_lambda_powertools.event_handler import APIGatewayRestResolver, Response
    from aws_lambda_powertools.event_handler.middlewares import BaseMiddlewareHandler, NextMiddleware

    app = APIGatewayRestResolver()
    logger = Logger()


    class CorrelationIdMiddleware(BaseMiddlewareHandler):
        def __init__(self, header: str):
            super().__init__()
            self.header = header

        def handler(self, app: APIGatewayRestResolver, next_middleware: NextMiddleware) -> Response:
            # BEFORE logic
            request_id = app.current_event.request_context.request_id
            correlation_id = app.current_event.get_header_value(
                name=self.header,
                default_value=request_id,
            )

            # Call next middleware or route handler ('/todos')
            response = next_middleware(app)

            # AFTER logic
            response.headers[self.header] = correlation_id

            return response


    @app.get("/todos", middlewares=[CorrelationIdMiddleware(header="x-correlation-id")])
    def get_todos():
        todos: requests.Response = requests.get("https://jsonplaceholder.typicode.com/todos")
        todos.raise_for_status()

        # for brevity, we'll limit to the first 10 only
        return {"todos": todos.json()[:10]}


    @logger.inject_lambda_context
    def lambda_handler(event, context):
        return app.resolve(event, context)

    ```

    """

    @abstractmethod
    def handler(self, app: EventHandlerInstance, next_middleware: NextMiddleware) -> Response:
        """
        The Middleware Handler

        Parameters
        ----------
        app: EventHandlerInstance
            An instance of an Event Handler that implements ApiGatewayResolver
        next_middleware: NextMiddleware
            The next middleware handler in the chain

        Returns
        -------
        Response
            The response from the next middleware handler in the chain

        """
        raise NotImplementedError()

    @property
    def __name__(self) -> str:  # noqa A003
        return str(self.__class__.__name__)

    def __call__(self, app: EventHandlerInstance, next_middleware: NextMiddleware) -> Response:
        """
        The Middleware handler function.

        Parameters
        ----------
        app: ApiGatewayResolver
            An instance of an Event Handler that implements ApiGatewayResolver
        next_middleware: NextMiddleware
            The next middleware handler in the chain

        Returns
        -------
        Response
            The response from the next middleware handler in the chain
        """
        return self.handler(app, next_middleware)

Ancestors

  • typing.Generic
  • abc.ABC

Subclasses

Methods

def handler(self, app: ~EventHandlerInstance, next_middleware: NextMiddleware) ‑> Response

The Middleware Handler

Parameters

app : EventHandlerInstance
An instance of an Event Handler that implements ApiGatewayResolver
next_middleware : NextMiddleware
The next middleware handler in the chain

Returns

Response
The response from the next middleware handler in the chain
Expand source code
@abstractmethod
def handler(self, app: EventHandlerInstance, next_middleware: NextMiddleware) -> Response:
    """
    The Middleware Handler

    Parameters
    ----------
    app: EventHandlerInstance
        An instance of an Event Handler that implements ApiGatewayResolver
    next_middleware: NextMiddleware
        The next middleware handler in the chain

    Returns
    -------
    Response
        The response from the next middleware handler in the chain

    """
    raise NotImplementedError()
class NextMiddleware (*args, **kwargs)

Base class for protocol classes.

Protocol classes are defined as::

class Proto(Protocol):
    def meth(self) -> int:
        ...

Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).

For example::

class C:
    def meth(self) -> int:
        return 0

def func(x: Proto) -> int:
    return x.meth()

func(C())  # Passes static type check

See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as::

class GenProto[T](Protocol):
    def meth(self) -> T:
        ...
Expand source code
class NextMiddleware(Protocol):
    def __call__(self, app: EventHandlerInstance) -> Response:
        """Protocol for callback regardless of next_middleware(app), get_response(app) etc"""
        ...

    def __name__(self) -> str:  # noqa A003
        """Protocol for name of the Middleware"""
        ...

Ancestors

  • typing.Protocol
  • typing.Generic