Skip to main content

Source layouts

Start with Getting Started for a complete project. The same layout is used by the Quickstart REST example and HTTP API example. LambdaApi accepts the public SourceLayout values SourceLayout.ROOT and SourceLayout.SERVICE. The default is ROOT.

In all layouts, lambda_path is the directory scanned recursively. Python files are parsed as source, and each top-level function with one or more recognized HTTP decorators becomes a Lambda entry point. The handler name is the Python function name. lambda_handler is a conventional name that works well in these examples.

SourceLayout.ROOT​

lambda_path is the packaging root. Every discovered handler is packaged from that directory, and its path relative to lambda_path becomes the index/module path.

project/
├── lambdas/
│ ├── __init__.py
│ ├── health.py
│ ├── users/
│ │ ├── __init__.py
│ │ └── handler.py
│ └── requirements.txt
└── stack.py
lambdas/health.py
from lambda_api_decorators import GET


@GET("/health")
def lambda_handler(event, context):
return {"statusCode": 200, "body": "ok"}

With lambda_path="lambdas", this handler uses:

ItemResult
lambda_pathlambdas
entry/source directorylambdas
module and handlerhealth.lambda_handler
dependencieslambdas/requirements.txt

For lambdas/users/handler.py, the corresponding result is entry directory lambdas, module and handler users.handler.lambda_handler.

SourceLayout.SERVICE​

The first directory below lambda_path is the packaging boundary for each handler. Every handler must be inside such a first-level service directory; a Python file directly under lambda_path raises ValueError.

project/
├── services/
│ ├── orders/
│ │ ├── __init__.py
│ │ ├── handlers/
│ │ │ ├── __init__.py
│ │ │ └── create.py
│ │ └── requirements.txt
│ └── users/
│ ├── __init__.py
│ ├── handlers/
│ │ ├── __init__.py
│ │ └── get.py
│ └── requirements.txt
└── stack.py
services/orders/handlers/create.py
from lambda_api_decorators import POST


@POST("/orders")
def lambda_handler(event, context):
return {"statusCode": 201, "body": "created"}

With lambda_path="services", this handler uses:

ItemResult
lambda_pathservices
entry/source directoryservices/orders
module and handlerhandlers.create.lambda_handler
dependenciesservices/orders/requirements.txt

The REST and HTTP builders use the same source-layout resolution. Only the API route emission differs; see REST and HTTP APIs.

Discovery and naming rules​

The current discovery behavior is deliberately narrow:

  • It walks lambda_path recursively and considers files ending in .py.
  • It inspects top-level def functions. Nested functions, classes, and async def functions are not discovered as handlers.
  • A function is a route only when its decorator is a recognized direct name: GET, POST, PUT, DELETE, or ANY, called with one non-empty literal path. The decorator must be written in a form the AST parser can identify, such as @GET("/items").
  • A handler with a second recognized HTTP decorator is rejected with ValueError; declare separate functions for separate routes.
  • Plain .py files and files whose functions have no recognized HTTP decorator are skipped. A file with only configuration or other decorators does not become a Lambda function.

ROOT accepts handlers directly under lambda_path or at any nested depth. SERVICE requires the first nested directory. The implementation passes the resulting directory and Python file path to CDK's PythonFunction; therefore use normal importable Python package/module names for directories and files (for example, avoid spaces and hyphens), and include __init__.py files when your package layout requires them. The code specifically rejects a string such as "root" in place of the enum member; pass SourceLayout.ROOT or SourceLayout.SERVICE.

The discovery code checks that route decorator paths are non-empty strings, but does not define a broader route grammar. Keep paths valid for the API Gateway resource type you select. For the complete enum definition, see the SourceLayout API reference.