Skip to content

Swifty helpers for working with AWS Lambda.

License

Notifications You must be signed in to change notification settings

Mobelux/swift-lambda-extras

Folders and files

NameName
Last commit message
Last commit date

Latest commit

3d5faf8 · Oct 22, 2024

History

10 Commits
Oct 22, 2024
Dec 18, 2023
Oct 22, 2024
Oct 22, 2024
Dec 18, 2023
Dec 27, 2023
Oct 22, 2024
Oct 22, 2024
Dec 8, 2023
Oct 22, 2024
Oct 22, 2024
Oct 22, 2024

Repository files navigation

Lambda Extras

Swifty helpers for working with AWS Lambda.

📱 Requirements

Swift 5.9 toolchain with Swift Package Manager.

🖥 Installation

Lambda Extras is distributed using the Swift Package Manager. To install it into a project, add it as a dependency within your Package.swift manifest:

dependencies: [
    .package(url: "https://github.com/Mobelux/swift-lambda-extras.git", from: "0.1.0")
]

Then, add the relevant product to any targets that need access to the library:

.product(name: "<product>", package: "swift-lambda-extras"),

Where <product> is one of the following:

  • LambdaExtrasCore
  • LambdaExtras
  • LambdaMocks

⚙️ Usage

This package is intended to support the creation of lambdas composed of 2 parts:

  • a regular target with a handler implementing the core logic without AWS dependencies
  • an executable target using that regular one

Handler

Create a target without AWS dependencies like AWSLambdaRuntime or AWSLambdaEvents to implement the the lambda's core logic. Add a type to represent all environment variables that will be used in the lambda:

public enum Environment: String {
    case multiplier = "MULTIPLIER"
    ...
}

as well as a model for the handler's input and optionally its output:

public struct Multiplicand: Codable {
    public let value: Int

    public init(value: Int) {
        self.value = value
    }
}

and a handler to implement the core logic of the lambda:

public struct MultiplyHandler {
    public init<C>(
        context: C
    ) async throws where C: InitializationContext, C: EnvironmentValueProvider<Environment> {
        // create any dependencies

        context.handleShutdown { eventLoop in
            // shut dependencies down ...
        }
    }

    public func handle<C>(
        _ event: Multiplicand,
        context: C
    ) async throws -> Int where C: RuntimeContext, C: EnvironmentValueProvider<Environment> {
        let multiplier = try context.value(for: .multiplier)
        return event.value * multiplier
    }
}

Lambda

Create an executable target and declare EnvironmentValueProvider conformances for LambdaInitializationContext and LambdaContext:

extension LambdaInitializationContext: EnvironmentValueProvider {
    public typealias EnvironmentVariable = Environment
}

extension LambdaContext: EnvironmentValueProvider {
    public typealias EnvironmentVariable = Environment
}

and implement a LambdaHandler with a LambdaCoding type like APIGatewayCoder that uses the handler created above:

@main
struct MultiplyLambda: LambdaHandler {
    let coder: APIGatewayCoder<Multiplicand, Int>
    let handler: MultiplyHandler

    init(context: LambdaInitializationContext) async throws {
        self.coder = APIGatewayCoder()
        self.handler = try await MultiplyHandler(context: context)
    }

    func handle(_ event: APIGatewayV2Request, context: LambdaContext) async throws -> APIGatewayV2Response {
        context.logger.info("RECEIVED: \(event)")
        do {
            let subscription = try await coder.decode(event: event)

            let output = try await handler.handle(subscription, context: context)
            context.logger.info("FINISHED: \(output)")

            return try coder.encode(output: output)
        } catch {
            context.logger.error("UNDERLYING ERROR: \(error.localizedDescription)")
            return try coder.encode(error: error)
        }
    }
}