HTTP Handler Migration Guide
Migrating HTTP step handlers to the new MotiaHttpArgs-based signature with { request, response } destructuring and SSE support.
This guide covers migrating HTTP step handlers from the old (req, ctx) signature to the new MotiaHttpArgs-based approach. It also introduces Server-Sent Events (SSE) support.
This guide focuses specifically on HTTP handler signature changes. For a complete migration from Motia v0.17.x to 1.0-RC, see the full migration guide.
What Changed
HTTP step handlers now receive a MotiaHttpArgs object as their first argument instead of a bare request object. This object contains both request and response, enabling streaming patterns like SSE alongside standard request/response flows.
| Aspect | Old | New |
|---|---|---|
| First arg (TS/JS) | req (request object directly) | { request, response } (MotiaHttpArgs) |
| First arg (Python) | req (dict-like object) | request: ApiRequest or args: MotiaHttpArgs |
| Body access (TS/JS) | req.body | request.body |
| Path params (TS/JS) | req.pathParams | request.pathParams |
| Headers (TS/JS) | req.headers | request.headers |
| Body access (Python) | req.get("body", {}) | request.body |
| Path params (Python) | req.get("pathParams", {}).get("id") | request.path_params.get("id") |
| Return type (Python) | {"status": 200, "body": {...}} | ApiResponse(status=200, body={...}) |
| Middleware placement | Config root: middleware: [...] | Inside trigger: { type: 'http', ..., middleware: [...] } |
| Middleware first arg | req | { request, response } |
TypeScript / JavaScript
Standard HTTP Handler
Key changes:
- Destructure
{ request }(or{ request, response }for SSE) from the first argument - Access
request.body,request.pathParams,request.queryParams,request.headers - Return value stays the same:
{ status, body, headers? }
Types
Multi-Trigger Steps
When using ctx.match(), the HTTP branch handler also receives MotiaHttpArgs:
Python
Standard HTTP Handler
Key changes:
- Import
ApiRequest,ApiResponse,FlowContextfrommotia - Use
http()helper for trigger definitions - Handler signature:
request: ApiRequest[Any]andctx: FlowContext[Any] - Access typed properties:
request.body,request.path_params,request.query_params,request.headers - Return
ApiResponse(status=..., body=...)instead of a plain dict
Python Types
Middleware
Placement Change
Middleware has moved from the config root into the HTTP trigger object.
Middleware Signature Change
Server-Sent Events (SSE)
SSE is enabled by the response object in MotiaHttpArgs. Instead of returning a response, you write directly to the stream.
SSE key points:
- Destructure both
requestandresponsefrom the first argument - Use
response.status()andresponse.headers()to configure the response - Write SSE-formatted data to
response.stream(TS/JS) orresponse.writer.stream(Python) - Call
response.close()when done streaming - Do not return a response object
Migration Checklist
TypeScript / JavaScript
- Change handler first argument from
(req, ctx)to({ request }, ctx)for all HTTP steps - Replace
req.bodywithrequest.body - Replace
req.pathParamswithrequest.pathParams - Replace
req.queryParamswithrequest.queryParams - Replace
req.headerswithrequest.headers - Move
middlewarearrays from config root into HTTP trigger objects - Update middleware functions: change
(req, ctx, next)to({ request }, ctx, next) - Update
ctx.match()HTTP handlers: change(request) =>to({ request }) =>
Python
- Add imports:
from motia import ApiRequest, ApiResponse, FlowContext, http - Use
http()helper in trigger definitions - Change handler signature to
handler(request: ApiRequest[Any], ctx: FlowContext[Any]) -> ApiResponse[Any] - Replace
req.get("body", {})withrequest.body - Replace
req.get("pathParams", {}).get("id")withrequest.path_params.get("id") - Replace
req.get("queryParams", {})withrequest.query_params - Replace
req.get("headers", {})withrequest.headers - Return
ApiResponse(status=..., body=...)instead of plain dicts - For SSE: use
MotiaHttpArgsinstead ofApiRequest