Skip to content

Conversation

natanrolnik
Copy link
Contributor

Add convenience initializer for LambdaRuntime to accept LambdaHandler instances directly

Motivation:

When using the Swift AWS Lambda Runtime with custom handler types that conform to LambdaHandler, developers previously had two options to initialize LambdaRuntime:

  1. Manually wrap their handler with LambdaCodableAdapter and LambdaHandlerAdapter:

    let lambdaHandler = MyHandler()
    let handler = LambdaCodableAdapter(
        encoder: JSONEncoder(),
        decoder: JSONDecoder(),
        handler: LambdaHandlerAdapter(handler: lambdaHandler)
    )
    let runtime = LambdaRuntime(handler: handler)
  2. Use a closure-based initializer that indirectly calls the handler:

    let lambdaHandler = MyHandler()
    let runtime = LambdaRuntime { event, context in
        try await lambdaHandler.handle(event, context: context)
    }

Both approaches are verbose and don't provide a clean, ergonomic API for the common case of initializing LambdaRuntime with a custom LambdaHandler instance. The closure approach also creates an unnecessary indirection layer, wrapping the handler in a ClosureHandler before adapting it, or using LambdaCodableAdapter.

Modifications:

Added a new convenience initializer to LambdaRuntime in Lambda+JSON.swift that accepts a LambdaHandler instance directly:

public convenience init<Event: Decodable, Output, LHandler: LambdaHandler>(
    decoder: JSONDecoder = JSONDecoder(),
    encoder: JSONEncoder = JSONEncoder(),
    logger: Logger = Logger(label: "LambdaRuntime"),
    lambdaHandler: sending LHandler
)
where
    Handler == LambdaCodableAdapter<
        LambdaHandlerAdapter<Event, Output, LHandler>,
        Event,
        Output,
        LambdaJSONEventDecoder,
        LambdaJSONOutputEncoder<Output>
    >,
    LHandler.Event == Event,
    LHandler.Output == Output

This initializer handles the wrapping of the LambdaHandler with the necessary adapters internally, matching the pattern already established for closure-based handlers.

Result:

Developers can now initialize LambdaRuntime with a LambdaHandler instance using a clean, direct API:

let lambdaHandler = MyHandler()
let runtime = LambdaRuntime(lambdaHandler: lambdaHandler)

This provides:

  • Better ergonomics: More intuitive and less verbose API
  • Consistency: Matches the pattern of accepting handlers directly, similar to how StreamingLambdaHandler can be used
  • No extra indirection: Avoids wrapping the handler in an unnecessary ClosureHandler or LambdaCodableAdapter
  • Type safety: Maintains full type inference for Event and Output types from the handler

@sebsto sebsto self-assigned this Oct 9, 2025
@sebsto sebsto added the 🆕 semver/minor Adds new public API. label Oct 9, 2025
@sebsto sebsto self-requested a review October 9, 2025 10:35
@sebsto
Copy link
Contributor

sebsto commented Oct 9, 2025

Love the idea and the simplification. Thank you @natanrolnik

@fabianfett any objection from an API point of view ?

@natanrolnik natanrolnik force-pushed the codable-handler-init branch from 56b4711 to ace567a Compare October 9, 2025 10:42
Copy link
Member

@fabianfett fabianfett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love it!

Copy link
Contributor

@sebsto sebsto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A very welcome addition

@sebsto sebsto merged commit 461c18a into swift-server:main Oct 9, 2025
75 of 76 checks passed
sebsto pushed a commit that referenced this pull request Oct 16, 2025
…Void output (#589)

_Add convenience initializer for `LambdaRuntime` to accept
`LambdaHandler` instances with `Void` output_

### Motivation:

Following PR #581, which added convenience initializers for
`LambdaRuntime` to accept `LambdaHandler` instances with `Encodable`
outputs, there was still a gap for handlers that return `Void`. This is
useful, for example, for AWS event sources like SQS, where the
`AWSLambdaEvents` package provides `SQSEvent` but there's no
corresponding output type - handlers simply process messages and return
`Void`.

Without this initializer, developers had to manually wrap their handlers
with `LambdaCodableAdapter` and `LambdaHandlerAdapter`, which was
verbose and inconsistent with the new API (similar to what is described
in #581)

### Modifications:

1. Added a new `init(decoder:handler:)` convenience initializer to
`LambdaCodableAdapter` in `Lambda+JSON.swift` that accepts a
`JSONDecoder` for handlers with `Void` output.

2. Added a new convenience initializer to `LambdaRuntime` that accepts a
`LambdaHandler` instance with `Void` output directly, handling the
wrapping internally.

3. Updated documentation comments to distinguish between handlers with
`Void` and `Encodable` outputs.

### Result:

Developers can now initialize `LambdaRuntime` with handlers that return
`Void` using a clean, direct API. This is especially useful for event
sources like SQS:

```swift
import AWSLambdaEvents

struct MySQSHandler: LambdaHandler {
    func handle(_ event: SQSEvent, context: LambdaContext) async throws {
        // Process SQS messages
    }
}

let runtime = LambdaRuntime(lambdaHandler: MySQSHandler())
```

This provides API completeness, matching the convenience initializers
for handlers with `Encodable` outputs, and delivers better ergonomics
for common serverless patterns.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🆕 semver/minor Adds new public API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants