@@ -34,7 +34,7 @@ The example consists of:
34341 . ** TenantData** - Immutable struct tracking tenant information:
3535 - ` tenantID ` : Unique identifier for the tenant
3636 - ` requestCount ` : Total number of requests from this tenant
37- - ` firstRequest ` : ISO 8601 timestamp of the first request
37+ - ` firstRequest ` : Unix timestamp (seconds since epoch) of the first request
3838 - ` requests ` : Array of individual request records
3939
40402 . ** TenantDataStore** - Actor-based storage providing thread-safe access to tenant data across invocations
@@ -71,29 +71,52 @@ actor TenantDataStore {
7171
7272// Lambda handler extracts tenant ID from context
7373let runtime = LambdaRuntime {
74- (event : APIGatewayV2Request , context : LambdaContext) -> APIGatewayV2Response in
74+ (event : APIGatewayRequest , context : LambdaContext) -> APIGatewayResponse in
7575
7676 guard let tenantID = context.tenantID else {
77- return APIGatewayV2Response (statusCode : .badRequest , body : " No Tenant ID provided" )
77+ return APIGatewayResponse (statusCode : .badRequest , body : " No Tenant ID provided" )
7878 }
7979
8080 // Process request for this tenant
8181 let currentData = await tenants[tenantID] ?? TenantData (tenantID : tenantID)
8282 let updatedData = currentData.addingRequest ()
8383 await tenants.update (id : tenantID, data : updatedData)
8484
85- return try APIGatewayV2Response (statusCode : .ok , encodableBody : updatedData)
85+ return try APIGatewayResponse (statusCode : .ok , encodableBody : updatedData)
8686}
8787```
8888
8989## Configuration
9090
9191### SAM Template (template.yaml)
9292
93- The function is configured with tenant isolation mode in the SAM template:
93+ The function is configured with tenant isolation mode and API Gateway parameter mapping in the SAM template:
9494
9595``` yaml
96- APIGatewayLambda :
96+ # API Gateway REST API with parameter mapping
97+ MultiTenantApi :
98+ Type : AWS::Serverless::Api
99+ Properties :
100+ StageName : Prod
101+ DefinitionBody :
102+ openapi : 3.0.1
103+ paths :
104+ / :
105+ get :
106+ parameters :
107+ - name : tenant-id
108+ in : query
109+ required : true
110+ x-amazon-apigateway-integration :
111+ type : aws_proxy
112+ httpMethod : POST
113+ uri : !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MultiTenantLambda.Arn}/invocations
114+ # Map query parameter to Lambda tenant header
115+ requestParameters :
116+ integration.request.header.X-Amz-Tenant-Id : method.request.querystring.tenant-id
117+
118+ # Lambda function with tenant isolation
119+ MultiTenantLambda :
97120 Type : AWS::Serverless::Function
98121 Properties :
99122 Runtime : provided.al2023
@@ -102,17 +125,31 @@ APIGatewayLambda:
102125 # Enable tenant isolation mode
103126 TenancyConfig :
104127 TenantIsolationMode : PER_TENANT
105- Events :
106- HttpApiEvent :
107- Type : HttpApi
108128` ` `
109129
110130### Key Configuration Points
111131
112132- **TenancyConfig.TenantIsolationMode**: Set to ` PER_TENANT` to enable tenant isolation
133+ - **Parameter Mapping**: API Gateway maps the `tenant-id` query parameter to the `X-Amz-Tenant-Id` header required by Lambda
134+ - **REST API**: Uses REST API (not HTTP API) to support request parameter mapping
135+ - **OpenAPI Definition**: Defines the integration using OpenAPI 3.0 specification for fine-grained control
113136- **Immutable property**: Tenant isolation can only be enabled when creating a new function
114137- **Required tenant-id**: All invocations must include a tenant identifier
115138
139+ # ## Why Parameter Mapping is Required
140+
141+ Lambda's tenant isolation feature requires the tenant ID to be passed via the `X-Amz-Tenant-Id` header. When using API Gateway :
142+
143+ 1. **Client sends request** with `tenant-id` as a query parameter
144+ 2. **API Gateway transforms** the query parameter into the `X-Amz-Tenant-Id` header
145+ 3. **Lambda receives** the header and routes to the appropriate tenant-isolated environment
146+
147+ This mapping is configured in the `x-amazon-apigateway-integration` section using :
148+ ` ` ` yaml
149+ requestParameters:
150+ integration.request.header.X-Amz-Tenant-Id: method.request.querystring.tenant-id
151+ ` ` `
152+
116153# # Deployment
117154
118155# ## Prerequisites
@@ -140,14 +177,34 @@ APIGatewayLambda:
140177
141178# ## Using API Gateway
142179
143- The tenant ID is passed as a query parameter :
180+ The tenant ID is passed as a query parameter. API Gateway automatically maps it to the `X-Amz-Tenant-Id` header :
144181
145182` ` ` bash
146183# Request from tenant "alice"
147- curl "https://your-api-id.execute-api.us-east-1.amazonaws.com?tenant-id=alice"
184+ curl "https://your-api-id.execute-api.us-east-1.amazonaws.com/Prod ?tenant-id=alice"
148185
149- # Request from tenant "bob"
150- curl "https://your-api-id.execute-api.us-east-1.amazonaws.com?tenant-id=bob"
186+ # Request from tenant "bob"
187+ curl "https://your-api-id.execute-api.us-east-1.amazonaws.com/Prod?tenant-id=bob"
188+
189+ # Multiple requests from the same tenant will reuse the execution environment
190+ for i in {1..5}; do
191+ curl "https://your-api-id.execute-api.us-east-1.amazonaws.com/Prod?tenant-id=alice"
192+ done
193+ ` ` `
194+
195+ # ## Using AWS CLI (Direct Lambda Invocation)
196+
197+ For direct Lambda invocation without API Gateway :
198+
199+ ` ` ` bash
200+ # Synchronous invocation
201+ aws lambda invoke \
202+ --function-name MultiTenantLambda \
203+ --tenant-id alice \
204+ response.json
205+
206+ # View the response
207+ cat response.json
151208` ` `
152209
153210# ## Expected Response
@@ -156,24 +213,26 @@ curl "https://your-api-id.execute-api.us-east-1.amazonaws.com?tenant-id=bob"
156213{
157214 "tenantID": "alice",
158215 "requestCount": 3,
159- "firstRequest": "2024-01-15T10:30:00Z ",
216+ "firstRequest": "1705320000.123456 ",
160217 "requests": [
161218 {
162219 "requestNumber": 1,
163- "timestamp": "2024-01-15T10:30:00Z "
220+ "timestamp": "1705320000.123456 "
164221 },
165222 {
166223 "requestNumber": 2,
167- "timestamp": "2024-01-15T10:31:15Z "
224+ "timestamp": "1705320075.789012 "
168225 },
169226 {
170227 "requestNumber": 3,
171- "timestamp": "2024-01-15T10:32:30Z "
228+ "timestamp": "1705320150.345678 "
172229 }
173230 ]
174231}
175232` ` `
176233
234+ **Note**: Timestamps are Unix epoch times (seconds since January 1, 1970) for cross-platform compatibility.
235+
177236# # How Tenant Isolation Works
178237
1792381. **Request arrives** with a tenant identifier (via query parameter, header, or direct invocation)
0 commit comments