Skip to main content

Runtime Reference

This reference covers the full contract for Python and Node.js runtimes — event object shape, response format, init() configuration, CORS, body handling, and error handling.


Python Runtimes

E2E FaaS provides three Python runtime families. Choose based on your application requirements.

RuntimeBest For
FastAPIREST APIs, microservices, AI APIs, request validation, OpenAPI documentation
FlaskMigrating existing Flask apps, simple APIs, Flask extensions
HTTPLightweight webhooks, event processing, scheduled tasks, serverless utilities

Choosing a Python Runtime

FastAPI

Recommended for most new Python applications.

  • Available version: Python 3.11
  • Use when: building production APIs, needing request validation or type hints, requiring auto-generated OpenAPI docs, building AI APIs
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
return {"message": "Hello World"}

FastAPI manages routing, middleware, request validation, CORS, and OpenAPI generation. No platform-specific init() configuration is required.

Flask

Simple and flexible web framework.

  • Available version: Python 3.11
  • Use when: migrating existing Flask applications, building lightweight services, using Flask extensions
from flask import Flask

app = Flask(__name__)

@app.route("/")
def home():
return {"message": "Hello World"}

Flask manages routing, middleware, and request handling. No platform-specific init() configuration is required.

HTTP Runtime

Lightweight function-based model. No web framework required.

  • Available versions: Python 3.9, 3.10, 3.11, 3.12, 3.13, 3.14
  • Use when: building webhooks, processing events, running scheduled tasks, minimizing cold-start overhead
import json

async def handler(event, context):
return {
"statusCode": 200,
"body": json.dumps({"message": "Hello World"})
}

Python HTTP Runtime — Event Object

Every request is converted into an event dictionary.

FieldDescription
event["httpMethod"]HTTP method: "GET", "POST", etc.
event["path"]URL path, e.g. "/users/42"
event["headers"]All request headers as key-value pairs
event["queryStringParameters"]Query params as a dict, or None
event["body"]Request body as a string (UTF-8) or base64 string for binary. None if empty.
event["rawBody"]Raw bytes of the request body
event["isBase64Encoded"]True when body is base64 (binary uploads)
import json

async def handler(event, context):
body = json.loads(event["body"])
page = (event.get("queryStringParameters") or {}).get("page")
method = event["httpMethod"]
return {"statusCode": 200}

Python HTTP Runtime — Response Object

Every handler must return a dictionary. Only statusCode is required.

# JSON response
import json

return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"success": True})
}

# Redirect
return {
"statusCode": 302,
"headers": {"Location": "https://example.com"}
}

# Cookies
return {
"statusCode": 200,
"multiValueHeaders": {"Set-Cookie": ["session=abc; HttpOnly"]},
"body": json.dumps({"ok": True})
}

# No content
return {"statusCode": 204, "body": None}

Python HTTP Runtime — init()

init() is optional. It configures CORS and body handling at startup. If not defined, sensible defaults apply.

warning

init() must be synchronous. Do not use async def or perform I/O inside init().

def init():
return {
"cors": {
"origins": ["*"],
"credentials": False
},
"body": {
"json": True,
"urlencoded": False,
"content_types": [],
"max_bytes": 10 * 1024 * 1024 # 10MB default, 100MB max
}
}

Defaults when init() is not defined

SettingDefaultMeaning
cors.origins["*"]Any domain can call your function
cors.credentialsFalseCookies and auth headers not forwarded
body.jsonTrueJSON requests accepted
body.urlencodedFalseForm submissions rejected with 415
body.content_types[]No additional content types
body.max_bytes10 MBRequests with larger bodies get 413

Platform validation

The function will fail to start if:

  • init() is asynchronous (async def)
  • init() does not return a dictionary
  • cors.origins is invalid
  • credentials is True with wildcard origins
  • max_bytes exceeds 100 MB

CORS Configuration (Python)

# Allow any origin (default)
def init():
return {"cors": {"origins": ["*"]}}

# Restrict to specific domains
def init():
return {"cors": {"origins": ["https://app.example.com"]}}

# Enable cookies and Authorization headers
def init():
return {
"cors": {
"origins": ["https://app.example.com"],
"credentials": True # wildcard origins cannot be used here
}
}

Body Configuration (Python)

# Form submissions
def init():
return {"body": {"urlencoded": True}}

# File uploads (50MB limit)
import re
def init():
return {
"body": {
"content_types": [re.compile(r"^multipart/form-data")],
"max_bytes": 50 * 1024 * 1024
}
}

# XML API
def init():
return {
"body": {
"json": False,
"content_types": ["text/xml", "application/xml"]
}
}

# Images
import re
def init():
return {"body": {"content_types": [re.compile(r"^image/")]}}

# PDF
def init():
return {"body": {"content_types": ["application/pdf"]}}

For binary content (multipart, images, PDF), use event["rawBody"] (bytes) instead of event["body"].


Async vs Sync Handlers (Python)

Both handler types are supported.

# Async — recommended for database operations, HTTP requests, external APIs
async def handler(event, context):
pass

# Sync — suitable for lightweight processing, CPU-bound logic
def handler(event, context):
pass

Error Handling (Python)

import json

async def handler(event, context):
try:
body = json.loads(event["body"])
if not body.get("email"):
return {
"statusCode": 400,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"error": "email required"})
}
return {"statusCode": 200, "body": json.dumps({"ok": True})}
except Exception as err:
print(err)
return {
"statusCode": 500,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"error": "Internal Server Error"})
}

init() Quick Reference (Python)

Goalinit() config
Use defaultsDon't define init() — JSON + 10MB + open CORS
Lock CORScors: { "origins": ["https://app.com"] }
Enable cookiescors: { "origins": ["https://app.com"], "credentials": True }
Accept formsbody: { "urlencoded": True }
File uploadsbody: { "content_types": [re.compile(r"^multipart/form-data")], "max_bytes": 50 * 1024 * 1024 }
XML APIbody: { "json": False, "content_types": ["text/xml"] }
Imagesbody: { "content_types": [re.compile(r"^image/")] }
Strict size limitbody: { "max_bytes": 1024 * 1024 } — 413 on >1MB

Node.js Runtimes

Applies to Node.js HTTP runtimes (Node 20, 22, 24). All three runtime versions share the same event, response, and configuration interface — code written for one works on any without changes.

info

This section applies to Node.js HTTP runtimes (Node 20/22/24) which use ESM format (index.mjs, export const handler). For Express runtimes (Node 18/20), refer to the Express handler syntax.

The Event Object

Every incoming HTTP request is converted into an event object passed to your handler.

FieldTypeDescription
event.httpMethodstring"GET", "POST", "PUT", "DELETE", "PATCH", etc.
event.pathstringURL path, e.g. "/users/42"
event.headersobjectAll request headers as key-value pairs (lowercase keys)
event.multiValueHeadersobjectSame headers but each value is an array
event.queryStringParametersobject|nullQuery params as strings — ?a=1&a=2 gives a:"2" (last wins). null if no query.
event.multiValueQueryStringParametersobject|nullQuery params as arrays — ?a=1&a=2 gives a:["1","2"]. null if no query.
event.bodystring|nullRequest body as a string. JSON/text = UTF-8. Binary = base64-encoded. null if empty.
event.isBase64Encodedbooleantrue when body is base64 (binary uploads). false for JSON/text.
event.rawBodyBufferRaw bytes of the request body. Use this for multipart/file parsing.
event.requestContext.stagestring"prod", "staging", or "local"
event.requestContext.identity.sourceIpstringClient IP address
event.requestContext.identity.userAgentstringClient User-Agent header

Reading request data

export const handler = async (event) => {
const body = JSON.parse(event.body);
const page = event.queryStringParameters?.page ?? '1';
const match = event.path.match(/^\/users\/([a-z0-9]+)$/i);
const userId = match?.[1];
const ip = event.requestContext.identity.sourceIp;
};

The Response Object

Your handler must return a plain object. Only statusCode is required.

return {
statusCode: 200, // required — integer 100–599
headers: { // optional — single value per header
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
},
multiValueHeaders: { // optional — multiple values per header
'Set-Cookie': ['session=abc', 'theme=dark'],
},
body: JSON.stringify({ ok: true }), // string, object, or null
isBase64Encoded: false, // set true when sending binary files
};

Common response patterns

GoalReturn value
JSON response{ statusCode: 200, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ data }) }
Plain text{ statusCode: 200, headers: { 'Content-Type': 'text/plain' }, body: 'Hello' }
Send a file{ statusCode: 200, headers: { 'Content-Type': 'image/png', 'Content-Disposition': 'inline; filename="photo.png"' }, body: buffer.toString('base64'), isBase64Encoded: true }
Set cookies{ statusCode: 200, multiValueHeaders: { 'Set-Cookie': ['session=abc; HttpOnly', 'theme=dark; Max-Age=86400'] }, body: JSON.stringify({ ok: true }) }
Redirect{ statusCode: 302, headers: { Location: 'https://example.com' }, body: '' }
No content{ statusCode: 204, body: null }
Error{ statusCode: 400, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ error: 'email is required' }) }

init() — Configure Your Function

init() is optional. Export it to configure CORS and body handling at startup. If you don't export it, sensible defaults apply.

warning

init() must be synchronous. No async, no await, no database connections — configuration only.

export const init = () => ({
cors: {
origins: ['*'], // which domains can call your function from a browser
credentials: false, // allow cookies and Authorization headers
},
body: {
json: true, // accept application/json (default: true)
urlencoded: false, // accept application/x-www-form-urlencoded
contentTypes: [], // additional MIME types — strings or RegExp
maxBytes: 10 * 1024 * 1024, // max body size (default 10MB, max 100MB)
},
});

Defaults when init() is not exported

SettingDefaultMeaning
cors.origins["*"]Any domain can call your function
cors.credentialsfalseCookies and auth headers not forwarded
body.jsontrueJSON requests accepted
body.urlencodedfalseForm submissions rejected with 415
body.contentTypes[]No additional content types accepted
body.maxBytes10 MBRequests with larger bodies get 413

CORS Configuration (Node.js)

CORS controls which browser domains can call your function. It does not affect server-to-server calls (curl, Postman, backend services).

Allow any origin (default)

cors: { origins: ['*'] }

Restrict to specific domains

cors: {
origins: ['https://myapp.com', 'https://staging.myapp.com'],
}

Allow cookies and Authorization headers

cors: {
origins: ['https://myapp.com'], // must NOT be wildcard
credentials: true,
}
warning

credentials: true with origins: ["*"] is blocked by policy. You must list specific domains.

Dynamic origins from environment variable

cors: { origins: (process.env.CORS_ORIGINS || '*').split(',') }

Set CORS_ORIGINS=https://myapp.com,https://staging.myapp.com in Function Configuration.


Body Handling (Node.js)

By default only JSON is accepted. Any other content type is rejected with 415 Unsupported Media Type before your handler is called.

Supported content types

Content TypeMIME TypeHow to enable
JSONapplication/jsonbody.json: true (default)
Form submissionapplication/x-www-form-urlencodedbody.urlencoded: true
File uploadmultipart/form-datacontentTypes: [/^multipart\/form-data/]
XMLtext/xmlcontentTypes: ["text/xml"]
Plain texttext/plaincontentTypes: ["text/plain"]
Images (all)image/*contentTypes: [/^image\//]
PDFapplication/pdfcontentTypes: ["application/pdf"]
Binaryapplication/octet-streamcontentTypes: ["application/octet-stream"]

Examples

JSON + form submissions

export const init = () => ({
body: { json: true, urlencoded: true },
});

export const handler = async (event) => {
const form = Object.fromEntries(new URLSearchParams(event.body));
// form.email, form.name, etc.
};

File uploads

export const init = () => ({
body: {
contentTypes: [/^multipart\/form-data/],
maxBytes: 50 * 1024 * 1024, // 50MB
},
});

// Use event.rawBody (not event.body) for multipart parsing
import Busboy from 'busboy';
import { Readable } from 'stream';

export const handler = async (event) => {
const busboy = Busboy({ headers: event.headers });
const readable = new Readable();
readable.push(event.rawBody);
readable.push(null);
readable.pipe(busboy);
};

Images

export const init = () => ({
body: { contentTypes: [/^image\//], maxBytes: 5 * 1024 * 1024 },
});
// event.isBase64Encoded is true, event.rawBody has raw bytes

How body is encoded per content type

Content Typeevent.bodyevent.isBase64Encoded
application/jsonUTF-8 stringfalse
text/plain, text/xmlUTF-8 stringfalse
x-www-form-urlencodedUTF-8 stringfalse
multipart/form-database64 stringtrue
image/*, application/pdfbase64 stringtrue
No body (GET, DELETE)nullfalse
tip

For binary content (multipart, images, PDF), always use event.rawBody instead of event.body.


Error Handling (Node.js)

What the platform handles automatically

ConditionStatusHandler called?
Body exceeds maxBytes413No
Content-Type not in allowed list415No
Handler returns wrong shape500Yes — but response was invalid
Handler throws an error500Yes — error logged, generic message sent to client

Error messages from your handler are never sent to clients. Only generic messages are returned. Your errors are logged.

What you handle

export const handler = async (event) => {
try {
const body = JSON.parse(event.body);
if (!body.email) {
return { statusCode: 400, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ error: 'email required' }) };
}
return { statusCode: 200, body: JSON.stringify({ ok: true }) };
} catch (err) {
console.error(err);
return { statusCode: 500, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ error: 'Internal Server Error' }) };
}
};

init() Quick Reference (Node.js)

Goalinit() config
Use defaultsDon't export init() — JSON + 10MB + open CORS
Lock CORScors: { origins: ['https://app.com'] }
Enable cookiescors: { origins: ['https://app.com'], credentials: true }
Accept formsbody: { urlencoded: true }
File uploadsbody: { contentTypes: [/^multipart\/form-data/], maxBytes: 50 * 1024 * 1024 }
XML APIbody: { json: false, contentTypes: ['text/xml'] }
Imagesbody: { contentTypes: [/^image\//] }
Strict size limitbody: { maxBytes: 1024 * 1024 } — 413 on >1MB
Last updated on June 3, 2026.