Migrate an AWS Lambda HTTP Handler to Raff Functions

Serdar Tekin
Serdar Tekin
Co-Founder & CTO
Updated Oct 24, 202616 min read
Written for: Developers and small teams migrating HTTP workloads from AWS Lambda Function URLs or API Gateway to a standard serverless runtime
AWS LambdaServerless MigrationRaff FunctionsHTTP APIs
Last reviewed:
Guide
Migrate an AWS Lambda HTTP Handler to Raff Functions
rafftechnologies.com/learn
On this page

Key Takeaways

Separate business logic from Lambda event/context adapters, map HTTP behavior explicitly, replace AWS-specific authentication and service dependencies, test endpoint equivalence, and keep rollback until production behavior is verified.

New to Raff? Get started in 60 seconds.

Migrating an AWS Lambda HTTP handler to Raff Functions is mostly a handler-shape and platform-services migration, not a complete application rewrite.

AWS Lambda HTTP workloads commonly receive a provider-specific event object from Lambda Function URLs or API Gateway and return a Lambda response envelope such as:

Json
{ "statusCode": 200, "headers": { "content-type": "application/json" }, "body": "{\"ok\":true}" }

Raff Functions takes a different approach: deploy a standard HTTP handler instead of adapting your application to a proprietary event format. Current Raff runtimes include Python FastAPI/ASGI, Node.js standard http, JavaScript Web Fetch, Go net/http, TypeScript, and a Dockerfile path for other runtimes.

For many HTTP Lambdas, the migration path is therefore:

Text
Lambda event/context handler → isolate business logic → expose standard HTTP handler → move configuration/secrets → replace AWS-specific service bindings → deploy to Raff Functions → verify behavior → cut traffic

This guide focuses on HTTP Lambda workloads. S3 events, EventBridge schedules, queues, IAM-heavy architectures, Step Functions, and other AWS services may need separate migration planning.

Port the handler to a standard HTTP interface, deploy it on Raff Functions, and cut traffic only after request/response behavior is verified.

Start by identifying how the Lambda is invoked

The first migration question is not the programming language.

It is:

What creates the Lambda event object?

Common HTTP paths are:

Lambda Function URL

AWS Lambda Function URLs expose a dedicated HTTPS endpoint and currently use the API Gateway payload format version 2.0 for request and response mapping.

The Lambda handler receives an event with fields such as:

Json
{ "version": "2.0", "rawPath": "/users/123", "rawQueryString": "expand=true", "headers": { "content-type": "application/json" }, "requestContext": { "http": { "method": "GET", "path": "/users/123" } } }

API Gateway HTTP API

API Gateway Lambda proxy integrations can use payload format 1.0 or 2.0.

That means two Lambda handlers that both look like "HTTP functions" may read request data differently.

Before migration, record:

  • Function URL vs API Gateway;
  • payload format version;
  • route/path handling;
  • query-string behavior;
  • cookie behavior;
  • CORS behavior;
  • authentication/authorizer dependencies;
  • binary/base64 response handling.

Do not port code until this input contract is understood.

The main code change is usually event-object removal

Consider a simple Node.js Lambda Function URL handler:

Js
export const handler = async (event) => { const name = event.queryStringParameters?.name || "world"; return { statusCode: 200, headers: { "content-type": "application/json" }, body: JSON.stringify({ message: `hello ${name}` }) }; };

The business logic is simple:

Text
read name → create JSON response

What is AWS-specific is:

  • event.queryStringParameters;
  • Lambda's statusCode/headers/body response envelope;
  • exported Lambda handler signature.

The migration becomes easier when that platform-specific layer is separated from business logic.

Refactor business logic before moving providers

A useful first step is:

Js
function createGreeting(name = "world") { return { message: `hello ${name}` }; }

The Lambda adapter becomes:

Js
export const handler = async (event) => { const result = createGreeting( event.queryStringParameters?.name ); return { statusCode: 200, headers: { "content-type": "application/json" }, body: JSON.stringify(result) }; };

Once the core logic no longer understands Lambda events, moving the HTTP adapter is much less risky.

This is the same portability principle covered in Portable Serverless Handlers: Avoiding Lambda Lock-In.

Node.js: Lambda handler to standard HTTP server

Raff Functions supports standard Node.js HTTP handlers.

The equivalent standard Node.js application can look like:

Js
import http from "node:http"; function createGreeting(name = "world") { return { message: `hello ${name}` }; } const server = http.createServer((req, res) => { const url = new URL( req.url, `http://${req.headers.host}` ); if (req.method !== "GET" || url.pathname !== "/") { res.writeHead(404, { "content-type": "application/json" }); return res.end( JSON.stringify({ error: "not found" }) ); } const result = createGreeting( url.searchParams.get("name") || "world" ); res.writeHead(200, { "content-type": "application/json" }); res.end(JSON.stringify(result)); }); server.listen( process.env.PORT || 3000, "0.0.0.0" );

The business logic did not change.

What changed was the provider adapter:

Text
Lambda event → Node request Lambda response envelope → normal HTTP response

This is the preferred migration shape: remove AWS-specific request plumbing while preserving application behavior.

Python: Lambda adapter to FastAPI / ASGI

A Python Lambda may start like:

Python
import json def handler(event, context): name = ( event.get("queryStringParameters") or {} ).get("name", "world") return { "statusCode": 200, "headers": { "content-type": "application/json" }, "body": json.dumps({ "message": f"hello {name}" }) }

Extract the business logic:

Python
def create_greeting(name="world"): return { "message": f"hello {name}" }

Then expose it through FastAPI:

Python
from fastapi import FastAPI app = FastAPI() @app.get("/") def root(name: str = "world"): return create_greeting(name)

Raff Functions supports FastAPI/ASGI, so the handler becomes a normal web application rather than a Lambda event adapter.

This also makes local testing easier because the same HTTP contract can run outside the provider.

Build a request-mapping checklist

Before removing the Lambda adapter, map every event field the function uses.

Common examples:

Lambda/API Gateway inputStandard HTTP equivalent
requestContext.http.methodHTTP method
rawPath / pathRequest path
queryStringParametersURL query parameters
headersRequest headers
cookiesCookie header / framework cookie API
bodyRequest body
isBase64EncodedBinary/body decoding responsibility
authorizer contextReplacement auth middleware / trusted headers

Do not assume API Gateway 1.0 and 2.0 behave identically.

AWS documents differences around cookies, multi-value headers, multi-value query strings, and path handling.

If your Lambda depends on these details, test them explicitly during migration.

Remove the Lambda response envelope

A common migration bug is leaving Lambda response JSON inside the new HTTP application.

For example, this is correct Lambda-style output:

Json
{ "statusCode": 201, "headers": { "content-type": "application/json" }, "body": "{\"id\":123}" }

But in a standard HTTP runtime, returning that whole object as JSON can accidentally produce:

Http
HTTP/1.1 200 OK { "statusCode": 201, "headers": {...}, "body": "{\"id\":123}" }

The correct standard HTTP behavior is:

Http
HTTP/1.1 201 Created Content-Type: application/json {"id":123}

During migration, convert:

  • Lambda statusCode → actual HTTP status;
  • Lambda headers → actual response headers;
  • Lambda body → actual response body;
  • Lambda cookies → framework/server cookie API.

CORS moves out of API Gateway or Function URL configuration

AWS Function URLs and API Gateway can apply CORS outside the application.

If the old deployment depends on provider-side CORS settings, moving only the function code may silently remove that behavior.

Inventory:

  • allowed origins;
  • allowed methods;
  • allowed headers;
  • credentials;
  • preflight behavior;
  • exposed headers.

On Raff, implement CORS through the application/framework or another verified edge layer.

Do not use Access-Control-Allow-Origin: * with credentials.

For broader API security, use Serverless API Security: Authentication, CORS & Rate Limiting.

Authentication is often the hardest migration dependency

Some HTTP Lambdas rely on:

  • API Gateway Lambda authorizers;
  • Cognito;
  • IAM/SigV4;
  • JWT authorizers;
  • custom headers inserted by upstream infrastructure.

Those are not part of the function's business code, but they are part of the production request path.

Before cutover, answer:

  • Who authenticates the caller today?
  • Where is the identity verified?
  • What claims reach the Lambda?
  • What authorization decision is made inside the function?
  • What replaces the AWS-specific identity layer?

Do not make the new Raff Function public while assuming the old API Gateway authorizer still protects it.

If the function should be public only to a webhook provider, use signature verification rather than relying on URL secrecy.

Environment variables migrate easily—AWS service credentials may not

Simple configuration typically maps directly:

Text
AWS Lambda environment variable → Raff Function environment variable / secret

Examples:

  • APP_ENV;
  • feature flags;
  • upstream API base URL;
  • application secret.

AWS-integrated credentials require more work.

A Lambda often accesses S3, DynamoDB, SQS, Secrets Manager, or another AWS service through its execution role.

That means there may be no explicit credential in the application configuration.

When migrating, inventory every AWS SDK call.

For each dependency, choose one of three paths:

  1. keep calling the AWS service from Raff;
  2. migrate the dependency to a Raff or third-party equivalent;
  3. abstract the dependency behind an application adapter.

Do not discover after cutover that the function depended on an IAM execution role that no longer exists.

Object storage dependency: S3 to Raff Object Storage

If the HTTP Lambda reads or writes S3 objects, Raff Object Storage is S3-compatible.

That can reduce migration work, but endpoint, bucket, region, and credential configuration still need review.

A useful application boundary is:

Text
objectStore.get(key) objectStore.put(key, body)

rather than scattering AWS SDK calls throughout business logic.

Raff Functions supports scoped per-function Object Storage bindings, allowing a function to receive limited bucket credentials without embedding primary credentials in source code.

Database dependency: replace IAM assumptions explicitly

Lambda database access may use:

  • static credentials;
  • AWS Secrets Manager;
  • RDS Proxy;
  • IAM database authentication;
  • VPC networking.

A Raff Function can bind to supported Raff Managed Databases with scoped per-function credentials.

But this is not automatically equivalent to RDS Proxy or IAM database authentication.

For each database dependency, re-evaluate:

  • connection limits;
  • pooling;
  • TLS;
  • secrets;
  • private/public connectivity;
  • retry behavior;
  • transaction semantics.

Do not treat "the SQL is the same" as a complete database migration.

Logging: CloudWatch-specific assumptions need removal

A Lambda may rely on CloudWatch Logs implicitly through console.log or Python logging.

Raff Functions also exposes logs and metrics, but you should remove code that assumes CloudWatch log-group names, AWS request IDs, or CloudWatch-specific query/alert integrations unless you intentionally keep those external systems.

Preserve useful application-level fields such as:

  • request ID;
  • user/account ID where appropriate;
  • operation name;
  • duration;
  • error type;
  • business event ID.

Avoid logging secrets or authorization headers.

Replace Lambda context dependencies

Lambda handlers can use the context object for:

  • request ID;
  • remaining execution time;
  • function metadata.

Search the old code for:

Text
context. awsRequestId getRemainingTimeInMillis functionName functionVersion

Decide whether each value is:

  • business-critical;
  • observability-only;
  • removable;
  • replaceable with application-generated metadata.

A portable application should not require an AWS request ID to function correctly.

Timeouts should be re-evaluated, not copied

AWS Lambda currently has a hard maximum execution duration of 15 minutes.

Raff Functions supports much longer runs: up to one hour by default and up to 24 hours on request.

That does not mean every migrated HTTP function should receive a one-hour timeout.

For normal APIs:

  • keep request handling bounded;
  • move long jobs to background/event workflows;
  • define client timeouts;
  • avoid holding HTTP connections unnecessarily.

Longer platform limits are a capability, not a target.

Use Long-Running Serverless Functions for AI, ETL, and Batch Jobs for workloads that genuinely need longer execution.

Function URL migration should preserve endpoint behavior

If the AWS workload uses a Lambda Function URL, record:

  • path;
  • query parameters;
  • method behavior;
  • CORS;
  • authentication mode;
  • custom domain/proxy in front;
  • response codes;
  • headers;
  • cookies;
  • redirects.

Raff Functions provides an HTTP live URL with automatic TLS.

Before DNS or client cutover, run the same request suite against both endpoints.

Example test matrix:

TestAWS endpointRaff endpoint
GET /200200
GET /?name=batuexpected JSONsame JSON
invalid path404404
malformed JSON400400
auth missing401/403equivalent
OPTIONS/CORSexpected headersequivalent

The responses do not need byte-for-byte identical infrastructure headers.

They do need equivalent application behavior.

API Gateway migrations need a route inventory

API Gateway may provide routing that the Lambda itself never sees as configuration.

Inventory every route:

Text
GET /users/{id} POST /users DELETE /users/{id}

Then decide whether Raff should run:

  • one function handling all routes;
  • separate functions;
  • a standard web framework/router.

For most small HTTP APIs, moving routing into FastAPI, Node, Go, or another standard framework improves portability.

Do not create one function per route simply because API Gateway originally did.

Keep traffic cutover reversible

A safe migration should be staged.

Phase 1 — Refactor

Extract business logic from Lambda event/context assumptions while still running on AWS.

Phase 2 — Deploy Raff Function

Deploy the standard HTTP handler. For the Raff-side setup and live HTTP/log verification steps, use Deploy an HTTP Function on Raff. This migration guide remains responsible for adapting Lambda event and API Gateway behavior, not for repeating the deployment tutorial.

Phase 3 — Shadow or test

Send representative requests to Raff and compare behavior.

Phase 4 — Move noncritical traffic

Switch a test client, staging hostname, or limited integration.

Phase 5 — Production cutover

Update DNS, client configuration, webhook destination, or API endpoint.

Phase 6 — Observe

Watch:

  • error rate;
  • latency;
  • logs;
  • downstream database/storage calls;
  • authentication failures;
  • unexpected retries.

Phase 7 — Retain rollback

Keep the AWS Lambda available until the agreed rollback window closes.

Do not delete AWS infrastructure immediately after the first successful Raff request.

Pricing migration should use workload measurements

AWS Lambda and Raff Functions meter different dimensions.

AWS Lambda pricing can include:

  • requests;
  • compute duration/memory;
  • architecture/runtime-dependent compute;
  • data transfer and adjacent AWS services.

Raff Functions currently meters:

  • memory GB-seconds;
  • active CPU vCPU-seconds.

Requests and function egress are currently free, and a spend cap is enabled by default.

Use actual workload data:

  • invocations;
  • memory;
  • CPU time;
  • average and p95 duration;
  • egress;
  • concurrency.

Do not compare only headline per-request prices.

For the provider-level comparison, use AWS Lambda Alternative 2026: Pricing, Performance & Features.

HTTP Lambda migration checklist

Before cutover:

  • invocation path is known;
  • API Gateway payload format is known;
  • every used event field is mapped;
  • business logic is isolated from Lambda adapter code;
  • Lambda response envelope is removed;
  • CORS behavior is reproduced;
  • authentication/authorizer behavior is replaced;
  • environment variables are migrated;
  • AWS SDK dependencies are inventoried;
  • IAM execution-role assumptions are replaced;
  • storage/database dependencies are verified;
  • logging is provider-neutral;
  • timeout behavior is intentional;
  • HTTP test suite passes against Raff;
  • rollback endpoint remains available;
  • production cutover is observable.

Frequently asked questions

Can I run an AWS Lambda handler unchanged on Raff Functions?

Usually not if it depends on the Lambda event and context objects. Raff Functions uses standard HTTP/runtime handlers, so provider-specific request/response adapters should be removed.

Do I need to rewrite the business logic?

Often no. If business logic is separated from Lambda event parsing, most of the change can be limited to the HTTP adapter and AWS-specific service integrations.

What happens to the Lambda statusCode/body response object?

Replace it with a real HTTP status, headers, and response body using the standard server or framework API.

Can Raff Functions replace Lambda Function URLs?

For normal HTTP workloads, Raff Functions provides a live HTTPS URL. You still need to migrate CORS, authentication, routing, headers, and any AWS-specific behavior explicitly.

Can I keep using AWS services from Raff Functions?

Yes if the service exposes an API reachable from the function and you configure appropriate credentials/network access. Whether you should keep or migrate each dependency is a separate architecture decision.

Does Raff Functions support long-running code?

Yes. Raff currently supports up to one hour by default and up to 24 hours on request. Normal HTTP endpoints should still remain bounded.

How should I migrate API Gateway authorizers?

Treat authentication as a separate dependency. Replace the authorizer with application middleware, another identity layer, signed requests, JWT verification, or another design that matches the workload.

Sources

Was this article helpful?
Serdar Tekin
Serdar Tekin
Co-Founder & CTO

Co-Founder & CTO at Raff, focused on cloud infrastructure, systems architecture, security, reliability, and production operations.

Put it into practice

Spin up a Raff server, follow the steps above on real infrastructure, and you are live. Linux or Windows, backed by a 14-day money-back guarantee.

Frequently asked questions

Can I run an AWS Lambda handler unchanged on Raff Functions?
Usually not if it depends on Lambda event and context objects. Raff Functions uses standard HTTP/runtime handlers, so provider-specific adapters should be removed.
Do I need to rewrite the business logic?
Often no. When business logic is isolated from Lambda event parsing, most changes are limited to the HTTP adapter and AWS-specific service integrations.
What happens to the Lambda statusCode/body response object?
Replace it with a real HTTP status, headers, and response body through the standard server or framework API.
Can Raff Functions replace Lambda Function URLs?
For normal HTTP workloads Raff Functions provides a live HTTPS URL, but CORS, authentication, routing, headers, and AWS-specific behavior must still be migrated explicitly.
Can I keep using AWS services from Raff Functions?
Yes when the service API is reachable and credentials/network access are configured. Each dependency should still be evaluated separately.
Does Raff Functions support long-running code?
Yes. Raff currently supports up to one hour by default and up to 24 hours on request, though normal HTTP endpoints should remain bounded.
How should I migrate API Gateway authorizers?
Treat authentication as a separate dependency and replace the authorizer with application middleware or another identity design suited to the workload.