mirror of
https://github.com/swift-server/swift-aws-lambda-runtime.git
synced 2026-05-03 07:22:27 +00:00
dee635267b
Change Examples, README, and doc to refer to https://github.org/awslabs instead of https://github.org/swift-server --------- Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
225 lines
12 KiB
Plaintext
225 lines
12 KiB
Plaintext
@Tutorial(time: 15) {
|
|
@Intro(title: "Write your first Lambda function") {
|
|
Learn how to create your project, add dependencies, and create and test your first Lambda function in Swift.
|
|
|
|
In this example, we will create a Lambda function that receives a text and checks if this text is a palindrome or not.
|
|
|
|
A palindrome is a word or phrase that reads the same forward and backward.
|
|
}
|
|
|
|
@Section(title: "Initialize a new project") {
|
|
@ContentAndMedia() {
|
|
Create a new swift project and open Xcode.
|
|
@Image(source: "03-01-terminal-package-init", alt: "A Terminal with package init command")
|
|
}
|
|
|
|
@Steps {
|
|
|
|
Start in your development folder.
|
|
|
|
@Step {
|
|
Open a Terminal and create a directory for your Lambda function.
|
|
@Code(name: "Commands to type in the Terminal", file: 03-01-01-package-init.sh)
|
|
}
|
|
|
|
@Step {
|
|
Initialize a new Swift package for an executable target.
|
|
@Code(name: "Commands to type in the Terminal", file: 03-01-02-package-init.sh)
|
|
}
|
|
|
|
@Step {
|
|
Open Xcode in this newly created directory.
|
|
@Code(name: "Commands to type in the Terminal", file: 03-01-03-package-init.sh)
|
|
}
|
|
|
|
@Step {
|
|
Alternatively, if you use VSCode, use the `code` command to open VSCode in your project repository.
|
|
@Code(name: "Commands to type in the Terminal", file: 03-01-04-package-init.sh)
|
|
}
|
|
|
|
@Step {
|
|
In your development environment, expand the project if necessary and open the file `Package.swift`.
|
|
|
|
If you are an iOS developer, you might wonder what is a `Package.swift`. In simple terms, your `Package.swift` defines the dependencies your code has and what products (libraries and/or executables) your code offers.
|
|
@Image(source: 03-01-xcode.png, alt: "project open in Xcode")
|
|
}
|
|
}
|
|
}
|
|
|
|
@Section(title: "Add the project dependencies") {
|
|
@ContentAndMedia() {
|
|
Prepare `Package.swift` to define the project targets and dependencies.
|
|
@Image(source: "03-02-swift-package-manager.png", alt: "Swift Package Manager icon as a box")
|
|
}
|
|
@Steps {
|
|
@Step {
|
|
In the Xcode editor, replace the content of `Package.swift` with the file on the right side of the screen.
|
|
|
|
It defines a package for a project named `Palindrome`. The package name only matters when you build a library that is used by other Swift packages.
|
|
|
|
> Comments are important here, do not skip them. They define the minimum version of Swift to use.
|
|
@Code(name: "Package.swift", file: 03-02-01-package.swift)
|
|
}
|
|
@Step {
|
|
Add the `platform` section.
|
|
|
|
It defines on which Apple platforms the code can be executed. Since Lambda functions are supposed to be run on Linux servers with Amazon Linux 2, it is reasonable to make them run only on macOS, for debugging for example. It does not make sense to run this code on iOS, iPadOS, tvOS, and watchOS.
|
|
@Code(name: "Package.swift", file: 03-02-02-package.swift)
|
|
}
|
|
@Step {
|
|
Add the `dependencies` section.
|
|
|
|
It defines what external libraries your code depends on. To run code within AWS Lambda you'll need a runtime that handles the communication with the [Lambda Runtime Interface](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html). This is what the `AWSLambdaRuntime` is for. You import it by specifying its GitHub url: `https://github.com/awslabs/swift-aws-lambda-runtime.git`.
|
|
|
|
@Code(name: "Package.swift", file: 03-02-03-package.swift)
|
|
}
|
|
@Step {
|
|
Add the `target` section.
|
|
|
|
In the `targets` section you specify your own targets. They are pretty comparable to targets you specify within an Xcode project (that's probably why they share the name 😎). In our example we only want to create an executable that is called `PalindromeLambda`. An executable must have an entrypoint. This can be either a `main.swift` or an object that is marked with `@main`. For Lambda we will use the `@main` approach.
|
|
|
|
@Code(name: "Package.swift", file: 03-02-04-package.swift)
|
|
}
|
|
@Step {
|
|
Add the `product` section.
|
|
|
|
To advertise our `executableTarget` as a product of our package, we add it to the `products` section.
|
|
@Code(name: "Package.swift", file: 03-02-05-package.swift)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
@Section(title: "Write the function code") {
|
|
@ContentAndMedia() {
|
|
Now that our project structure is ready, let's write the code of your Lambda function. Perform the following steps in Xcode or another IDE of your choice.
|
|
|
|
@Image(source: 03-03-swift-code-xcode, alt: "Swift code in Xcode")
|
|
}
|
|
|
|
@Steps {
|
|
|
|
@Step {
|
|
Open the `main.swift` file, remove the code generated and write the code to represent the request sent to your Lambda function.
|
|
|
|
Input parameters must conform to the `Decodable` protocol. This ensures that your Lambda function accepts any JSON input.
|
|
|
|
> When your function is triggered by another AWS service, we modeled most of the input and output data format for you. You can add the dependency on [https://github.com/awslabs/swift-aws-lambda-events](https://github.com/awslabs/swift-aws-lambda-events) and import `AWSLambdaEvents` in your code.
|
|
|
|
@Code(name: "main.swift", file: 03-03-01-main.swift)
|
|
}
|
|
|
|
@Step {
|
|
Write the code to represent the response returned by your Lambda function.
|
|
|
|
Output parameters must conform to the `Encodable` protocol. This ensures that your Lambda function returns a valid JSON output. Your function might also return `Void` if it does not return any value.
|
|
|
|
> You can also write function that stream a response back to the caller. This is useful when you have a large amount of data to return. See the [Lambda Streaming example](https://github.com/awslabs/swift-aws-lambda-runtime/tree/main/Examples/Streaming) for more information.
|
|
|
|
@Code(name: "main.swift", file: 03-03-02-main.swift)
|
|
}
|
|
|
|
@Step {
|
|
Write your business logic.
|
|
|
|
In real life project, this will be the most complex part of your code. It will live in spearate files or libraries. For this example, we will keep it simple and just return `true` if a `String` is a palindrome.
|
|
|
|
@Code(name: "main.swift", file: 03-03-03-main.swift)
|
|
}
|
|
|
|
@Step {
|
|
Add an `import` statement to import the `AWSLambdaRuntime` library.
|
|
|
|
@Code(name: "main.swift", file: 03-03-04-main.swift)
|
|
}
|
|
|
|
@Step {
|
|
Create a `LambdaRuntime` struct and add a handler function that will be called by the Lambda runtime.
|
|
|
|
This function is passed as a closure to the initializer of the `LambdaRuntime` struct. It accepts two parameters: the input event and the context. The input event is the JSON payload sent to your Lambda function. The context provides information about the function, such as the function name, memory limit, and log group name. The function returns the output event, which is the JSON payload returned by your Lambda function or Void if your function does not return any value.
|
|
|
|
@Code(name: "main.swift", file: 03-03-05-main.swift)
|
|
}
|
|
|
|
@Step {
|
|
Add the business logic to the handler function and return the response.
|
|
|
|
In this example, we call the `isPalindrome(_:)` function to check if the input string is a palindrome. Then, we create a response with the result of the check.
|
|
|
|
@Code(name: "main.swift", file: 03-03-06-main.swift)
|
|
}
|
|
|
|
@Step {
|
|
Start the runtime by calling the `run()` function.
|
|
|
|
This function starts the Lambda runtime and listens for incoming requests. When a request is received, it calls the handler function with the input event and context. The handler function processes the request and returns the output event. The runtime sends the output event back to the caller. This function might `throw` an error if the runtime fails to process an event or if the handler function throws an error. This function is asynchronous and does not return until the runtime is stopped.
|
|
|
|
@Code(name: "main.swift", file: 03-03-07-main.swift)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
@Section(title: "Test Your Code Locally") {
|
|
@ContentAndMedia() {
|
|
Before to deploy your Lambda to AWS, you want to ensure that it works on your local machine.
|
|
|
|
The `AWSLambdaRuntime` embeds a simple web server you can start and use to send your requests to your Lambda function.
|
|
|
|
@Image(source: 03-04-test-locally.png, alt: "Icons of succeeded and failed tests")
|
|
}
|
|
|
|
@Steps {
|
|
|
|
The embedded web server starts only when compiling in `DEBUG` mode and when the code is not run inside a Lambda function environment. You will start the test server directly from Xcode.
|
|
|
|
@Step {
|
|
Compile and run your project. Click on the `Run` button (▶️) in Xcode.
|
|
|
|
@Image(source: 03-04-01-compile-run.png, alt: "Compile and run the project")
|
|
}
|
|
|
|
@Step {
|
|
Verify the server is correctlys started. You should see the following output in the console.
|
|
|
|
@Code(name: "Console output", file: 03-04-02-console-output.sh)
|
|
}
|
|
|
|
@Step {
|
|
Now that the local server started, open a Terminal and use `curl` or any other HTTP client to POST your input payload to `127.0.0.1:7000`.
|
|
|
|
@Code(name: "curl command in a terminal", file: 03-04-03-curl.sh)
|
|
}
|
|
|
|
@Step {
|
|
When you pass `'{"text": "Was it a car or a cat I saw?"}'`, you should receive the response `{"message":"Your text is a palindrome","isPalindrome":true,"text":"Was it a car or a cat I saw?"}`
|
|
|
|
> Do not forget to stop the running scheme in Xcode (⏹️) when you're done.
|
|
|
|
@Code(name: "curl command in a terminal", file: 03-04-04-curl.sh)
|
|
}
|
|
|
|
Alternatively, you can use the command line from the Terminal.
|
|
|
|
@Step {
|
|
Use the command `swift run` to start the local embedded web server.
|
|
@Code(name: "curl command in a terminal", file: 03-04-06-terminal.sh)
|
|
}
|
|
|
|
@Step {
|
|
You should see the following output in the console.
|
|
@Code(name: "curl command in a terminal", file: 03-04-07-terminal.sh)
|
|
}
|
|
|
|
@Step {
|
|
Now that the local server started, open a second tab in the Terminal and use `curl` or any other HTTP client to POST your input payload to `127.0.0.1:7000`.
|
|
|
|
> Do not forget to stop the local server with `CTRL-C` when you're done.
|
|
@Code(name: "curl command in a terminal", file: 03-04-03-curl.sh)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|