Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions Examples/APIGatewayV1/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// swift-tools-version:6.2

import PackageDescription

// needed for CI to test the local version of the library
import struct Foundation.URL

let package = Package(
name: "swift-aws-lambda-runtime-example",
platforms: [.macOS(.v15)],
products: [
.executable(name: "APIGatewayLambda", targets: ["APIGatewayLambda"])
],
dependencies: [
// during CI, the dependency on local version of swift-aws-lambda-runtime is added dynamically below
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", from: "2.0.0"),
.package(url: "https://github.com/swift-server/swift-aws-lambda-events.git", from: "1.0.0"),
],
targets: [
.executableTarget(
name: "APIGatewayLambda",
dependencies: [
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
.product(name: "AWSLambdaEvents", package: "swift-aws-lambda-events"),
],
path: "Sources"
)
]
)

if let localDepsPath = Context.environment["LAMBDA_USE_LOCAL_DEPS"],
localDepsPath != "",
let v = try? URL(fileURLWithPath: localDepsPath).resourceValues(forKeys: [.isDirectoryKey]),
v.isDirectory == true
{
// when we use the local runtime as deps, let's remove the dependency added above
let indexToRemove = package.dependencies.firstIndex { dependency in
if case .sourceControl(
name: _,
location: "https://github.com/swift-server/swift-aws-lambda-runtime.git",
requirement: _
) = dependency.kind {
return true
}
return false
}
if let indexToRemove {
package.dependencies.remove(at: indexToRemove)
}

// then we add the dependency on LAMBDA_USE_LOCAL_DEPS' path (typically ../..)
print("[INFO] Compiling against swift-aws-lambda-runtime located at \(localDepsPath)")
package.dependencies += [
.package(name: "swift-aws-lambda-runtime", path: localDepsPath)
]
}
142 changes: 142 additions & 0 deletions Examples/APIGatewayV1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# API Gateway

This is a simple example of an AWS Lambda function invoked through an Amazon API Gateway V1.

> [!NOTE]
> This example uses the API Gateway V1 `Api` endpoint type, whereas the [API Gateway V2](https://github.com/swift-server/swift-aws-lambda-runtime/tree/main/Examples/APIGateway) example uses the `HttpApi` endpoint type. For more information, see [Choose between REST AIs and HTTP APIs](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html).

## Code

The Lambda function takes all HTTP headers it receives as input and returns them as output.

The code creates a `LambdaRuntime` struct. In it's simplest form, the initializer takes a function as argument. The function is the handler that will be invoked when the API Gateway receives an HTTP request.

The handler is `(event: APIGatewayRequest, context: LambdaContext) -> APIGatewayResponse`. The function takes two arguments:
- the event argument is a `APIGatewayRequest`. It is the parameter passed by the API Gateway. It contains all data passed in the HTTP request and some meta data.
- the context argument is a `Lambda Context`. It is a description of the runtime context.

The function must return a `APIGatewayResponse`.

`APIGatewayRequest` and `APIGatewayResponse` are defined in the [Swift AWS Lambda Events](https://github.com/swift-server/swift-aws-lambda-events) library.

## Build & Package

To build the package, type the following commands.

```bash
swift build
swift package archive --allow-network-connections docker
```

If there is no error, there is a ZIP file ready to deploy.
The ZIP file is located at `.build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/APIGatewayLambda/APIGatewayLambda.zip`

## Deploy

The deployment must include the Lambda function and the API Gateway. We use the [Serverless Application Model (SAM)](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) to deploy the infrastructure.

**Prerequisites** : Install the [SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html)

The example directory contains a file named `template.yaml` that describes the deployment.

To actually deploy your Lambda function and create the infrastructure, type the following `sam` command.

```bash
sam deploy \
--resolve-s3 \
--template-file template.yaml \
--stack-name APIGatewayLambda \
--capabilities CAPABILITY_IAM
```

At the end of the deployment, the script lists the API Gateway endpoint.
The output is similar to this one.

```
-----------------------------------------------------------------------------------------------------------------------------
Outputs
-----------------------------------------------------------------------------------------------------------------------------
Key APIGatewayEndpoint
Description API Gateway endpoint URL"
Value https://a5q74es3k2.execute-api.us-east-1.amazonaws.com
-----------------------------------------------------------------------------------------------------------------------------
```

## Invoke your Lambda function

To invoke the Lambda function, use this `curl` command line.

```bash
curl https://a5q74es3k2.execute-api.us-east-1.amazonaws.com
```

Be sure to replace the URL with the API Gateway endpoint returned in the previous step.

This should print a JSON similar to

```bash
{"httpMethod":"GET","queryStringParameters":{},"isBase64Encoded":false,"resource":"\/","path":"\/","headers":{"X-Forwarded-Port":"3000","X-Forwarded-Proto":"http","User-Agent":"curl\/8.7.1","Host":"localhost:3000","Accept":"*\/*"},"requestContext":{"resourcePath":"\/","identity":{"sourceIp":"127.0.0.1","userAgent":"Custom User Agent String"},"httpMethod":"GET","resourceId":"123456","accountId":"123456789012","apiId":"1234567890","requestId":"a9d2db08-8364-4da4-8237-8912bf8148c8","domainName":"localhost:3000","stage":"Prod","path":"\/"},"multiValueQueryStringParameters":{},"pathParameters":{},"multiValueHeaders":{"Accept":["*\/*"],"Host":["localhost:3000"],"X-Forwarded-Port":["3000"],"User-Agent":["curl\/8.7.1"],"X-Forwarded-Proto":["http"]},"stageVariables":{}}
```

If you have `jq` installed, you can use it to pretty print the output.

```bash
curl -s https://a5q74es3k2.execute-api.us-east-1.amazonaws.com | jq
{
"stageVariables": {},
"queryStringParameters": {},
"multiValueHeaders": {
"Accept": [
"*/*"
],
"User-Agent": [
"curl/8.7.1"
],
"X-Forwarded-Proto": [
"http"
],
"Host": [
"localhost:3000"
],
"X-Forwarded-Port": [
"3000"
]
},
"pathParameters": {},
"isBase64Encoded": false,
"path": "/",
"requestContext": {
"apiId": "1234567890",
"stage": "Prod",
"httpMethod": "GET",
"domainName": "localhost:3000",
"requestId": "a9d2db08-8364-4da4-8237-8912bf8148c8",
"identity": {
"userAgent": "Custom User Agent String",
"sourceIp": "127.0.0.1"
},
"resourceId": "123456",
"path": "/",
"resourcePath": "/",
"accountId": "123456789012"
},
"multiValueQueryStringParameters": {},
"resource": "/",
"headers": {
"Accept": "*/*",
"X-Forwarded-Proto": "http",
"X-Forwarded-Port": "3000",
"Host": "localhost:3000",
"User-Agent": "curl/8.7.1"
},
"httpMethod": "GET"
}
```

## Undeploy

When done testing, you can delete the infrastructure with this command.

```bash
sam delete
```
30 changes: 30 additions & 0 deletions Examples/APIGatewayV1/Sources/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2025 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import AWSLambdaEvents
import AWSLambdaRuntime

let runtime = LambdaRuntime {
(event: APIGatewayRequest, context: LambdaContext) -> APIGatewayResponse in

var header = HTTPHeaders()
context.logger.debug("Rest API Message received")

header["content-type"] = "application/json"

// echo the request in the response
return try APIGatewayResponse(statusCode: .ok, headers: header, encodableBody: event)
}

try await runtime.run()
34 changes: 34 additions & 0 deletions Examples/APIGatewayV1/template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: SAM Template for APIGateway Lambda Example

Resources:
# Lambda function
APIGatewayLambda:
Type: AWS::Serverless::Function
Properties:
CodeUri: .build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/APIGatewayLambda/APIGatewayLambda.zip
Timeout: 60
Handler: swift.bootstrap # ignored by the Swift runtime
Runtime: provided.al2
MemorySize: 128
Architectures:
- arm64
Environment:
Variables:
# by default, AWS Lambda runtime produces no log
# use `LOG_LEVEL: debug` for for lifecycle and event handling information
# use `LOG_LEVEL: trace` for detailed input event information
LOG_LEVEL: debug
Events:
RestApi:
Type: Api
Properties:
Path: /
Method: GET

Outputs:
# print API Gateway endpoint
APIGatewayEndpoint:
Description: "API Gateway endpoint URL"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"