Module aws_lambda_powertools.event_handler

Event handler decorators for common Lambda events

Expand source code
"""
Event handler decorators for common Lambda events
"""

from aws_lambda_powertools.event_handler.api_gateway import (
    ALBResolver,
    APIGatewayHttpResolver,
    ApiGatewayResolver,
    APIGatewayRestResolver,
    CORSConfig,
    Response,
)
from aws_lambda_powertools.event_handler.appsync import AppSyncResolver
from aws_lambda_powertools.event_handler.bedrock_agent import BedrockAgentResolver
from aws_lambda_powertools.event_handler.lambda_function_url import (
    LambdaFunctionUrlResolver,
)
from aws_lambda_powertools.event_handler.vpc_lattice import VPCLatticeResolver, VPCLatticeV2Resolver

__all__ = [
    "AppSyncResolver",
    "APIGatewayRestResolver",
    "APIGatewayHttpResolver",
    "ALBResolver",
    "ApiGatewayResolver",
    "BedrockAgentResolver",
    "CORSConfig",
    "LambdaFunctionUrlResolver",
    "Response",
    "VPCLatticeResolver",
    "VPCLatticeV2Resolver",
]

Sub-modules

aws_lambda_powertools.event_handler.api_gateway
aws_lambda_powertools.event_handler.appsync
aws_lambda_powertools.event_handler.bedrock_agent
aws_lambda_powertools.event_handler.content_types
aws_lambda_powertools.event_handler.exceptions
aws_lambda_powertools.event_handler.lambda_function_url
aws_lambda_powertools.event_handler.middlewares
aws_lambda_powertools.event_handler.openapi
aws_lambda_powertools.event_handler.router
aws_lambda_powertools.event_handler.types
aws_lambda_powertools.event_handler.util
aws_lambda_powertools.event_handler.vpc_lattice

Classes

class ALBResolver (cors: Optional[CORSConfig] = None, debug: Optional[bool] = None, serializer: Optional[Callable[[Dict], str]] = None, strip_prefixes: Optional[List[Union[str, Pattern]]] = None, enable_validation: bool = False)

API Gateway and ALB proxy resolver

Examples

Simple example with a custom lambda handler using the Tracer capture_lambda_handler decorator

from aws_lambda_powertools import Tracer
from aws_lambda_powertools.event_handler import APIGatewayRestResolver

tracer = Tracer()
app = APIGatewayRestResolver()

@app.get("/get-call")
def simple_get():
    return {"message": "Foo"}

@app.post("/post-call")
def simple_post():
    post_data: dict = app.current_event.json_body
    return {"message": post_data["value"]}

@tracer.capture_lambda_handler
def lambda_handler(event, context):
    return app.resolve(event, context)

Amazon Application Load Balancer (ALB) resolver

Expand source code
class ALBResolver(ApiGatewayResolver):
    current_event: ALBEvent

    def __init__(
        self,
        cors: Optional[CORSConfig] = None,
        debug: Optional[bool] = None,
        serializer: Optional[Callable[[Dict], str]] = None,
        strip_prefixes: Optional[List[Union[str, Pattern]]] = None,
        enable_validation: bool = False,
    ):
        """Amazon Application Load Balancer (ALB) resolver"""
        super().__init__(ProxyEventType.ALBEvent, cors, debug, serializer, strip_prefixes, enable_validation)

    def _get_base_path(self) -> str:
        # ALB doesn't have a stage variable, so we just return an empty string
        return ""

Ancestors

Class variables

var current_eventALBEvent

Inherited members

class APIGatewayHttpResolver (cors: Optional[CORSConfig] = None, debug: Optional[bool] = None, serializer: Optional[Callable[[Dict], str]] = None, strip_prefixes: Optional[List[Union[str, Pattern]]] = None, enable_validation: bool = False)

API Gateway and ALB proxy resolver

Examples

Simple example with a custom lambda handler using the Tracer capture_lambda_handler decorator

from aws_lambda_powertools import Tracer
from aws_lambda_powertools.event_handler import APIGatewayRestResolver

tracer = Tracer()
app = APIGatewayRestResolver()

@app.get("/get-call")
def simple_get():
    return {"message": "Foo"}

@app.post("/post-call")
def simple_post():
    post_data: dict = app.current_event.json_body
    return {"message": post_data["value"]}

@tracer.capture_lambda_handler
def lambda_handler(event, context):
    return app.resolve(event, context)

Amazon API Gateway HTTP API v2 payload resolver

Expand source code
class APIGatewayHttpResolver(ApiGatewayResolver):
    current_event: APIGatewayProxyEventV2

    def __init__(
        self,
        cors: Optional[CORSConfig] = None,
        debug: Optional[bool] = None,
        serializer: Optional[Callable[[Dict], str]] = None,
        strip_prefixes: Optional[List[Union[str, Pattern]]] = None,
        enable_validation: bool = False,
    ):
        """Amazon API Gateway HTTP API v2 payload resolver"""
        super().__init__(
            ProxyEventType.APIGatewayProxyEventV2,
            cors,
            debug,
            serializer,
            strip_prefixes,
            enable_validation,
        )

    def _get_base_path(self) -> str:
        # 3 different scenarios:
        #
        # 1. SAM local: even though a stage variable is sent to the Lambda function, it's not used in the path
        # 2. API Gateway HTTP API: stage variable is used in the path
        # 3. API Gateway HTTP Custom Domain: stage variable is not used in the path
        #
        # To solve the 3 scenarios, we try to match the beginning of the path with the stage variable
        stage = self.current_event.request_context.stage
        if stage and stage != "$default" and self.current_event.request_context.http.path.startswith(f"/{stage}"):
            return f"/{stage}"
        return ""

Ancestors

Class variables

var current_eventAPIGatewayProxyEventV2

Inherited members

class APIGatewayRestResolver (cors: Optional[CORSConfig] = None, debug: Optional[bool] = None, serializer: Optional[Callable[[Dict], str]] = None, strip_prefixes: Optional[List[Union[str, Pattern]]] = None, enable_validation: bool = False)

API Gateway and ALB proxy resolver

Examples

Simple example with a custom lambda handler using the Tracer capture_lambda_handler decorator

from aws_lambda_powertools import Tracer
from aws_lambda_powertools.event_handler import APIGatewayRestResolver

tracer = Tracer()
app = APIGatewayRestResolver()

@app.get("/get-call")
def simple_get():
    return {"message": "Foo"}

@app.post("/post-call")
def simple_post():
    post_data: dict = app.current_event.json_body
    return {"message": post_data["value"]}

@tracer.capture_lambda_handler
def lambda_handler(event, context):
    return app.resolve(event, context)

Amazon API Gateway REST and HTTP API v1 payload resolver

Expand source code
class APIGatewayRestResolver(ApiGatewayResolver):
    current_event: APIGatewayProxyEvent

    def __init__(
        self,
        cors: Optional[CORSConfig] = None,
        debug: Optional[bool] = None,
        serializer: Optional[Callable[[Dict], str]] = None,
        strip_prefixes: Optional[List[Union[str, Pattern]]] = None,
        enable_validation: bool = False,
    ):
        """Amazon API Gateway REST and HTTP API v1 payload resolver"""
        super().__init__(
            ProxyEventType.APIGatewayProxyEvent,
            cors,
            debug,
            serializer,
            strip_prefixes,
            enable_validation,
        )

    def _get_base_path(self) -> str:
        # 3 different scenarios:
        #
        # 1. SAM local: even though a stage variable is sent to the Lambda function, it's not used in the path
        # 2. API Gateway REST API: stage variable is used in the path
        # 3. API Gateway REST Custom Domain: stage variable is not used in the path
        #
        # To solve the 3 scenarios, we try to match the beginning of the path with the stage variable
        stage = self.current_event.request_context.stage
        if stage and stage != "$default" and self.current_event.request_context.path.startswith(f"/{stage}"):
            return f"/{stage}"
        return ""

    # override route to ignore trailing "/" in routes for REST API
    def route(
        self,
        rule: str,
        method: Union[str, Union[List[str], Tuple[str]]],
        cors: Optional[bool] = None,
        compress: bool = False,
        cache_control: Optional[str] = None,
        summary: Optional[str] = None,
        description: Optional[str] = None,
        responses: Optional[Dict[int, OpenAPIResponse]] = None,
        response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
        tags: Optional[List[str]] = None,
        operation_id: Optional[str] = None,
        include_in_schema: bool = True,
        middlewares: Optional[List[Callable[..., Any]]] = None,
    ):
        # NOTE: see #1552 for more context.
        return super().route(
            rule.rstrip("/"),
            method,
            cors,
            compress,
            cache_control,
            summary,
            description,
            responses,
            response_description,
            tags,
            operation_id,
            include_in_schema,
            middlewares,
        )

    # Override _compile_regex to exclude trailing slashes for route resolution
    @staticmethod
    def _compile_regex(rule: str, base_regex: str = _ROUTE_REGEX):
        return super(APIGatewayRestResolver, APIGatewayRestResolver)._compile_regex(rule, "^{}/*$")

Ancestors

Class variables

var current_eventAPIGatewayProxyEvent

Inherited members

class ApiGatewayResolver (proxy_type: enum.Enum = ProxyEventType.APIGatewayProxyEvent, cors: Optional[CORSConfig] = None, debug: Optional[bool] = None, serializer: Optional[Callable[[Dict], str]] = None, strip_prefixes: Optional[List[Union[str, Pattern]]] = None, enable_validation: bool = False)

API Gateway and ALB proxy resolver

Examples

Simple example with a custom lambda handler using the Tracer capture_lambda_handler decorator

from aws_lambda_powertools import Tracer
from aws_lambda_powertools.event_handler import APIGatewayRestResolver

tracer = Tracer()
app = APIGatewayRestResolver()

@app.get("/get-call")
def simple_get():
    return {"message": "Foo"}

@app.post("/post-call")
def simple_post():
    post_data: dict = app.current_event.json_body
    return {"message": post_data["value"]}

@tracer.capture_lambda_handler
def lambda_handler(event, context):
    return app.resolve(event, context)

Parameters

proxy_type : ProxyEventType
Proxy request type, defaults to API Gateway V1
cors : CORSConfig
Optionally configure and enabled CORS. Not each route will need to have to cors=True
debug : Optional[bool]
Enables debug mode, by default False. Can be also be enabled by "POWERTOOLS_DEV" environment variable
serializer : Callable, optional
function to serialize obj to a JSON formatted str, by default json.dumps
strip_prefixes : List[Union[str, Pattern]], optional
optional list of prefixes to be removed from the request path before doing the routing. This is often used with api gateways with multiple custom mappings. Each prefix can be a static string or a compiled regex pattern
enable_validation : Optional[bool]
Enables validation of the request body against the route schema, by default False.
Expand source code
class ApiGatewayResolver(BaseRouter):
    """API Gateway and ALB proxy resolver

    Examples
    --------
    Simple example with a custom lambda handler using the Tracer capture_lambda_handler decorator

    ```python
    from aws_lambda_powertools import Tracer
    from aws_lambda_powertools.event_handler import APIGatewayRestResolver

    tracer = Tracer()
    app = APIGatewayRestResolver()

    @app.get("/get-call")
    def simple_get():
        return {"message": "Foo"}

    @app.post("/post-call")
    def simple_post():
        post_data: dict = app.current_event.json_body
        return {"message": post_data["value"]}

    @tracer.capture_lambda_handler
    def lambda_handler(event, context):
        return app.resolve(event, context)
    ```
    """

    def __init__(
        self,
        proxy_type: Enum = ProxyEventType.APIGatewayProxyEvent,
        cors: Optional[CORSConfig] = None,
        debug: Optional[bool] = None,
        serializer: Optional[Callable[[Dict], str]] = None,
        strip_prefixes: Optional[List[Union[str, Pattern]]] = None,
        enable_validation: bool = False,
    ):
        """
        Parameters
        ----------
        proxy_type: ProxyEventType
            Proxy request type, defaults to API Gateway V1
        cors: CORSConfig
            Optionally configure and enabled CORS. Not each route will need to have to cors=True
        debug: Optional[bool]
            Enables debug mode, by default False. Can be also be enabled by "POWERTOOLS_DEV"
            environment variable
        serializer: Callable, optional
            function to serialize `obj` to a JSON formatted `str`, by default json.dumps
        strip_prefixes: List[Union[str, Pattern]], optional
            optional list of prefixes to be removed from the request path before doing the routing.
            This is often used with api gateways with multiple custom mappings.
            Each prefix can be a static string or a compiled regex pattern
        enable_validation: Optional[bool]
            Enables validation of the request body against the route schema, by default False.
        """
        self._proxy_type = proxy_type
        self._dynamic_routes: List[Route] = []
        self._static_routes: List[Route] = []
        self._route_keys: List[str] = []
        self._exception_handlers: Dict[Type, Callable] = {}
        self._cors = cors
        self._cors_enabled: bool = cors is not None
        self._cors_methods: Set[str] = {"OPTIONS"}
        self._debug = self._has_debug(debug)
        self._enable_validation = enable_validation
        self._strip_prefixes = strip_prefixes
        self.context: Dict = {}  # early init as customers might add context before event resolution
        self.processed_stack_frames = []
        self._response_builder_class = ResponseBuilder[BaseProxyEvent]

        # Allow for a custom serializer or a concise json serialization
        self._serializer = serializer or partial(json.dumps, separators=(",", ":"), cls=Encoder)

        if self._enable_validation:
            from aws_lambda_powertools.event_handler.middlewares.openapi_validation import OpenAPIValidationMiddleware

            self.use([OpenAPIValidationMiddleware()])

    def get_openapi_schema(
        self,
        *,
        title: str = "Powertools API",
        version: str = DEFAULT_API_VERSION,
        openapi_version: str = DEFAULT_OPENAPI_VERSION,
        summary: Optional[str] = None,
        description: Optional[str] = None,
        tags: Optional[List[Union["Tag", str]]] = None,
        servers: Optional[List["Server"]] = None,
        terms_of_service: Optional[str] = None,
        contact: Optional["Contact"] = None,
        license_info: Optional["License"] = None,
    ) -> "OpenAPI":
        """
        Returns the OpenAPI schema as a pydantic model.

        Parameters
        ----------
        title: str
            The title of the application.
        version: str
            The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API
        openapi_version: str, default = "3.0.0"
            The version of the OpenAPI Specification (which the document uses).
        summary: str, optional
            A short summary of what the application does.
        description: str, optional
            A verbose explanation of the application behavior.
        tags: List[Tag | str], optional
            A list of tags used by the specification with additional metadata.
        servers: List[Server], optional
            An array of Server Objects, which provide connectivity information to a target server.
        terms_of_service: str, optional
            A URL to the Terms of Service for the API. MUST be in the format of a URL.
        contact: Contact, optional
            The contact information for the exposed API.
        license_info: License, optional
            The license information for the exposed API.

        Returns
        -------
        OpenAPI: pydantic model
            The OpenAPI schema as a pydantic model.
        """

        from aws_lambda_powertools.event_handler.openapi.compat import (
            GenerateJsonSchema,
            get_compat_model_name_map,
            get_definitions,
        )
        from aws_lambda_powertools.event_handler.openapi.models import OpenAPI, PathItem, Server, Tag
        from aws_lambda_powertools.event_handler.openapi.types import (
            COMPONENT_REF_TEMPLATE,
        )

        # Start with the bare minimum required for a valid OpenAPI schema
        info: Dict[str, Any] = {"title": title, "version": version}

        optional_fields = {
            "summary": summary,
            "description": description,
            "termsOfService": terms_of_service,
            "contact": contact,
            "license": license_info,
        }

        info.update({field: value for field, value in optional_fields.items() if value})

        output: Dict[str, Any] = {"openapi": openapi_version, "info": info}
        if servers:
            output["servers"] = servers
        else:
            # If the servers property is not provided, or is an empty array, the default value would be a Server Object
            # with an url value of /.
            output["servers"] = [Server(url="/")]

        components: Dict[str, Dict[str, Any]] = {}
        paths: Dict[str, Dict[str, Any]] = {}
        operation_ids: Set[str] = set()

        all_routes = self._dynamic_routes + self._static_routes
        all_fields = self._get_fields_from_routes(all_routes)
        model_name_map = get_compat_model_name_map(all_fields)

        # Collect all models and definitions
        schema_generator = GenerateJsonSchema(ref_template=COMPONENT_REF_TEMPLATE)
        field_mapping, definitions = get_definitions(
            fields=all_fields,
            schema_generator=schema_generator,
            model_name_map=model_name_map,
        )

        # Add routes to the OpenAPI schema
        for route in all_routes:
            if not route.include_in_schema:
                continue

            result = route._get_openapi_path(
                dependant=route.dependant,
                operation_ids=operation_ids,
                model_name_map=model_name_map,
                field_mapping=field_mapping,
            )
            if result:
                path, path_definitions = result
                if path:
                    paths.setdefault(route.openapi_path, {}).update(path)
                if path_definitions:
                    definitions.update(path_definitions)

        if definitions:
            components["schemas"] = {k: definitions[k] for k in sorted(definitions)}
        if components:
            output["components"] = components
        if tags:
            output["tags"] = [Tag(name=tag) if isinstance(tag, str) else tag for tag in tags]

        output["paths"] = {k: PathItem(**v) for k, v in paths.items()}

        return OpenAPI(**output)

    def get_openapi_json_schema(
        self,
        *,
        title: str = "Powertools API",
        version: str = DEFAULT_API_VERSION,
        openapi_version: str = DEFAULT_OPENAPI_VERSION,
        summary: Optional[str] = None,
        description: Optional[str] = None,
        tags: Optional[List[Union["Tag", str]]] = None,
        servers: Optional[List["Server"]] = None,
        terms_of_service: Optional[str] = None,
        contact: Optional["Contact"] = None,
        license_info: Optional["License"] = None,
    ) -> str:
        """
        Returns the OpenAPI schema as a JSON serializable dict

        Parameters
        ----------
        title: str
            The title of the application.
        version: str
            The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API
        openapi_version: str, default = "3.0.0"
            The version of the OpenAPI Specification (which the document uses).
        summary: str, optional
            A short summary of what the application does.
        description: str, optional
            A verbose explanation of the application behavior.
        tags: List[Tag, str], optional
            A list of tags used by the specification with additional metadata.
        servers: List[Server], optional
            An array of Server Objects, which provide connectivity information to a target server.
        terms_of_service: str, optional
            A URL to the Terms of Service for the API. MUST be in the format of a URL.
        contact: Contact, optional
            The contact information for the exposed API.
        license_info: License, optional
            The license information for the exposed API.

        Returns
        -------
        str
            The OpenAPI schema as a JSON serializable dict.
        """
        from aws_lambda_powertools.event_handler.openapi.compat import model_json

        return model_json(
            self.get_openapi_schema(
                title=title,
                version=version,
                openapi_version=openapi_version,
                summary=summary,
                description=description,
                tags=tags,
                servers=servers,
                terms_of_service=terms_of_service,
                contact=contact,
                license_info=license_info,
            ),
            by_alias=True,
            exclude_none=True,
            indent=2,
        )

    def enable_swagger(
        self,
        *,
        path: str = "/swagger",
        title: str = "Powertools for AWS Lambda (Python) API",
        version: str = DEFAULT_API_VERSION,
        openapi_version: str = DEFAULT_OPENAPI_VERSION,
        summary: Optional[str] = None,
        description: Optional[str] = None,
        tags: Optional[List[Union["Tag", str]]] = None,
        servers: Optional[List["Server"]] = None,
        terms_of_service: Optional[str] = None,
        contact: Optional["Contact"] = None,
        license_info: Optional["License"] = None,
        swagger_base_url: Optional[str] = None,
        middlewares: Optional[List[Callable[..., Response]]] = None,
    ):
        """
        Returns the OpenAPI schema as a JSON serializable dict

        Parameters
        ----------
        path: str, default = "/swagger"
            The path to the swagger UI.
        title: str
            The title of the application.
        version: str
            The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API
        openapi_version: str, default = "3.0.0"
            The version of the OpenAPI Specification (which the document uses).
        summary: str, optional
            A short summary of what the application does.
        description: str, optional
            A verbose explanation of the application behavior.
        tags: List[Tag, str], optional
            A list of tags used by the specification with additional metadata.
        servers: List[Server], optional
            An array of Server Objects, which provide connectivity information to a target server.
        terms_of_service: str, optional
            A URL to the Terms of Service for the API. MUST be in the format of a URL.
        contact: Contact, optional
            The contact information for the exposed API.
        license_info: License, optional
            The license information for the exposed API.
        swagger_base_url: str, optional
            The base url for the swagger UI. If not provided, we will serve a recent version of the Swagger UI.
        middlewares: List[Callable[..., Response]], optional
            List of middlewares to be used for the swagger route.
        """
        from aws_lambda_powertools.event_handler.openapi.compat import model_json
        from aws_lambda_powertools.event_handler.openapi.models import Server

        @self.get(path, middlewares=middlewares, include_in_schema=False)
        def swagger_handler():
            base_path = self._get_base_path()

            if swagger_base_url:
                swagger_js = f"{swagger_base_url}/swagger-ui-bundle.min.js"
                swagger_css = f"{swagger_base_url}/swagger-ui.min.css"
            else:
                # We now inject CSS and JS into the SwaggerUI file
                swagger_js = Path.open(
                    Path(__file__).parent / "openapi" / "swagger_ui" / "swagger-ui-bundle.min.js",
                ).read()
                swagger_css = Path.open(Path(__file__).parent / "openapi" / "swagger_ui" / "swagger-ui.min.css").read()

            openapi_servers = servers or [Server(url=(base_path or "/"))]

            spec = self.get_openapi_schema(
                title=title,
                version=version,
                openapi_version=openapi_version,
                summary=summary,
                description=description,
                tags=tags,
                servers=openapi_servers,
                terms_of_service=terms_of_service,
                contact=contact,
                license_info=license_info,
            )

            # The .replace('</', '<\\/') part is necessary to prevent a potential issue where the JSON string contains
            # </script> or similar tags. Escaping the forward slash in </ as <\/ ensures that the JSON does not
            # inadvertently close the script tag, and the JSON remains a valid string within the JavaScript code.
            escaped_spec = model_json(
                spec,
                by_alias=True,
                exclude_none=True,
                indent=2,
            ).replace("</", "<\\/")

            # Check for query parameters; if "format" is specified as "json",
            # respond with the JSON used in the OpenAPI spec
            # Example: https://www.example.com/swagger?format=json
            query_params = self.current_event.query_string_parameters or {}
            if query_params.get("format") == "json":
                return Response(
                    status_code=200,
                    content_type="application/json",
                    body=escaped_spec,
                )

            body = generate_swagger_html(escaped_spec, path, swagger_js, swagger_css, swagger_base_url)

            return Response(
                status_code=200,
                content_type="text/html",
                body=body,
            )

    def route(
        self,
        rule: str,
        method: Union[str, Union[List[str], Tuple[str]]],
        cors: Optional[bool] = None,
        compress: bool = False,
        cache_control: Optional[str] = None,
        summary: Optional[str] = None,
        description: Optional[str] = None,
        responses: Optional[Dict[int, OpenAPIResponse]] = None,
        response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
        tags: Optional[List[str]] = None,
        operation_id: Optional[str] = None,
        include_in_schema: bool = True,
        middlewares: Optional[List[Callable[..., Any]]] = None,
    ):
        """Route decorator includes parameter `method`"""

        def register_resolver(func: Callable):
            methods = (method,) if isinstance(method, str) else method
            logger.debug(f"Adding route using rule {rule} and methods: {','.join((m.upper() for m in methods))}")

            cors_enabled = self._cors_enabled if cors is None else cors

            for item in methods:
                _route = Route(
                    item,
                    rule,
                    self._compile_regex(rule),
                    func,
                    cors_enabled,
                    compress,
                    cache_control,
                    summary,
                    description,
                    responses,
                    response_description,
                    tags,
                    operation_id,
                    include_in_schema,
                    middlewares,
                )

                # The more specific route wins.
                # We store dynamic (/studies/{studyid}) and static routes (/studies/fetch) separately.
                # Then attempt a match for static routes before dynamic routes.
                # This ensures that the most specific route is prioritized and processed first (studies/fetch).
                if _route.rule.groups > 0:
                    self._dynamic_routes.append(_route)
                else:
                    self._static_routes.append(_route)

                self._create_route_key(item, rule)

                if cors_enabled:
                    logger.debug(f"Registering method {item.upper()} to Allow Methods in CORS")
                    self._cors_methods.add(item.upper())

            return func

        return register_resolver

    def resolve(self, event, context) -> Dict[str, Any]:
        """Resolves the response based on the provide event and decorator routes

        Parameters
        ----------
        event: Dict[str, Any]
            Event
        context: LambdaContext
            Lambda context
        Returns
        -------
        dict
            Returns the dict response
        """
        if isinstance(event, BaseProxyEvent):
            warnings.warn(
                "You don't need to serialize event to Event Source Data Class when using Event Handler; "
                "see issue #1152",
                stacklevel=2,
            )
            event = event.raw_event

        if self._debug:
            print(self._serializer(event))

        # Populate router(s) dependencies without keeping a reference to each registered router
        BaseRouter.current_event = self._to_proxy_event(event)
        BaseRouter.lambda_context = context

        response = self._resolve().build(self.current_event, self._cors)

        # Debug print Processed Middlewares
        if self._debug:
            print("\nProcessed Middlewares:")
            print("======================")
            print("\n".join(self.processed_stack_frames))
            print("======================")

        self.clear_context()

        return response

    def __call__(self, event, context) -> Any:
        return self.resolve(event, context)

    def _create_route_key(self, item: str, rule: str):
        route_key = item + rule
        if route_key in self._route_keys:
            warnings.warn(
                f"A route like this was already registered. method: '{item}' rule: '{rule}'",
                stacklevel=2,
            )
        self._route_keys.append(route_key)

    def _get_base_path(self) -> str:
        raise NotImplementedError()

    @staticmethod
    def _has_debug(debug: Optional[bool] = None) -> bool:
        # It might have been explicitly switched off (debug=False)
        if debug is not None:
            return debug

        return powertools_dev_is_set()

    @staticmethod
    def _compile_regex(rule: str, base_regex: str = _ROUTE_REGEX):
        """Precompile regex pattern

        Logic
        -----

        1. Find any dynamic routes defined as <pattern>
            e.g. @app.get("/accounts/<account_id>")
        2. Create a new regex by substituting every dynamic route found as a named group (?P<group>),
        and match whole words only (word boundary) instead of a greedy match

            non-greedy example with word boundary

                rule: '/accounts/<account_id>'
                regex: r'/accounts/(?P<account_id>\\w+\\b)'

                value: /accounts/123/some_other_path
                account_id: 123

            greedy example without word boundary

                regex: r'/accounts/(?P<account_id>.+)'

                value: /accounts/123/some_other_path
                account_id: 123/some_other_path
        3. Compiles a regex and include start (^) and end ($) in between for an exact match

        NOTE: See #520 for context
        """
        rule_regex: str = re.sub(_DYNAMIC_ROUTE_PATTERN, _NAMED_GROUP_BOUNDARY_PATTERN, rule)
        return re.compile(base_regex.format(rule_regex))

    def _to_proxy_event(self, event: Dict) -> BaseProxyEvent:  # noqa: PLR0911  # ignore many returns
        """Convert the event dict to the corresponding data class"""
        if self._proxy_type == ProxyEventType.APIGatewayProxyEvent:
            logger.debug("Converting event to API Gateway REST API contract")
            return APIGatewayProxyEvent(event)
        if self._proxy_type == ProxyEventType.APIGatewayProxyEventV2:
            logger.debug("Converting event to API Gateway HTTP API contract")
            return APIGatewayProxyEventV2(event)
        if self._proxy_type == ProxyEventType.BedrockAgentEvent:
            logger.debug("Converting event to Bedrock Agent contract")
            return BedrockAgentEvent(event)
        if self._proxy_type == ProxyEventType.LambdaFunctionUrlEvent:
            logger.debug("Converting event to Lambda Function URL contract")
            return LambdaFunctionUrlEvent(event)
        if self._proxy_type == ProxyEventType.VPCLatticeEvent:
            logger.debug("Converting event to VPC Lattice contract")
            return VPCLatticeEvent(event)
        if self._proxy_type == ProxyEventType.VPCLatticeEventV2:
            logger.debug("Converting event to VPC LatticeV2 contract")
            return VPCLatticeEventV2(event)
        logger.debug("Converting event to ALB contract")
        return ALBEvent(event)

    def _resolve(self) -> ResponseBuilder:
        """Resolves the response or return the not found response"""
        method = self.current_event.http_method.upper()
        path = self._remove_prefix(self.current_event.path)

        for route in self._static_routes + self._dynamic_routes:
            if method != route.method:
                continue
            match_results: Optional[Match] = route.rule.match(path)
            if match_results:
                logger.debug("Found a registered route. Calling function")
                # Add matched Route reference into the Resolver context
                self.append_context(_route=route, _path=path)

                route_keys = self._convert_matches_into_route_keys(match_results)
                return self._call_route(route, route_keys)  # pass fn args

        logger.debug(f"No match found for path {path} and method {method}")
        return self._not_found(method)

    def _remove_prefix(self, path: str) -> str:
        """Remove the configured prefix from the path"""
        if not isinstance(self._strip_prefixes, list):
            return path

        for prefix in self._strip_prefixes:
            if isinstance(prefix, str):
                if path == prefix:
                    return "/"

                if self._path_starts_with(path, prefix):
                    return path[len(prefix) :]

            if isinstance(prefix, Pattern):
                path = re.sub(prefix, "", path)

                # When using regexes, we might get into a point where everything is removed
                # from the string, so we check if it's empty and return /, since there's nothing
                # else to strip anymore.
                if not path:
                    return "/"

        return path

    def _convert_matches_into_route_keys(self, match: Match) -> Dict[str, str]:
        """Converts the regex match into a dict of route keys"""
        return match.groupdict()

    @staticmethod
    def _path_starts_with(path: str, prefix: str):
        """Returns true if the `path` starts with a prefix plus a `/`"""
        if not isinstance(prefix, str) or prefix == "":
            return False

        return path.startswith(prefix + "/")

    def _not_found(self, method: str) -> ResponseBuilder:
        """Called when no matching route was found and includes support for the cors preflight response"""
        headers: Dict[str, Union[str, List[str]]] = {}
        if self._cors:
            logger.debug("CORS is enabled, updating headers.")
            headers.update(self._cors.to_dict(self.current_event.get_header_value("Origin")))

            if method == "OPTIONS":
                logger.debug("Pre-flight request detected. Returning CORS with null response")
                headers["Access-Control-Allow-Methods"] = ",".join(sorted(self._cors_methods))
                return ResponseBuilder(
                    response=Response(status_code=204, content_type=None, headers=headers, body=""),
                    serializer=self._serializer,
                )

        handler = self._lookup_exception_handler(NotFoundError)
        if handler:
            return self._response_builder_class(response=handler(NotFoundError()), serializer=self._serializer)

        return self._response_builder_class(
            response=Response(
                status_code=HTTPStatus.NOT_FOUND.value,
                content_type=content_types.APPLICATION_JSON,
                headers=headers,
                body={"statusCode": HTTPStatus.NOT_FOUND.value, "message": "Not found"},
            ),
            serializer=self._serializer,
        )

    def _call_route(self, route: Route, route_arguments: Dict[str, str]) -> ResponseBuilder:
        """Actually call the matching route with any provided keyword arguments."""
        try:
            # Reset Processed stack for Middleware (for debugging purposes)
            self._reset_processed_stack()

            return self._response_builder_class(
                response=self._to_response(
                    route(router_middlewares=self._router_middlewares, app=self, route_arguments=route_arguments),
                ),
                serializer=self._serializer,
                route=route,
            )
        except Exception as exc:
            # If exception is handled then return the response builder to reduce noise
            response_builder = self._call_exception_handler(exc, route)
            if response_builder:
                return response_builder

            logger.exception(exc)
            if self._debug:
                # If the user has turned on debug mode,
                # we'll let the original exception propagate, so
                # they get more information about what went wrong.
                return self._response_builder_class(
                    response=Response(
                        status_code=500,
                        content_type=content_types.TEXT_PLAIN,
                        body="".join(traceback.format_exc()),
                    ),
                    serializer=self._serializer,
                    route=route,
                )

            raise

    def not_found(self, func: Optional[Callable] = None):
        if func is None:
            return self.exception_handler(NotFoundError)
        return self.exception_handler(NotFoundError)(func)

    def exception_handler(self, exc_class: Union[Type[Exception], List[Type[Exception]]]):
        def register_exception_handler(func: Callable):
            if isinstance(exc_class, list):
                for exp in exc_class:
                    self._exception_handlers[exp] = func
            else:
                self._exception_handlers[exc_class] = func
            return func

        return register_exception_handler

    def _lookup_exception_handler(self, exp_type: Type) -> Optional[Callable]:
        # Use "Method Resolution Order" to allow for matching against a base class
        # of an exception
        for cls in exp_type.__mro__:
            if cls in self._exception_handlers:
                return self._exception_handlers[cls]
        return None

    def _call_exception_handler(self, exp: Exception, route: Route) -> Optional[ResponseBuilder]:
        handler = self._lookup_exception_handler(type(exp))
        if handler:
            try:
                return self._response_builder_class(response=handler(exp), serializer=self._serializer, route=route)
            except ServiceError as service_error:
                exp = service_error

        if isinstance(exp, RequestValidationError):
            # For security reasons, we hide msg details (don't leak Python, Pydantic or file names)
            errors = [{"loc": e["loc"], "type": e["type"]} for e in exp.errors()]

            return self._response_builder_class(
                response=Response(
                    status_code=HTTPStatus.UNPROCESSABLE_ENTITY,
                    content_type=content_types.APPLICATION_JSON,
                    body={"statusCode": HTTPStatus.UNPROCESSABLE_ENTITY, "detail": errors},
                ),
                serializer=self._serializer,
                route=route,
            )

        if isinstance(exp, ServiceError):
            return self._response_builder_class(
                response=Response(
                    status_code=exp.status_code,
                    content_type=content_types.APPLICATION_JSON,
                    body={"statusCode": exp.status_code, "message": exp.msg},
                ),
                serializer=self._serializer,
                route=route,
            )

        return None

    def _to_response(self, result: Union[Dict, Tuple, Response]) -> Response:
        """Convert the route's result to a Response

         3 main result types are supported:

        - Dict[str, Any]: Rest api response with just the Dict to json stringify and content-type is set to
          application/json
        - Tuple[dict, int]: Same dict handling as above but with the option of including a status code
        - Response: returned as is, and allows for more flexibility
        """
        status_code = HTTPStatus.OK
        if isinstance(result, Response):
            return result
        elif isinstance(result, tuple) and len(result) == 2:
            # Unpack result dict and status code from tuple
            result, status_code = result

        logger.debug("Simple response detected, serializing return before constructing final response")
        return Response(
            status_code=status_code,
            content_type=content_types.APPLICATION_JSON,
            body=result,
        )

    def include_router(self, router: "Router", prefix: Optional[str] = None) -> None:
        """Adds all routes and context defined in a router

        Parameters
        ----------
        router : Router
            The Router containing a list of routes to be registered after the existing routes
        prefix : str, optional
            An optional prefix to be added to the originally defined rule
        """

        # Add reference to parent ApiGatewayResolver to support use cases where people subclass it to add custom logic
        router.api_resolver = self

        logger.debug("Merging App context with Router context")
        self.context.update(**router.context)

        logger.debug("Appending Router middlewares into App middlewares.")
        self._router_middlewares = self._router_middlewares + router._router_middlewares

        # use pointer to allow context clearance after event is processed e.g., resolve(evt, ctx)
        router.context = self.context

        # Iterate through the routes defined in the router to configure and apply middlewares for each route
        for route, func in router._routes.items():
            new_route = route

            if prefix:
                rule = route[0]
                rule = prefix if rule == "/" else f"{prefix}{rule}"
                new_route = (rule, *route[1:])

            # Middlewares are stored by route separately - must grab them to include
            # Middleware store the route without prefix, so we must not include prefix when grabbing
            middlewares = router._routes_with_middleware.get(route)

            # Need to use "type: ignore" here since mypy does not like a named parameter after
            # tuple expansion since may cause duplicate named parameters in the function signature.
            # In this case this is not possible since the tuple expansion is from a hashable source
            # and the `middlewares` List is a non-hashable structure so will never be included.
            # Still need to ignore for mypy checks or will cause failures (false-positive)
            self.route(*new_route, middlewares=middlewares)(func)  # type: ignore

    @staticmethod
    def _get_fields_from_routes(routes: Sequence[Route]) -> List["ModelField"]:
        """
        Returns a list of fields from the routes
        """

        from aws_lambda_powertools.event_handler.openapi.compat import ModelField
        from aws_lambda_powertools.event_handler.openapi.dependant import (
            get_flat_params,
        )

        body_fields_from_routes: List["ModelField"] = []
        responses_from_routes: List["ModelField"] = []
        request_fields_from_routes: List["ModelField"] = []

        for route in routes:
            if route.body_field:
                if not isinstance(route.body_field, ModelField):
                    raise AssertionError("A request body myst be a Pydantic Field")
                body_fields_from_routes.append(route.body_field)

            params = get_flat_params(route.dependant)
            request_fields_from_routes.extend(params)

            if route.dependant.return_param:
                responses_from_routes.append(route.dependant.return_param)

            if route.dependant.response_extra_models:
                responses_from_routes.extend(route.dependant.response_extra_models)

        flat_models = list(responses_from_routes + request_fields_from_routes + body_fields_from_routes)
        return flat_models

Ancestors

Subclasses

Methods

def enable_swagger(self, *, path: str = '/swagger', title: str = 'Powertools for AWS Lambda (Python) API', version: str = '1.0.0', openapi_version: str = '3.0.0', summary: Optional[str] = None, description: Optional[str] = None, tags: Optional[List[Union[ForwardRef('Tag'), str]]] = None, servers: Optional[List[ForwardRef('Server')]] = None, terms_of_service: Optional[str] = None, contact: Optional[ForwardRef('Contact')] = None, license_info: Optional[ForwardRef('License')] = None, swagger_base_url: Optional[str] = None, middlewares: Optional[List[Callable[..., Response]]] = None)

Returns the OpenAPI schema as a JSON serializable dict

Parameters

path : str, default = "/swagger"
The path to the swagger UI.
title : str
The title of the application.
version : str
The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API
openapi_version : str, default = "3.0.0"
The version of the OpenAPI Specification (which the document uses).
summary : str, optional
A short summary of what the application does.
description : str, optional
A verbose explanation of the application behavior.
tags : List[Tag, str], optional
A list of tags used by the specification with additional metadata.
servers : List[Server], optional
An array of Server Objects, which provide connectivity information to a target server.
terms_of_service : str, optional
A URL to the Terms of Service for the API. MUST be in the format of a URL.
contact : Contact, optional
The contact information for the exposed API.
license_info : License, optional
The license information for the exposed API.
swagger_base_url : str, optional
The base url for the swagger UI. If not provided, we will serve a recent version of the Swagger UI.
middlewares : List[Callable[…, Response]], optional
List of middlewares to be used for the swagger route.
Expand source code
def enable_swagger(
    self,
    *,
    path: str = "/swagger",
    title: str = "Powertools for AWS Lambda (Python) API",
    version: str = DEFAULT_API_VERSION,
    openapi_version: str = DEFAULT_OPENAPI_VERSION,
    summary: Optional[str] = None,
    description: Optional[str] = None,
    tags: Optional[List[Union["Tag", str]]] = None,
    servers: Optional[List["Server"]] = None,
    terms_of_service: Optional[str] = None,
    contact: Optional["Contact"] = None,
    license_info: Optional["License"] = None,
    swagger_base_url: Optional[str] = None,
    middlewares: Optional[List[Callable[..., Response]]] = None,
):
    """
    Returns the OpenAPI schema as a JSON serializable dict

    Parameters
    ----------
    path: str, default = "/swagger"
        The path to the swagger UI.
    title: str
        The title of the application.
    version: str
        The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API
    openapi_version: str, default = "3.0.0"
        The version of the OpenAPI Specification (which the document uses).
    summary: str, optional
        A short summary of what the application does.
    description: str, optional
        A verbose explanation of the application behavior.
    tags: List[Tag, str], optional
        A list of tags used by the specification with additional metadata.
    servers: List[Server], optional
        An array of Server Objects, which provide connectivity information to a target server.
    terms_of_service: str, optional
        A URL to the Terms of Service for the API. MUST be in the format of a URL.
    contact: Contact, optional
        The contact information for the exposed API.
    license_info: License, optional
        The license information for the exposed API.
    swagger_base_url: str, optional
        The base url for the swagger UI. If not provided, we will serve a recent version of the Swagger UI.
    middlewares: List[Callable[..., Response]], optional
        List of middlewares to be used for the swagger route.
    """
    from aws_lambda_powertools.event_handler.openapi.compat import model_json
    from aws_lambda_powertools.event_handler.openapi.models import Server

    @self.get(path, middlewares=middlewares, include_in_schema=False)
    def swagger_handler():
        base_path = self._get_base_path()

        if swagger_base_url:
            swagger_js = f"{swagger_base_url}/swagger-ui-bundle.min.js"
            swagger_css = f"{swagger_base_url}/swagger-ui.min.css"
        else:
            # We now inject CSS and JS into the SwaggerUI file
            swagger_js = Path.open(
                Path(__file__).parent / "openapi" / "swagger_ui" / "swagger-ui-bundle.min.js",
            ).read()
            swagger_css = Path.open(Path(__file__).parent / "openapi" / "swagger_ui" / "swagger-ui.min.css").read()

        openapi_servers = servers or [Server(url=(base_path or "/"))]

        spec = self.get_openapi_schema(
            title=title,
            version=version,
            openapi_version=openapi_version,
            summary=summary,
            description=description,
            tags=tags,
            servers=openapi_servers,
            terms_of_service=terms_of_service,
            contact=contact,
            license_info=license_info,
        )

        # The .replace('</', '<\\/') part is necessary to prevent a potential issue where the JSON string contains
        # </script> or similar tags. Escaping the forward slash in </ as <\/ ensures that the JSON does not
        # inadvertently close the script tag, and the JSON remains a valid string within the JavaScript code.
        escaped_spec = model_json(
            spec,
            by_alias=True,
            exclude_none=True,
            indent=2,
        ).replace("</", "<\\/")

        # Check for query parameters; if "format" is specified as "json",
        # respond with the JSON used in the OpenAPI spec
        # Example: https://www.example.com/swagger?format=json
        query_params = self.current_event.query_string_parameters or {}
        if query_params.get("format") == "json":
            return Response(
                status_code=200,
                content_type="application/json",
                body=escaped_spec,
            )

        body = generate_swagger_html(escaped_spec, path, swagger_js, swagger_css, swagger_base_url)

        return Response(
            status_code=200,
            content_type="text/html",
            body=body,
        )
def exception_handler(self, exc_class: Union[Type[Exception], List[Type[Exception]]])
Expand source code
def exception_handler(self, exc_class: Union[Type[Exception], List[Type[Exception]]]):
    def register_exception_handler(func: Callable):
        if isinstance(exc_class, list):
            for exp in exc_class:
                self._exception_handlers[exp] = func
        else:
            self._exception_handlers[exc_class] = func
        return func

    return register_exception_handler
def get_openapi_json_schema(self, *, title: str = 'Powertools API', version: str = '1.0.0', openapi_version: str = '3.0.0', summary: Optional[str] = None, description: Optional[str] = None, tags: Optional[List[Union[ForwardRef('Tag'), str]]] = None, servers: Optional[List[ForwardRef('Server')]] = None, terms_of_service: Optional[str] = None, contact: Optional[ForwardRef('Contact')] = None, license_info: Optional[ForwardRef('License')] = None) ‑> str

Returns the OpenAPI schema as a JSON serializable dict

Parameters

title : str
The title of the application.
version : str
The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API
openapi_version : str, default = "3.0.0"
The version of the OpenAPI Specification (which the document uses).
summary : str, optional
A short summary of what the application does.
description : str, optional
A verbose explanation of the application behavior.
tags : List[Tag, str], optional
A list of tags used by the specification with additional metadata.
servers : List[Server], optional
An array of Server Objects, which provide connectivity information to a target server.
terms_of_service : str, optional
A URL to the Terms of Service for the API. MUST be in the format of a URL.
contact : Contact, optional
The contact information for the exposed API.
license_info : License, optional
The license information for the exposed API.

Returns

str
The OpenAPI schema as a JSON serializable dict.
Expand source code
def get_openapi_json_schema(
    self,
    *,
    title: str = "Powertools API",
    version: str = DEFAULT_API_VERSION,
    openapi_version: str = DEFAULT_OPENAPI_VERSION,
    summary: Optional[str] = None,
    description: Optional[str] = None,
    tags: Optional[List[Union["Tag", str]]] = None,
    servers: Optional[List["Server"]] = None,
    terms_of_service: Optional[str] = None,
    contact: Optional["Contact"] = None,
    license_info: Optional["License"] = None,
) -> str:
    """
    Returns the OpenAPI schema as a JSON serializable dict

    Parameters
    ----------
    title: str
        The title of the application.
    version: str
        The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API
    openapi_version: str, default = "3.0.0"
        The version of the OpenAPI Specification (which the document uses).
    summary: str, optional
        A short summary of what the application does.
    description: str, optional
        A verbose explanation of the application behavior.
    tags: List[Tag, str], optional
        A list of tags used by the specification with additional metadata.
    servers: List[Server], optional
        An array of Server Objects, which provide connectivity information to a target server.
    terms_of_service: str, optional
        A URL to the Terms of Service for the API. MUST be in the format of a URL.
    contact: Contact, optional
        The contact information for the exposed API.
    license_info: License, optional
        The license information for the exposed API.

    Returns
    -------
    str
        The OpenAPI schema as a JSON serializable dict.
    """
    from aws_lambda_powertools.event_handler.openapi.compat import model_json

    return model_json(
        self.get_openapi_schema(
            title=title,
            version=version,
            openapi_version=openapi_version,
            summary=summary,
            description=description,
            tags=tags,
            servers=servers,
            terms_of_service=terms_of_service,
            contact=contact,
            license_info=license_info,
        ),
        by_alias=True,
        exclude_none=True,
        indent=2,
    )
def get_openapi_schema(self, *, title: str = 'Powertools API', version: str = '1.0.0', openapi_version: str = '3.0.0', summary: Optional[str] = None, description: Optional[str] = None, tags: Optional[List[Union[ForwardRef('Tag'), str]]] = None, servers: Optional[List[ForwardRef('Server')]] = None, terms_of_service: Optional[str] = None, contact: Optional[ForwardRef('Contact')] = None, license_info: Optional[ForwardRef('License')] = None) ‑> OpenAPI

Returns the OpenAPI schema as a pydantic model.

Parameters

title : str
The title of the application.
version : str
The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API
openapi_version : str, default = "3.0.0"
The version of the OpenAPI Specification (which the document uses).
summary : str, optional
A short summary of what the application does.
description : str, optional
A verbose explanation of the application behavior.
tags : List[Tag | str], optional
A list of tags used by the specification with additional metadata.
servers : List[Server], optional
An array of Server Objects, which provide connectivity information to a target server.
terms_of_service : str, optional
A URL to the Terms of Service for the API. MUST be in the format of a URL.
contact : Contact, optional
The contact information for the exposed API.
license_info : License, optional
The license information for the exposed API.

Returns

OpenAPI : pydantic model
The OpenAPI schema as a pydantic model.
Expand source code
def get_openapi_schema(
    self,
    *,
    title: str = "Powertools API",
    version: str = DEFAULT_API_VERSION,
    openapi_version: str = DEFAULT_OPENAPI_VERSION,
    summary: Optional[str] = None,
    description: Optional[str] = None,
    tags: Optional[List[Union["Tag", str]]] = None,
    servers: Optional[List["Server"]] = None,
    terms_of_service: Optional[str] = None,
    contact: Optional["Contact"] = None,
    license_info: Optional["License"] = None,
) -> "OpenAPI":
    """
    Returns the OpenAPI schema as a pydantic model.

    Parameters
    ----------
    title: str
        The title of the application.
    version: str
        The version of the OpenAPI document (which is distinct from the OpenAPI Specification version or the API
    openapi_version: str, default = "3.0.0"
        The version of the OpenAPI Specification (which the document uses).
    summary: str, optional
        A short summary of what the application does.
    description: str, optional
        A verbose explanation of the application behavior.
    tags: List[Tag | str], optional
        A list of tags used by the specification with additional metadata.
    servers: List[Server], optional
        An array of Server Objects, which provide connectivity information to a target server.
    terms_of_service: str, optional
        A URL to the Terms of Service for the API. MUST be in the format of a URL.
    contact: Contact, optional
        The contact information for the exposed API.
    license_info: License, optional
        The license information for the exposed API.

    Returns
    -------
    OpenAPI: pydantic model
        The OpenAPI schema as a pydantic model.
    """

    from aws_lambda_powertools.event_handler.openapi.compat import (
        GenerateJsonSchema,
        get_compat_model_name_map,
        get_definitions,
    )
    from aws_lambda_powertools.event_handler.openapi.models import OpenAPI, PathItem, Server, Tag
    from aws_lambda_powertools.event_handler.openapi.types import (
        COMPONENT_REF_TEMPLATE,
    )

    # Start with the bare minimum required for a valid OpenAPI schema
    info: Dict[str, Any] = {"title": title, "version": version}

    optional_fields = {
        "summary": summary,
        "description": description,
        "termsOfService": terms_of_service,
        "contact": contact,
        "license": license_info,
    }

    info.update({field: value for field, value in optional_fields.items() if value})

    output: Dict[str, Any] = {"openapi": openapi_version, "info": info}
    if servers:
        output["servers"] = servers
    else:
        # If the servers property is not provided, or is an empty array, the default value would be a Server Object
        # with an url value of /.
        output["servers"] = [Server(url="/")]

    components: Dict[str, Dict[str, Any]] = {}
    paths: Dict[str, Dict[str, Any]] = {}
    operation_ids: Set[str] = set()

    all_routes = self._dynamic_routes + self._static_routes
    all_fields = self._get_fields_from_routes(all_routes)
    model_name_map = get_compat_model_name_map(all_fields)

    # Collect all models and definitions
    schema_generator = GenerateJsonSchema(ref_template=COMPONENT_REF_TEMPLATE)
    field_mapping, definitions = get_definitions(
        fields=all_fields,
        schema_generator=schema_generator,
        model_name_map=model_name_map,
    )

    # Add routes to the OpenAPI schema
    for route in all_routes:
        if not route.include_in_schema:
            continue

        result = route._get_openapi_path(
            dependant=route.dependant,
            operation_ids=operation_ids,
            model_name_map=model_name_map,
            field_mapping=field_mapping,
        )
        if result:
            path, path_definitions = result
            if path:
                paths.setdefault(route.openapi_path, {}).update(path)
            if path_definitions:
                definitions.update(path_definitions)

    if definitions:
        components["schemas"] = {k: definitions[k] for k in sorted(definitions)}
    if components:
        output["components"] = components
    if tags:
        output["tags"] = [Tag(name=tag) if isinstance(tag, str) else tag for tag in tags]

    output["paths"] = {k: PathItem(**v) for k, v in paths.items()}

    return OpenAPI(**output)
def include_router(self, router: Router, prefix: Optional[str] = None) ‑> None

Adds all routes and context defined in a router

Parameters

router : Router
The Router containing a list of routes to be registered after the existing routes
prefix : str, optional
An optional prefix to be added to the originally defined rule
Expand source code
def include_router(self, router: "Router", prefix: Optional[str] = None) -> None:
    """Adds all routes and context defined in a router

    Parameters
    ----------
    router : Router
        The Router containing a list of routes to be registered after the existing routes
    prefix : str, optional
        An optional prefix to be added to the originally defined rule
    """

    # Add reference to parent ApiGatewayResolver to support use cases where people subclass it to add custom logic
    router.api_resolver = self

    logger.debug("Merging App context with Router context")
    self.context.update(**router.context)

    logger.debug("Appending Router middlewares into App middlewares.")
    self._router_middlewares = self._router_middlewares + router._router_middlewares

    # use pointer to allow context clearance after event is processed e.g., resolve(evt, ctx)
    router.context = self.context

    # Iterate through the routes defined in the router to configure and apply middlewares for each route
    for route, func in router._routes.items():
        new_route = route

        if prefix:
            rule = route[0]
            rule = prefix if rule == "/" else f"{prefix}{rule}"
            new_route = (rule, *route[1:])

        # Middlewares are stored by route separately - must grab them to include
        # Middleware store the route without prefix, so we must not include prefix when grabbing
        middlewares = router._routes_with_middleware.get(route)

        # Need to use "type: ignore" here since mypy does not like a named parameter after
        # tuple expansion since may cause duplicate named parameters in the function signature.
        # In this case this is not possible since the tuple expansion is from a hashable source
        # and the `middlewares` List is a non-hashable structure so will never be included.
        # Still need to ignore for mypy checks or will cause failures (false-positive)
        self.route(*new_route, middlewares=middlewares)(func)  # type: ignore
def not_found(self, func: Optional[Callable] = None)
Expand source code
def not_found(self, func: Optional[Callable] = None):
    if func is None:
        return self.exception_handler(NotFoundError)
    return self.exception_handler(NotFoundError)(func)
def resolve(self, event, context) ‑> Dict[str, Any]

Resolves the response based on the provide event and decorator routes

Parameters

event : Dict[str, Any]
Event
context : LambdaContext
Lambda context

Returns

dict
Returns the dict response
Expand source code
def resolve(self, event, context) -> Dict[str, Any]:
    """Resolves the response based on the provide event and decorator routes

    Parameters
    ----------
    event: Dict[str, Any]
        Event
    context: LambdaContext
        Lambda context
    Returns
    -------
    dict
        Returns the dict response
    """
    if isinstance(event, BaseProxyEvent):
        warnings.warn(
            "You don't need to serialize event to Event Source Data Class when using Event Handler; "
            "see issue #1152",
            stacklevel=2,
        )
        event = event.raw_event

    if self._debug:
        print(self._serializer(event))

    # Populate router(s) dependencies without keeping a reference to each registered router
    BaseRouter.current_event = self._to_proxy_event(event)
    BaseRouter.lambda_context = context

    response = self._resolve().build(self.current_event, self._cors)

    # Debug print Processed Middlewares
    if self._debug:
        print("\nProcessed Middlewares:")
        print("======================")
        print("\n".join(self.processed_stack_frames))
        print("======================")

    self.clear_context()

    return response
def route(self, rule: str, method: Union[str, List[str], Tuple[str]], cors: Optional[bool] = None, compress: bool = False, cache_control: Optional[str] = None, summary: Optional[str] = None, description: Optional[str] = None, responses: Optional[Dict[int, OpenAPIResponse]] = None, response_description: str = 'Successful Response', tags: Optional[List[str]] = None, operation_id: Optional[str] = None, include_in_schema: bool = True, middlewares: Optional[List[Callable[..., Any]]] = None)

Route decorator includes parameter method

Expand source code
def route(
    self,
    rule: str,
    method: Union[str, Union[List[str], Tuple[str]]],
    cors: Optional[bool] = None,
    compress: bool = False,
    cache_control: Optional[str] = None,
    summary: Optional[str] = None,
    description: Optional[str] = None,
    responses: Optional[Dict[int, OpenAPIResponse]] = None,
    response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
    tags: Optional[List[str]] = None,
    operation_id: Optional[str] = None,
    include_in_schema: bool = True,
    middlewares: Optional[List[Callable[..., Any]]] = None,
):
    """Route decorator includes parameter `method`"""

    def register_resolver(func: Callable):
        methods = (method,) if isinstance(method, str) else method
        logger.debug(f"Adding route using rule {rule} and methods: {','.join((m.upper() for m in methods))}")

        cors_enabled = self._cors_enabled if cors is None else cors

        for item in methods:
            _route = Route(
                item,
                rule,
                self._compile_regex(rule),
                func,
                cors_enabled,
                compress,
                cache_control,
                summary,
                description,
                responses,
                response_description,
                tags,
                operation_id,
                include_in_schema,
                middlewares,
            )

            # The more specific route wins.
            # We store dynamic (/studies/{studyid}) and static routes (/studies/fetch) separately.
            # Then attempt a match for static routes before dynamic routes.
            # This ensures that the most specific route is prioritized and processed first (studies/fetch).
            if _route.rule.groups > 0:
                self._dynamic_routes.append(_route)
            else:
                self._static_routes.append(_route)

            self._create_route_key(item, rule)

            if cors_enabled:
                logger.debug(f"Registering method {item.upper()} to Allow Methods in CORS")
                self._cors_methods.add(item.upper())

        return func

    return register_resolver

Inherited members

class AppSyncResolver

AppSync resolver decorator

Example

Sample usage

from aws_lambda_powertools.event_handler import AppSyncResolver

app = AppSyncResolver()

@app.resolver(type_name="Query", field_name="listLocations")
def list_locations(page: int = 0, size: int = 10) -> list:
    # Your logic to fetch locations with arguments passed in
    return [{"id": 100, "name": "Smooth Grooves"}]

@app.resolver(type_name="Merchant", field_name="extraInfo")
def get_extra_info() -> dict:
    # Can use "app.current_event.source" to filter within the parent context
    account_type = app.current_event.source["accountType"]
    method = "BTC" if account_type == "NEW" else "USD"
    return {"preferredPaymentMethod": method}

@app.resolver(field_name="commonField")
def common_field() -> str:
    # Would match all fieldNames matching 'commonField'
    return str(uuid.uuid4())
Expand source code
class AppSyncResolver(BaseRouter):
    """
    AppSync resolver decorator

    Example
    -------

    **Sample usage**

        from aws_lambda_powertools.event_handler import AppSyncResolver

        app = AppSyncResolver()

        @app.resolver(type_name="Query", field_name="listLocations")
        def list_locations(page: int = 0, size: int = 10) -> list:
            # Your logic to fetch locations with arguments passed in
            return [{"id": 100, "name": "Smooth Grooves"}]

        @app.resolver(type_name="Merchant", field_name="extraInfo")
        def get_extra_info() -> dict:
            # Can use "app.current_event.source" to filter within the parent context
            account_type = app.current_event.source["accountType"]
            method = "BTC" if account_type == "NEW" else "USD"
            return {"preferredPaymentMethod": method}

        @app.resolver(field_name="commonField")
        def common_field() -> str:
            # Would match all fieldNames matching 'commonField'
            return str(uuid.uuid4())
    """

    def __init__(self):
        super().__init__()
        self.context = {}  # early init as customers might add context before event resolution

    def resolve(
        self,
        event: dict,
        context: LambdaContext,
        data_model: Type[AppSyncResolverEvent] = AppSyncResolverEvent,
    ) -> Any:
        """Resolve field_name

        Parameters
        ----------
        event : dict
            Lambda event
        context : LambdaContext
            Lambda context
        data_model:
            Your data data_model to decode AppSync event, by default AppSyncResolverEvent

        Example
        -------

        ```python
        from aws_lambda_powertools.event_handler import AppSyncResolver
        from aws_lambda_powertools.utilities.typing import LambdaContext

        @app.resolver(field_name="createSomething")
        def create_something(id: str):  # noqa AA03 VNE003
            return id

        def handler(event, context: LambdaContext):
            return app.resolve(event, context)
        ```

        **Bringing custom models**

        ```python
        from aws_lambda_powertools import Logger, Tracer

        from aws_lambda_powertools.logging import correlation_paths
        from aws_lambda_powertools.event_handler import AppSyncResolver

        tracer = Tracer(service="sample_resolver")
        logger = Logger(service="sample_resolver")
        app = AppSyncResolver()


        class MyCustomModel(AppSyncResolverEvent):
            @property
            def country_viewer(self) -> str:
                return self.request_headers.get("cloudfront-viewer-country")


        @app.resolver(field_name="listLocations")
        @app.resolver(field_name="locations")
        def get_locations(name: str, description: str = ""):
            if app.current_event.country_viewer == "US":
                ...
            return name + description


        @logger.inject_lambda_context(correlation_id_path=correlation_paths.APPSYNC_RESOLVER)
        @tracer.capture_lambda_handler
        def lambda_handler(event, context):
            return app.resolve(event, context, data_model=MyCustomModel)
        ```

        Returns
        -------
        Any
            Returns the result of the resolver

        Raises
        -------
        ValueError
            If we could not find a field resolver
        """
        # Maintenance: revisit generics/overload to fix [attr-defined] in mypy usage
        BaseRouter.current_event = data_model(event)
        BaseRouter.lambda_context = context

        resolver = self._get_resolver(BaseRouter.current_event.type_name, BaseRouter.current_event.field_name)
        response = resolver(**BaseRouter.current_event.arguments)
        self.clear_context()

        return response

    def _get_resolver(self, type_name: str, field_name: str) -> Callable:
        """Get resolver for field_name

        Parameters
        ----------
        type_name : str
            Type name
        field_name : str
            Field name

        Returns
        -------
        Callable
            callable function and configuration
        """
        full_name = f"{type_name}.{field_name}"
        resolver = self._resolvers.get(full_name, self._resolvers.get(f"*.{field_name}"))
        if not resolver:
            raise ValueError(f"No resolver found for '{full_name}'")
        return resolver["func"]

    def __call__(
        self,
        event: dict,
        context: LambdaContext,
        data_model: Type[AppSyncResolverEvent] = AppSyncResolverEvent,
    ) -> Any:
        """Implicit lambda handler which internally calls `resolve`"""
        return self.resolve(event, context, data_model)

    def include_router(self, router: "Router") -> None:
        """Adds all resolvers defined in a router

        Parameters
        ----------
        router : Router
            A router containing a dict of field resolvers
        """
        # Merge app and router context
        self.context.update(**router.context)
        # use pointer to allow context clearance after event is processed e.g., resolve(evt, ctx)
        router.context = self.context

        self._resolvers.update(router._resolvers)

Ancestors

Methods

def include_router(self, router: Router) ‑> None

Adds all resolvers defined in a router

Parameters

router : Router
A router containing a dict of field resolvers
Expand source code
def include_router(self, router: "Router") -> None:
    """Adds all resolvers defined in a router

    Parameters
    ----------
    router : Router
        A router containing a dict of field resolvers
    """
    # Merge app and router context
    self.context.update(**router.context)
    # use pointer to allow context clearance after event is processed e.g., resolve(evt, ctx)
    router.context = self.context

    self._resolvers.update(router._resolvers)
def resolve(self, event: dict, context: LambdaContext, data_model: Type[AppSyncResolverEvent] = aws_lambda_powertools.utilities.data_classes.appsync_resolver_event.AppSyncResolverEvent) ‑> Any

Resolve field_name

Parameters

event : dict
Lambda event
context : LambdaContext
Lambda context

data_model: Your data data_model to decode AppSync event, by default AppSyncResolverEvent

Example

from aws_lambda_powertools.event_handler import AppSyncResolver
from aws_lambda_powertools.utilities.typing import LambdaContext

@app.resolver(field_name="createSomething")
def create_something(id: str):  # noqa AA03 VNE003
    return id

def handler(event, context: LambdaContext):
    return app.resolve(event, context)

Bringing custom models

from aws_lambda_powertools import Logger, Tracer

from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.event_handler import AppSyncResolver

tracer = Tracer(service="sample_resolver")
logger = Logger(service="sample_resolver")
app = AppSyncResolver()


class MyCustomModel(AppSyncResolverEvent):
    @property
    def country_viewer(self) -> str:
        return self.request_headers.get("cloudfront-viewer-country")


@app.resolver(field_name="listLocations")
@app.resolver(field_name="locations")
def get_locations(name: str, description: str = ""):
    if app.current_event.country_viewer == "US":
        ...
    return name + description


@logger.inject_lambda_context(correlation_id_path=correlation_paths.APPSYNC_RESOLVER)
@tracer.capture_lambda_handler
def lambda_handler(event, context):
    return app.resolve(event, context, data_model=MyCustomModel)

Returns

Any
Returns the result of the resolver

Raises

ValueError
If we could not find a field resolver
Expand source code
def resolve(
    self,
    event: dict,
    context: LambdaContext,
    data_model: Type[AppSyncResolverEvent] = AppSyncResolverEvent,
) -> Any:
    """Resolve field_name

    Parameters
    ----------
    event : dict
        Lambda event
    context : LambdaContext
        Lambda context
    data_model:
        Your data data_model to decode AppSync event, by default AppSyncResolverEvent

    Example
    -------

    ```python
    from aws_lambda_powertools.event_handler import AppSyncResolver
    from aws_lambda_powertools.utilities.typing import LambdaContext

    @app.resolver(field_name="createSomething")
    def create_something(id: str):  # noqa AA03 VNE003
        return id

    def handler(event, context: LambdaContext):
        return app.resolve(event, context)
    ```

    **Bringing custom models**

    ```python
    from aws_lambda_powertools import Logger, Tracer

    from aws_lambda_powertools.logging import correlation_paths
    from aws_lambda_powertools.event_handler import AppSyncResolver

    tracer = Tracer(service="sample_resolver")
    logger = Logger(service="sample_resolver")
    app = AppSyncResolver()


    class MyCustomModel(AppSyncResolverEvent):
        @property
        def country_viewer(self) -> str:
            return self.request_headers.get("cloudfront-viewer-country")


    @app.resolver(field_name="listLocations")
    @app.resolver(field_name="locations")
    def get_locations(name: str, description: str = ""):
        if app.current_event.country_viewer == "US":
            ...
        return name + description


    @logger.inject_lambda_context(correlation_id_path=correlation_paths.APPSYNC_RESOLVER)
    @tracer.capture_lambda_handler
    def lambda_handler(event, context):
        return app.resolve(event, context, data_model=MyCustomModel)
    ```

    Returns
    -------
    Any
        Returns the result of the resolver

    Raises
    -------
    ValueError
        If we could not find a field resolver
    """
    # Maintenance: revisit generics/overload to fix [attr-defined] in mypy usage
    BaseRouter.current_event = data_model(event)
    BaseRouter.lambda_context = context

    resolver = self._get_resolver(BaseRouter.current_event.type_name, BaseRouter.current_event.field_name)
    response = resolver(**BaseRouter.current_event.arguments)
    self.clear_context()

    return response

Inherited members

class BedrockAgentResolver (debug: bool = False, enable_validation: bool = True)

Bedrock Agent Resolver

See https://aws.amazon.com/bedrock/agents/ for more information.

Examples

Simple example with a custom lambda handler using the Tracer capture_lambda_handler decorator

from aws_lambda_powertools import Tracer
from aws_lambda_powertools.event_handler import BedrockAgentResolver

tracer = Tracer()
app = BedrockAgentResolver()

@app.get("/claims")
def simple_get():
    return "You have 3 claims"

@tracer.capture_lambda_handler
def lambda_handler(event, context):
    return app.resolve(event, context)

Parameters

proxy_type : ProxyEventType
Proxy request type, defaults to API Gateway V1
cors : CORSConfig
Optionally configure and enabled CORS. Not each route will need to have to cors=True
debug : Optional[bool]
Enables debug mode, by default False. Can be also be enabled by "POWERTOOLS_DEV" environment variable
serializer : Callable, optional
function to serialize obj to a JSON formatted str, by default json.dumps
strip_prefixes : List[Union[str, Pattern]], optional
optional list of prefixes to be removed from the request path before doing the routing. This is often used with api gateways with multiple custom mappings. Each prefix can be a static string or a compiled regex pattern
enable_validation : Optional[bool]
Enables validation of the request body against the route schema, by default False.
Expand source code
class BedrockAgentResolver(ApiGatewayResolver):
    """Bedrock Agent Resolver

    See https://aws.amazon.com/bedrock/agents/ for more information.

    Examples
    --------
    Simple example with a custom lambda handler using the Tracer capture_lambda_handler decorator

    ```python
    from aws_lambda_powertools import Tracer
    from aws_lambda_powertools.event_handler import BedrockAgentResolver

    tracer = Tracer()
    app = BedrockAgentResolver()

    @app.get("/claims")
    def simple_get():
        return "You have 3 claims"

    @tracer.capture_lambda_handler
    def lambda_handler(event, context):
        return app.resolve(event, context)
    ```

    """

    current_event: BedrockAgentEvent

    def __init__(self, debug: bool = False, enable_validation: bool = True):
        super().__init__(
            proxy_type=ProxyEventType.BedrockAgentEvent,
            cors=None,
            debug=debug,
            serializer=None,
            strip_prefixes=None,
            enable_validation=enable_validation,
        )
        self._response_builder_class = BedrockResponseBuilder

    @override
    def _convert_matches_into_route_keys(self, match: Match) -> Dict[str, str]:
        # In Bedrock Agents, all the parameters come inside the "parameters" key, not on the apiPath
        # So we have to search for route parameters in the parameters key
        parameters: Dict[str, str] = {}
        if match.groupdict() and self.current_event.parameters:
            parameters = {parameter["name"]: parameter["value"] for parameter in self.current_event.parameters}
        return parameters

Ancestors

Class variables

var current_eventBedrockAgentEvent

Inherited members

class CORSConfig (allow_origin: str = '*', extra_origins: Optional[List[str]] = None, allow_headers: Optional[List[str]] = None, expose_headers: Optional[List[str]] = None, max_age: Optional[int] = None, allow_credentials: bool = False)

CORS Config

Examples

Simple cors example using the default permissive cors, not this should only be used during early prototyping

from aws_lambda_powertools.event_handler import APIGatewayRestResolver

app = APIGatewayRestResolver()

@app.get("/my/path", cors=True)
def with_cors():
    return {"message": "Foo"}

Using a custom CORSConfig where with_cors used the custom provided CORSConfig and without_cors do not include any cors headers.

from aws_lambda_powertools.event_handler.api_gateway import (
    APIGatewayRestResolver, CORSConfig
)

cors_config = CORSConfig(
    allow_origin="https://wwww.example.com/",
    extra_origins=["https://dev.example.com/"],
    expose_headers=["x-exposed-response-header"],
    allow_headers=["x-custom-request-header"],
    max_age=100,
    allow_credentials=True,
)
app = APIGatewayRestResolver(cors=cors_config)

@app.get("/my/path")
def with_cors():
    return {"message": "Foo"}

@app.get("/another-one", cors=False)
def without_cors():
    return {"message": "Foo"}

Parameters

allow_origin : str
The value of the Access-Control-Allow-Origin to send in the response. Defaults to "*", but should only be used during development.
extra_origins : Optional[List[str]]
The list of additional allowed origins.
allow_headers : Optional[List[str]]
The list of additional allowed headers. This list is added to list of built-in allowed headers: Authorization, Content-Type, X-Amz-Date, X-Api-Key, X-Amz-Security-Token.
expose_headers : Optional[List[str]]
A list of values to return for the Access-Control-Expose-Headers
max_age : Optional[int]
The value for the Access-Control-Max-Age
allow_credentials : bool
A boolean value that sets the value of Access-Control-Allow-Credentials
Expand source code
class CORSConfig:
    """CORS Config

    Examples
    --------

    Simple cors example using the default permissive cors, not this should only be used during early prototyping

    ```python
    from aws_lambda_powertools.event_handler import APIGatewayRestResolver

    app = APIGatewayRestResolver()

    @app.get("/my/path", cors=True)
    def with_cors():
        return {"message": "Foo"}
    ```

    Using a custom CORSConfig where `with_cors` used the custom provided CORSConfig and `without_cors`
    do not include any cors headers.

    ```python
    from aws_lambda_powertools.event_handler.api_gateway import (
        APIGatewayRestResolver, CORSConfig
    )

    cors_config = CORSConfig(
        allow_origin="https://wwww.example.com/",
        extra_origins=["https://dev.example.com/"],
        expose_headers=["x-exposed-response-header"],
        allow_headers=["x-custom-request-header"],
        max_age=100,
        allow_credentials=True,
    )
    app = APIGatewayRestResolver(cors=cors_config)

    @app.get("/my/path")
    def with_cors():
        return {"message": "Foo"}

    @app.get("/another-one", cors=False)
    def without_cors():
        return {"message": "Foo"}
    ```
    """

    _REQUIRED_HEADERS = ["Authorization", "Content-Type", "X-Amz-Date", "X-Api-Key", "X-Amz-Security-Token"]

    def __init__(
        self,
        allow_origin: str = "*",
        extra_origins: Optional[List[str]] = None,
        allow_headers: Optional[List[str]] = None,
        expose_headers: Optional[List[str]] = None,
        max_age: Optional[int] = None,
        allow_credentials: bool = False,
    ):
        """
        Parameters
        ----------
        allow_origin: str
            The value of the `Access-Control-Allow-Origin` to send in the response. Defaults to "*", but should
            only be used during development.
        extra_origins: Optional[List[str]]
            The list of additional allowed origins.
        allow_headers: Optional[List[str]]
            The list of additional allowed headers. This list is added to list of
            built-in allowed headers: `Authorization`, `Content-Type`, `X-Amz-Date`,
            `X-Api-Key`, `X-Amz-Security-Token`.
        expose_headers: Optional[List[str]]
            A list of values to return for the Access-Control-Expose-Headers
        max_age: Optional[int]
            The value for the `Access-Control-Max-Age`
        allow_credentials: bool
            A boolean value that sets the value of `Access-Control-Allow-Credentials`
        """
        self._allowed_origins = [allow_origin]
        if extra_origins:
            self._allowed_origins.extend(extra_origins)
        self.allow_headers = set(self._REQUIRED_HEADERS + (allow_headers or []))
        self.expose_headers = expose_headers or []
        self.max_age = max_age
        self.allow_credentials = allow_credentials

    def to_dict(self, origin: Optional[str]) -> Dict[str, str]:
        """Builds the configured Access-Control http headers"""

        # If there's no Origin, don't add any CORS headers
        if not origin:
            return {}

        # If the origin doesn't match any of the allowed origins, and we don't allow all origins ("*"),
        # don't add any CORS headers
        if origin not in self._allowed_origins and "*" not in self._allowed_origins:
            return {}

        # The origin matched an allowed origin, so return the CORS headers
        headers: Dict[str, str] = {
            "Access-Control-Allow-Origin": origin,
            "Access-Control-Allow-Headers": ",".join(sorted(self.allow_headers)),
        }

        if self.expose_headers:
            headers["Access-Control-Expose-Headers"] = ",".join(self.expose_headers)
        if self.max_age is not None:
            headers["Access-Control-Max-Age"] = str(self.max_age)
        if self.allow_credentials is True:
            headers["Access-Control-Allow-Credentials"] = "true"
        return headers

Methods

def to_dict(self, origin: Optional[str]) ‑> Dict[str, str]

Builds the configured Access-Control http headers

Expand source code
def to_dict(self, origin: Optional[str]) -> Dict[str, str]:
    """Builds the configured Access-Control http headers"""

    # If there's no Origin, don't add any CORS headers
    if not origin:
        return {}

    # If the origin doesn't match any of the allowed origins, and we don't allow all origins ("*"),
    # don't add any CORS headers
    if origin not in self._allowed_origins and "*" not in self._allowed_origins:
        return {}

    # The origin matched an allowed origin, so return the CORS headers
    headers: Dict[str, str] = {
        "Access-Control-Allow-Origin": origin,
        "Access-Control-Allow-Headers": ",".join(sorted(self.allow_headers)),
    }

    if self.expose_headers:
        headers["Access-Control-Expose-Headers"] = ",".join(self.expose_headers)
    if self.max_age is not None:
        headers["Access-Control-Max-Age"] = str(self.max_age)
    if self.allow_credentials is True:
        headers["Access-Control-Allow-Credentials"] = "true"
    return headers
class LambdaFunctionUrlResolver (cors: Optional[CORSConfig] = None, debug: Optional[bool] = None, serializer: Optional[Callable[[Dict], str]] = None, strip_prefixes: Optional[List[Union[str, Pattern]]] = None, enable_validation: bool = False)

AWS Lambda Function URL resolver

Notes:

Lambda Function URL follows the API Gateway HTTP APIs Payload Format Version 2.0.

Documentation: - https://docs.aws.amazon.com/lambda/latest/dg/urls-configuration.html - https://docs.aws.amazon.com/lambda/latest/dg/urls-invocation.html#urls-payloads

Examples

Simple example integrating with Tracer

```python from aws_lambda_powertools import Tracer from aws_lambda_powertools.event_handler import LambdaFunctionUrlResolver

tracer = Tracer() app = LambdaFunctionUrlResolver()

@app.get("/get-call") def simple_get(): return {"message": "Foo"}

@app.post("/post-call") def simple_post(): post_data: dict = app.current_event.json_body return {"message": post_data}

@tracer.capture_lambda_handler def lambda_handler(event, context): return app.resolve(event, context)

Parameters

proxy_type : ProxyEventType
Proxy request type, defaults to API Gateway V1
cors : CORSConfig
Optionally configure and enabled CORS. Not each route will need to have to cors=True
debug : Optional[bool]
Enables debug mode, by default False. Can be also be enabled by "POWERTOOLS_DEV" environment variable
serializer : Callable, optional
function to serialize obj to a JSON formatted str, by default json.dumps
strip_prefixes : List[Union[str, Pattern]], optional
optional list of prefixes to be removed from the request path before doing the routing. This is often used with api gateways with multiple custom mappings. Each prefix can be a static string or a compiled regex pattern
enable_validation : Optional[bool]
Enables validation of the request body against the route schema, by default False.
Expand source code
class LambdaFunctionUrlResolver(ApiGatewayResolver):
    """AWS Lambda Function URL resolver

    Notes:
    -----
    Lambda Function URL follows the API Gateway HTTP APIs Payload Format Version 2.0.

    Documentation:
    - https://docs.aws.amazon.com/lambda/latest/dg/urls-configuration.html
    - https://docs.aws.amazon.com/lambda/latest/dg/urls-invocation.html#urls-payloads

    Examples
    --------
    Simple example integrating with Tracer

    ```python
    from aws_lambda_powertools import Tracer
    from aws_lambda_powertools.event_handler import LambdaFunctionUrlResolver

    tracer = Tracer()
    app = LambdaFunctionUrlResolver()

    @app.get("/get-call")
    def simple_get():
        return {"message": "Foo"}

    @app.post("/post-call")
    def simple_post():
        post_data: dict = app.current_event.json_body
        return {"message": post_data}

    @tracer.capture_lambda_handler
    def lambda_handler(event, context):
        return app.resolve(event, context)
    """

    current_event: LambdaFunctionUrlEvent

    def __init__(
        self,
        cors: Optional[CORSConfig] = None,
        debug: Optional[bool] = None,
        serializer: Optional[Callable[[Dict], str]] = None,
        strip_prefixes: Optional[List[Union[str, Pattern]]] = None,
        enable_validation: bool = False,
    ):
        super().__init__(
            ProxyEventType.LambdaFunctionUrlEvent,
            cors,
            debug,
            serializer,
            strip_prefixes,
            enable_validation,
        )

    def _get_base_path(self) -> str:
        stage = self.current_event.request_context.stage
        if stage and stage != "$default" and self.current_event.request_context.http.method.startswith(f"/{stage}"):
            return f"/{stage}"
        return ""

Ancestors

Class variables

var current_eventLambdaFunctionUrlEvent

Inherited members

class Response (status_code: int, content_type: Optional[str] = None, body: Optional[~ResponseT] = None, headers: Optional[Dict[str, Union[str, List[str]]]] = None, cookies: Optional[List[Cookie]] = None, compress: Optional[bool] = None)

Response data class that provides greater control over what is returned from the proxy event

Parameters

status_code : int
Http status code, example 200
content_type : str
Optionally set the Content-Type header, example "application/json". Note this will be merged into any provided http headers
body : Union[str, bytes, None]
Optionally set the response body. Note: bytes body will be automatically base64 encoded
headers : dict[str, Union[str, List[str]]]
Optionally set specific http headers. Setting "Content-Type" here would override the content_type value.
cookies : list[Cookie]
Optionally set cookies.
Expand source code
class Response(Generic[ResponseT]):
    """Response data class that provides greater control over what is returned from the proxy event"""

    def __init__(
        self,
        status_code: int,
        content_type: Optional[str] = None,
        body: Optional[ResponseT] = None,
        headers: Optional[Dict[str, Union[str, List[str]]]] = None,
        cookies: Optional[List[Cookie]] = None,
        compress: Optional[bool] = None,
    ):
        """

        Parameters
        ----------
        status_code: int
            Http status code, example 200
        content_type: str
            Optionally set the Content-Type header, example "application/json". Note this will be merged into any
            provided http headers
        body: Union[str, bytes, None]
            Optionally set the response body. Note: bytes body will be automatically base64 encoded
        headers: dict[str, Union[str, List[str]]]
            Optionally set specific http headers. Setting "Content-Type" here would override the `content_type` value.
        cookies: list[Cookie]
            Optionally set cookies.
        """
        self.status_code = status_code
        self.body = body
        self.base64_encoded = False
        self.headers: Dict[str, Union[str, List[str]]] = headers if headers else {}
        self.cookies = cookies or []
        self.compress = compress
        self.content_type = content_type
        if content_type:
            self.headers.setdefault("Content-Type", content_type)

    def is_json(self) -> bool:
        """
        Returns True if the response is JSON, based on the Content-Type.
        """
        content_type = self.headers.get("Content-Type", "")
        if isinstance(content_type, list):
            content_type = content_type[0]
        return content_type.startswith("application/json")

Ancestors

  • typing.Generic

Methods

def is_json(self) ‑> bool

Returns True if the response is JSON, based on the Content-Type.

Expand source code
def is_json(self) -> bool:
    """
    Returns True if the response is JSON, based on the Content-Type.
    """
    content_type = self.headers.get("Content-Type", "")
    if isinstance(content_type, list):
        content_type = content_type[0]
    return content_type.startswith("application/json")
class VPCLatticeResolver (cors: Optional[CORSConfig] = None, debug: Optional[bool] = None, serializer: Optional[Callable[[Dict], str]] = None, strip_prefixes: Optional[List[Union[str, Pattern]]] = None, enable_validation: bool = False)

VPC Lattice resolver

Documentation: - https://docs.aws.amazon.com/lambda/latest/dg/services-vpc-lattice.html - https://docs.aws.amazon.com/lambda/latest/dg/services-vpc-lattice.html#vpc-lattice-receiving-events

Examples

Simple example integrating with Tracer

```python from aws_lambda_powertools import Tracer from aws_lambda_powertools.event_handler import VPCLatticeResolver

tracer = Tracer() app = VPCLatticeResolver()

@app.get("/get-call") def simple_get(): return {"message": "Foo"}

@app.post("/post-call") def simple_post(): post_data: dict = app.current_event.json_body return {"message": post_data}

@tracer.capture_lambda_handler def lambda_handler(event, context): return app.resolve(event, context)

Amazon VPC Lattice resolver

Expand source code
class VPCLatticeResolver(ApiGatewayResolver):
    """VPC Lattice resolver

    Documentation:
    - https://docs.aws.amazon.com/lambda/latest/dg/services-vpc-lattice.html
    - https://docs.aws.amazon.com/lambda/latest/dg/services-vpc-lattice.html#vpc-lattice-receiving-events

    Examples
    --------
    Simple example integrating with Tracer

    ```python
    from aws_lambda_powertools import Tracer
    from aws_lambda_powertools.event_handler import VPCLatticeResolver

    tracer = Tracer()
    app = VPCLatticeResolver()

    @app.get("/get-call")
    def simple_get():
        return {"message": "Foo"}

    @app.post("/post-call")
    def simple_post():
        post_data: dict = app.current_event.json_body
        return {"message": post_data}

    @tracer.capture_lambda_handler
    def lambda_handler(event, context):
        return app.resolve(event, context)
    """

    current_event: VPCLatticeEvent

    def __init__(
        self,
        cors: Optional[CORSConfig] = None,
        debug: Optional[bool] = None,
        serializer: Optional[Callable[[Dict], str]] = None,
        strip_prefixes: Optional[List[Union[str, Pattern]]] = None,
        enable_validation: bool = False,
    ):
        """Amazon VPC Lattice resolver"""
        super().__init__(ProxyEventType.VPCLatticeEvent, cors, debug, serializer, strip_prefixes, enable_validation)

    def _get_base_path(self) -> str:
        return ""

Ancestors

Class variables

var current_eventVPCLatticeEvent

Inherited members

class VPCLatticeV2Resolver (cors: Optional[CORSConfig] = None, debug: Optional[bool] = None, serializer: Optional[Callable[[Dict], str]] = None, strip_prefixes: Optional[List[Union[str, Pattern]]] = None, enable_validation: bool = False)

VPC Lattice resolver

Documentation: - https://docs.aws.amazon.com/lambda/latest/dg/services-vpc-lattice.html - https://docs.aws.amazon.com/lambda/latest/dg/services-vpc-lattice.html#vpc-lattice-receiving-events

Examples

Simple example integrating with Tracer

```python from aws_lambda_powertools import Tracer from aws_lambda_powertools.event_handler import VPCLatticeV2Resolver

tracer = Tracer() app = VPCLatticeV2Resolver()

@app.get("/get-call") def simple_get(): return {"message": "Foo"}

@app.post("/post-call") def simple_post(): post_data: dict = app.current_event.json_body return {"message": post_data}

@tracer.capture_lambda_handler def lambda_handler(event, context): return app.resolve(event, context)

Amazon VPC Lattice resolver

Expand source code
class VPCLatticeV2Resolver(ApiGatewayResolver):
    """VPC Lattice resolver

    Documentation:
    - https://docs.aws.amazon.com/lambda/latest/dg/services-vpc-lattice.html
    - https://docs.aws.amazon.com/lambda/latest/dg/services-vpc-lattice.html#vpc-lattice-receiving-events

    Examples
    --------
    Simple example integrating with Tracer

    ```python
    from aws_lambda_powertools import Tracer
    from aws_lambda_powertools.event_handler import VPCLatticeV2Resolver

    tracer = Tracer()
    app = VPCLatticeV2Resolver()

    @app.get("/get-call")
    def simple_get():
        return {"message": "Foo"}

    @app.post("/post-call")
    def simple_post():
        post_data: dict = app.current_event.json_body
        return {"message": post_data}

    @tracer.capture_lambda_handler
    def lambda_handler(event, context):
        return app.resolve(event, context)
    """

    current_event: VPCLatticeEventV2

    def __init__(
        self,
        cors: Optional[CORSConfig] = None,
        debug: Optional[bool] = None,
        serializer: Optional[Callable[[Dict], str]] = None,
        strip_prefixes: Optional[List[Union[str, Pattern]]] = None,
        enable_validation: bool = False,
    ):
        """Amazon VPC Lattice resolver"""
        super().__init__(ProxyEventType.VPCLatticeEventV2, cors, debug, serializer, strip_prefixes, enable_validation)

    def _get_base_path(self) -> str:
        return ""

Ancestors

Class variables

var current_eventVPCLatticeEventV2

Inherited members