Skip to content

feat: add AWS Lambda deployment adapter - #197

Open
rturnq wants to merge 3 commits into
mainfrom
claude/aws-lambda-adapter
Open

feat: add AWS Lambda deployment adapter#197
rturnq wants to merge 3 commits into
mainfrom
claude/aws-lambda-adapter

Conversation

@rturnq

@rturnq rturnq commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Description

Adds a new adapter package, @marko/run-adapter-aws-lambda, for deploying Marko Run apps to AWS Lambda behind a Function URL or an API Gateway HTTP API.

  • Builds a self-contained handler (dist/index.mjs, exported as handler) for the payload format version 2.0. It converts the Lambda event into a web Request, runs the app, and converts the Response back into a base64-encoded Lambda result (including set-cookie handling).
  • Serves the bundled static assets from dist/public when no route matches — with immutable caching for /assets/*, a content-type lookup, and a path-traversal guard.
  • marko-run preview runs the handler behind a local HTTP server, so the Lambda build can be tested without deploying or running SAM.
  • Route handlers receive the raw Lambda event and invocation context via the exported AWSLambdaPlatformInfo type.

Package the dist directory and deploy it with index.handler as the handler on a Node.js runtime (SAM, CDK, Serverless Framework, Terraform, or the AWS CLI all work).

Also adds a test fixture to the @marko/run package (aws-lambda-adapter) covering both dev and preview.

Motivation and Context

AWS Lambda is one of the most common serverless deployment targets, but Marko Run had no adapter for it. This produces a self-contained handler compatible with both Function URLs and API Gateway HTTP APIs, serves static assets from the bundle, and can be previewed locally — alongside the existing Node, static, and Netlify adapters.

Notes for reviewers

The runtime's fetch returns a 404 Response (not undefined) for unmatched paths, so the handler checks the router first and only falls back to static-asset serving on a 404 — otherwise asset requests (e.g. /assets/*.js) would never be served. The preview test exercises this end-to-end (hydration works because the client bundle is served).

Screenshots (if appropriate):

Checklist:

  • I have updated/added documentation affected by my changes.
  • I have added tests to cover my changes.

Generated by Claude Code

Add @marko/run-adapter-aws-lambda for deploying Marko Run apps to AWS
Lambda behind a Function URL or API Gateway HTTP API.

- Builds a self-contained handler (dist/index.mjs, exported as `handler`)
  for the payload format version 2.0; converts the event to a web Request
  and the Response back to a base64-encoded Lambda result (with cookies)
- Serves the bundled static assets from dist/public when no route matches
  (immutable caching for /assets, path traversal guard)
- `marko-run preview` runs the handler behind a local HTTP server, so it
  can be tested without deploying
- Exposes the raw event and invocation context via AWSLambdaPlatformInfo
- Adds a run-package fixture covering both dev and preview
@changeset-bot

changeset-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ac80bd8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@marko/run-adapter-aws-lambda Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 55 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 59835f62-6afa-4162-a6f7-64729eb6efb5

📥 Commits

Reviewing files that changed from the base of the PR and between 92d4d2e and ac80bd8.

📒 Files selected for processing (1)
  • packages/adapters/aws-lambda/README.md

Walkthrough

This PR adds a new @marko/run-adapter-aws-lambda package for AWS Lambda deployments via Function URLs or API Gateway HTTP API payload v2. It introduces shared Lambda/API Gateway types, a default handler that converts requests and serves static assets, an adapter entrypoint with preview support, esbuild build tooling, package metadata and docs, and a test fixture app with generated route types and runtime config.

Changes

Related issues: None specified
Related PRs: None specified
Suggested labels: enhancement, adapter, aws-lambda
Suggested reviewers: None specified

Sequence Diagram(s)

sequenceDiagram
  participant APIGateway
  participant handler
  participant Router
  participant serveStatic
  APIGateway->>handler: APIGatewayProxyEventV2
  handler->>handler: eventToRequest(event)
  handler->>Router: fetch(request)
  Router-->>handler: Response
  handler->>serveStatic: pathname (if 404)
  serveStatic-->>handler: file or null
  handler->>handler: responseToResult(response)
  handler-->>APIGateway: APIGatewayProxyResultV2
Loading
sequenceDiagram
  participant Client
  participant PreviewServer
  participant nodeRequestToEvent
  participant handler
  Client->>PreviewServer: HTTP request
  PreviewServer->>PreviewServer: readBody(req)
  PreviewServer->>nodeRequestToEvent: req, body
  nodeRequestToEvent-->>PreviewServer: APIGatewayProxyEventV2
  PreviewServer->>handler: handler(event, context)
  handler-->>PreviewServer: APIGatewayProxyResultV2
  PreviewServer-->>Client: HTTP response
Loading

Estimated code review effort: 4/5 (~200 minutes)

Poem:
Lambda paths now take their flight,
With preview, build, and static light.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding an AWS Lambda deployment adapter.
Description check ✅ Passed The description is directly related to the changeset and accurately describes the new adapter and fixture.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/aws-lambda-adapter

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (2)
packages/adapters/aws-lambda/scripts/build.ts (1)

7-13: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider aligning esbuild target with the supported Lambda runtime.

target: ["node14"] is older than the nodejs20.x runtime recommended in the README. Not a functional bug, but syntax lowering for node14 is unnecessary overhead for a package that will run on node18+.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/adapters/aws-lambda/scripts/build.ts` around lines 7 - 13, The
esbuild configuration in build() is still targeting node14, which is older than
the Lambda runtime this package supports. Update the BuildOptions target in the
aws-lambda build script to match the supported nodejs20.x/node18+ runtime
referenced by the adapter, so the bundle is not unnecessarily lowered for older
Node syntax.
packages/run/src/__tests__/fixtures/aws-lambda-adapter/package.json (1)

1-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing private: true and declared dependencies.

The fixture imports @marko/run/vite and @marko/run-adapter-aws-lambda in vite.config.ts, but neither is declared as a dependency here, and there's no "private": true. If this package is resolved solely via workspace hoisting it may still work in the monorepo, but omitting explicit dependencies risks the fixture accidentally being picked up by publish/release tooling and makes the dependency on the adapter implicit rather than declared.

♻️ Proposed fix
 {
   "name": "aws-lambda-adapter",
   "version": "1.0.0",
+  "private": true,
+  "dependencies": {
+    "`@marko/run`": "workspace:*",
+    "`@marko/run-adapter-aws-lambda`": "workspace:*",
+    "marko": "*",
+    "vite": "*"
+  },
   "scripts": {}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/run/src/__tests__/fixtures/aws-lambda-adapter/package.json` around
lines 1 - 5, The aws-lambda-adapter fixture package.json is missing explicit
dependency declarations and should be marked private to keep it out of
publish/release workflows. Update the fixture’s package metadata to add private
true and declare the imports used by vite.config.ts, specifically
`@marko/run/vite` and `@marko/run-adapter-aws-lambda`, so the fixture’s requirements
are explicit and self-contained.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/adapters/aws-lambda/package.json`:
- Around line 16-22: The package metadata is pointing consumers at source files
that are not included in the published tarball. Update the aws-lambda package’s
exports and types entries so they reference the built entrypoint produced in
dist rather than src/index.ts, keeping the published entrypoint aligned with
what files actually ships. Use the package.json exports and types fields to
locate the change and ensure adapter imports and type resolution work after
publish.

In `@packages/adapters/aws-lambda/README.md`:
- Around line 55-66: The AWS Lambda README example is using the wrong
route-handler parameter shape: `platform` is being destructured from the second
argument, which is actually `next`, not the request context. Update the example
in the `GET` handler to read Lambda data from `context.platform` instead,
keeping the `AWSLambdaPlatformInfo` cast and `lambdaContext` usage aligned with
the `GET(context, next)` signature.

In `@packages/adapters/aws-lambda/scripts/build.ts`:
- Around line 7-28: The package entrypoint configuration still points at src
instead of the published dist output, so update the build/package setup around
the BuildOptions entryPoints and the package export/type references to resolve
from dist. Make sure the aws-lambda package’s exports["."] and types point to
the built artifact produced by the build script, not src/index.ts, so the packed
package has a usable entrypoint.

In `@packages/adapters/aws-lambda/scripts/importMetaURL.js`:
- Around line 1-2: `__importMetaURL` is currently exported as a URL object from
the importMetaURL shim, but it must match `import.meta.url` and be a string in
the CJS build. Update the `__importMetaURL` export in `importMetaURL.js` to
return the string form from `pathToFileURL(__filename)` (use the standard
esbuild shim pattern), so downstream uses like `path.dirname(import.meta.url)`
in `index.ts` keep working.

In `@packages/adapters/aws-lambda/src/default-entry.ts`:
- Around line 143-145: The static fallback path in default-entry.ts can throw
when decodeURIComponent is called on a malformed pathname, so guard that
conversion before calling serveStatic. Update the request handling around the
URL pathname extraction and static fallback to catch invalid percent-encoding
and return the normal non-static response path instead of failing the
invocation; use the request.url, pathname, and serveStatic flow to locate the
fix.

In `@packages/adapters/aws-lambda/src/index.ts`:
- Around line 155-157: The cookie parsing in the Lambda adapter is too strict
because the logic in the request handling path only splits on "; " and misses
valid cookie separators without a trailing space. Update the cookie extraction
in the `index.ts` request flow so `req.headers.cookie` is parsed in a
whitespace-tolerant way, and ensure the resulting `cookies` value matches what
`platform.event.cookies` exposes for the same header format. Use the existing
cookie handling block around `req.headers.cookie.split(...)` as the place to
adjust this behavior.
- Around line 149-153: Update eventToRequest() in the AWS Lambda adapter so
preview events explicitly forward the request scheme instead of relying on the
default https fallback. Set x-forwarded-proto (or the equivalent forwarded
scheme header) for preview requests to http when building the headers map, while
preserving existing behavior for non-preview events, so absolute redirects and
scheme-sensitive logic use the correct protocol.

---

Nitpick comments:
In `@packages/adapters/aws-lambda/scripts/build.ts`:
- Around line 7-13: The esbuild configuration in build() is still targeting
node14, which is older than the Lambda runtime this package supports. Update the
BuildOptions target in the aws-lambda build script to match the supported
nodejs20.x/node18+ runtime referenced by the adapter, so the bundle is not
unnecessarily lowered for older Node syntax.

In `@packages/run/src/__tests__/fixtures/aws-lambda-adapter/package.json`:
- Around line 1-5: The aws-lambda-adapter fixture package.json is missing
explicit dependency declarations and should be marked private to keep it out of
publish/release workflows. Update the fixture’s package metadata to add private
true and declare the imports used by vite.config.ts, specifically
`@marko/run/vite` and `@marko/run-adapter-aws-lambda`, so the fixture’s requirements
are explicit and self-contained.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 65f817a3-8bb1-4eaa-be26-9c0f1c0ba08e

📥 Commits

Reviewing files that changed from the base of the PR and between 779776f and 5f22cb2.

⛔ Files ignored due to path filters (3)
  • package-lock.json is excluded by !**/package-lock.json and included by **
  • packages/run/src/__tests__/fixtures/aws-lambda-adapter/__snapshots__/dev.expected.md is excluded by !**/__snapshots__/** and included by **
  • packages/run/src/__tests__/fixtures/aws-lambda-adapter/__snapshots__/preview.expected.md is excluded by !**/__snapshots__/** and included by **
📒 Files selected for processing (18)
  • .changeset/aws-lambda-adapter.md
  • cspell.json
  • packages/adapters/aws-lambda/README.md
  • packages/adapters/aws-lambda/package.json
  • packages/adapters/aws-lambda/scripts/build.ts
  • packages/adapters/aws-lambda/scripts/importMetaURL.js
  • packages/adapters/aws-lambda/src/default-entry.ts
  • packages/adapters/aws-lambda/src/index.ts
  • packages/adapters/aws-lambda/src/types.ts
  • packages/adapters/aws-lambda/tsconfig.json
  • packages/run/src/__tests__/fixtures/aws-lambda-adapter/.gitignore
  • packages/run/src/__tests__/fixtures/aws-lambda-adapter/.marko-run/routes.d.ts
  • packages/run/src/__tests__/fixtures/aws-lambda-adapter/package.json
  • packages/run/src/__tests__/fixtures/aws-lambda-adapter/src/components/counter.marko
  • packages/run/src/__tests__/fixtures/aws-lambda-adapter/src/routes/+page.marko
  • packages/run/src/__tests__/fixtures/aws-lambda-adapter/test.config.ts
  • packages/run/src/__tests__/fixtures/aws-lambda-adapter/tsconfig.json
  • packages/run/src/__tests__/fixtures/aws-lambda-adapter/vite.config.ts

Comment thread packages/adapters/aws-lambda/package.json
Comment thread packages/adapters/aws-lambda/README.md
Comment thread packages/adapters/aws-lambda/scripts/build.ts
Comment thread packages/adapters/aws-lambda/scripts/importMetaURL.js Outdated
Comment thread packages/adapters/aws-lambda/src/default-entry.ts
Comment thread packages/adapters/aws-lambda/src/index.ts
Comment thread packages/adapters/aws-lambda/src/index.ts
rturnq added 2 commits July 1, 2026 04:55
- Add package.toggle.json so publish points exports/types at dist/
- Make the injected import.meta.url shim a string (.href) for the CJS build
- Fix the README platform example to read from context.platform
- Guard decodeURIComponent so a malformed path returns 400 instead of
  failing the invocation
- Force http scheme for preview events (the preview server is plain HTTP)
  so scheme-sensitive logic and absolute redirects are correct
- Parse the preview Cookie header tolerant of `;` without a trailing space
Clarify that Marko Run automatically uses an installed adapter with no
Vite config, and place the build + package + `aws lambda` deploy steps in
a Deploying section right after installation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant