This example demonstrates how to use a StreamingLambdaHandlerWithEvent protocol to create Lambda functions, exposed through a FunctionUrl, that:
- Receive JSON input: Automatically decode JSON events into Swift structs
- Stream responses: Send data incrementally as it becomes available
- Execute background work: Perform additional processing after the response is sent
- Function URL Only: This streaming codable approach only works with Lambda functions exposed through Lambda Function URLs
- Limited Request Access: This approach hides the details of the
FunctionURLRequest(like HTTP headers, query parameters, etc.) from developers
Decision Rule:
-
Use this streaming codable approach when:
- Your function is exposed through a Lambda Function URL
- You have a JSON payload that you want automatically decoded
- You don't need to inspect HTTP headers, query parameters, or other request details
- You prioritize convenience over flexibility
-
Use the ByteBuffer
StreamingLambdaHandlerapproach when:- You need full control over the
FunctionURLRequestdetails - You're invoking the Lambda through other means (API Gateway, direct invocation, etc.)
- You need access to HTTP headers, query parameters, or request metadata
- You require maximum flexibility (requires writing more code)
- You need full control over the
This example balances convenience and flexibility. The streaming codable interface combines the benefits of:
- Type-safe JSON input decoding (like regular
LambdaHandler) - Response streaming capabilities (like
StreamingLambdaHandler) - Background work execution after response completion
Streaming responses incurs a cost. For more information, see AWS Lambda Pricing.
You can stream responses through Lambda function URLs, the AWS SDK, or using the Lambda InvokeWithResponseStream API.
The sample code creates a StreamingFromEventHandler struct that conforms to the StreamingLambdaHandlerWithEvent protocol provided by the Swift AWS Lambda Runtime.
The handle(...) method of this protocol receives incoming events as a decoded Swift struct (StreamingRequest) and returns the output through a LambdaResponseStreamWriter.
The Lambda function expects a JSON payload with the following structure:
{
"count": 5,
"message": "Hello from streaming Lambda!",
"delayMs": 1000
}Where:
count: Number of messages to stream (1-100)message: The message content to repeatdelayMs: Optional delay between messages in milliseconds (defaults to 500ms)
The response is streamed through the LambdaResponseStreamWriter, which is passed as an argument in the handle function. The code calls the write(_:) function of the LambdaResponseStreamWriter with partial data written repeatedly before finally closing the response stream by calling finish(). Developers can also choose to return the entire output and not stream the response by calling writeAndFinish(_:).
An error is thrown if finish() is called multiple times or if it is called after having called writeAndFinish(_:).
The handle(...) method is marked as mutating to allow handlers to be implemented with a struct.
Once the struct is created and the handle(...) method is defined, the sample code creates a LambdaRuntime struct and initializes it with the handler just created. Then, the code calls run() to start the interaction with the AWS Lambda control plane.
Key features demonstrated:
- JSON Input Decoding: The function automatically parses the JSON input into a
StreamingRequeststruct - Input Validation: Validates the count parameter and returns an error message if invalid
- Progressive Streaming: Sends messages one by one with configurable delays
- Timestamped Output: Each message includes an ISO8601 timestamp
- Background Processing: Performs cleanup and logging after the response is complete
- Error Handling: Gracefully handles invalid input with descriptive error messages
To build & archive the package, type the following commands.
swift package --allow-network-connections docker lambda-buildIf there is no error, there is a ZIP file ready to deploy.
The ZIP file is located at .build/plugins/AWSLambdaBuilder/outputs/AWSLambdaBuilder/StreamingFromEvent/StreamingFromEvent.zip
You can test the function locally before deploying:
swift run
# In another terminal, test with curl:
curl -v \
--header "Content-Type: application/json" \
--data '{"count": 3, "message": "Hello World!", "delayMs": 1000}' \
http://127.0.0.1:7000/invokeOr simulate a call from a Lambda Function URL (where the body is encapsulated in a Lambda Function URL request):
curl -v \
--header "Content-Type: application/json" \
--data @events/sample-request.json \
http://127.0.0.1:7000/invokeHere is how to deploy using the lambda-deploy plugin with a Function URL.
swift package --allow-network-connections all:443 lambda-deploy --with-urlThis creates the Lambda function, provisions the necessary IAM role, configures a Function URL with IAM authentication, and uploads the deployment package. The output will include the Function URL and a ready-to-use curl command.
To invoke the Lambda function, use curl with the AWS Sigv4 option to generate the signature.
Read the AWS Credentials and Signature section for more details about the AWS Sigv4 protocol and how to obtain AWS credentials.
When you have the aws command line installed and configured, you will find the credentials in the ~/.aws/credentials file.
URL=https://ul3nf4dogmgyr7ffl5r5rs22640fwocc.lambda-url.us-east-1.on.aws/
REGION=us-east-1
# Set the AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN environment variables
eval $(aws configure export-credentials --format env)
curl --user "${AWS_ACCESS_KEY_ID}":"${AWS_SECRET_ACCESS_KEY}" \
--aws-sigv4 "aws:amz:${REGION}:lambda" \
-H "x-amz-security-token: ${AWS_SESSION_TOKEN}" \
--no-buffer \
--header "Content-Type: application/json" \
--data '{"count": 3, "message": "Hello World!", "delayMs": 1000}' \
"$URL" This should output the following result, with configurable delays between each message:
[2024-07-15T05:00:00Z] Message 1/3: Hello World!
[2024-07-15T05:00:01Z] Message 2/3: Hello World!
[2024-07-15T05:00:02Z] Message 3/3: Hello World!
✅ Successfully sent 3 messages
When done testing, you can delete the Lambda function with this command.
swift package --allow-network-connections all:443 lambda-deploy --deleteAlternatively, you can use AWS SAM to deploy the Lambda function.
Prerequisites : Install the SAM CLI
The template file is provided as part of the example in the template.yaml file. It defines a Lambda function based on the binary ZIP file. It creates the function url with IAM authentication and sets the function timeout to 15 seconds.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: SAM Template for StreamingFromEvent Example
Resources:
# Lambda function
StreamingNumbers:
Type: AWS::Serverless::Function
Properties:
CodeUri: .build/plugins/AWSLambdaBuilder/outputs/AWSLambdaBuilder/StreamingFromEvent/StreamingFromEvent.zip
Timeout: 15
Handler: swift.bootstrap # ignored by the Swift runtime
Runtime: provided.al2023
MemorySize: 128
Architectures:
- arm64
FunctionUrlConfig:
AuthType: AWS_IAM
InvokeMode: RESPONSE_STREAM
Outputs:
# print Lambda function URL
LambdaURL:
Description: Lambda URL
Value: !GetAtt StreamingNumbersUrl.FunctionUrlsam deploy \
--resolve-s3 \
--template-file template.yaml \
--stack-name StreamingFromEvent \
--capabilities CAPABILITY_IAM The URL of the function is provided as part of the output.
CloudFormation outputs from deployed stack
-----------------------------------------------------------------------------------------------------------------------------
Outputs
-----------------------------------------------------------------------------------------------------------------------------
Key LambdaURL
Description Lambda URL
Value https://gaudpin2zjqizfujfnqxstnv6u0czrfu.lambda-url.us-east-1.on.aws/
-----------------------------------------------------------------------------------------------------------------------------
Once the function is deployed, you can invoke it with curl, similarly to what you did when deploying with the AWS CLI.
# Set the AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN environment variables
eval $(aws configure export-credentials --format env)
curl -X POST \
--data '{"count": 3, "message": "Hello World!", "delayMs": 1000}' \
--user "$AWS_ACCESS_KEY_ID":"$AWS_SECRET_ACCESS_KEY" \
--aws-sigv4 "aws:amz:${REGION}:lambda" \
-H "x-amz-security-token: $AWS_SESSION_TOKEN" \
--no-buffer \
"$URL"When done testing, you can delete the infrastructure with this command.
sam delete These are example applications for demonstration purposes. When deploying such infrastructure in production environments, we strongly encourage you to follow these best practices for improved security and resiliency:
- Enable access logging on API Gateway (documentation)
- Ensure that AWS Lambda function is configured for function-level concurrent execution limit (concurrency documentation, configuration guide)
- Check encryption settings for Lambda environment variables (documentation)
- Ensure that AWS Lambda function is configured for a Dead Letter Queue (DLQ) (documentation)
- Ensure that AWS Lambda function is configured inside a VPC when it needs to access private resources (documentation, code example)