π¦«ποΈ dispatch to foreman
π§ task enqueued
ββ priority = ?
ββ yieldage = ?
ββ leverage = ?
title
feat: let forApiGateway return the handler's exact wire response (statusCode/headers/body, incl 204/308/xml)
description
.what
sdk-aws-lambda should let an api-gateway lambda return its exact wire response β
arbitrary statusCode, headers, and body (incl. an empty body) β rather than hardcode a
single success shape. today forApiGateway fixes { statusCode: 200, body: JSON.stringify(x) },
so a handler cannot express a 204 (no body), a 308 (Location redirect), or an xml/text body.
proposed: a response-passthrough mode (or a separate wrapper) where the logic fn returns
{ statusCode, headers?, body? } and the sdk passes it through verbatim, plus the same
error-conversion contract the prior simple-lambda-handlers middy stack gave:
BadRequestError -> 400 { errorMessage, errorType, causeMessage? } (client fault; the
invocation stays a success, so no cloudwatch error + no retry signal)
- any other error -> log loudly, return 500 with no body (no internal/secret leak)
.why
forApiGateway hardcodes { statusCode: 200, body: json }. real webhooks need other shapes:
- twilio voice/sms webhooks return 204 (no body) and twiml xml bodies
- cloudfront / short-url redirects return 308 with a
Location header
- neither can be expressed through the fixed 200+json contract
evidence β svc-notifications had to hand-roll a forApiGatewayPlain adapter to replace the
migrated-away createApiGatewayHandler, purely to pass statusCode / headers / body
through and to keep the middy error-conversion contract:
export const forApiGatewayPlain =
(logic: (event: APIGatewayProxyEvent) => Promise<PlainApiGatewayResponse>) =>
async (event): Promise<APIGatewayProxyResult> => {
try {
const response = await logic(event);
return { statusCode: response.statusCode, headers: response.headers, body: response.body ?? '' };
} catch (error) {
if (!(error instanceof Error)) throw error;
if (error instanceof BadRequestError)
return {
statusCode: 400,
body: JSON.stringify({
errorMessage: error.message,
errorType: 'BadRequestError',
...(error.cause instanceof Error ? { causeMessage: error.cause.message } : {}),
}),
};
log.error('handler.error', { errorMessage: error.message, stackTrace: error.stack });
return { statusCode: 500, body: '' };
}
};
every repo whose api-gateway lambdas need a non-200 / non-json wire response re-writes this same
adapter. a shipped sdk feature eliminates the per-repo duplication + keeps the error-conversion
contract consistent.
scope / caveat
- the prior middy stack also added owasp security headers + cors + v1/v2 event normalization;
svc-notifications omitted those deliberately (these webhooks configured no cors, and neither
twilio nor cloudfront depend on the security headers for the wire contract). the sdk feature
can offer those as opt-in, but the core ask is just: let the handler own the wire response.
refs
- svc-notifications branch
beav/feat-declapract-upgrade,
src/contract/handlers/utils/forApiGatewayPlain.ts (the hand-rolled adapter this would retire).
π¦«ποΈ dispatch to foreman
title
feat: let forApiGateway return the handler's exact wire response (statusCode/headers/body, incl 204/308/xml)
description
.what
sdk-aws-lambdashould let an api-gateway lambda return its exact wire response βarbitrary
statusCode,headers, andbody(incl. an empty body) β rather than hardcode asingle success shape. today
forApiGatewayfixes{ statusCode: 200, body: JSON.stringify(x) },so a handler cannot express a 204 (no body), a 308 (Location redirect), or an xml/text body.
proposed: a response-passthrough mode (or a separate wrapper) where the logic fn returns
{ statusCode, headers?, body? }and the sdk passes it through verbatim, plus the sameerror-conversion contract the prior
simple-lambda-handlersmiddy stack gave:BadRequestError-> 400{ errorMessage, errorType, causeMessage? }(client fault; theinvocation stays a success, so no cloudwatch error + no retry signal)
.why
forApiGatewayhardcodes{ statusCode: 200, body: json }. real webhooks need other shapes:Locationheaderevidence β svc-notifications had to hand-roll a
forApiGatewayPlainadapter to replace themigrated-away
createApiGatewayHandler, purely to passstatusCode/headers/bodythrough and to keep the middy error-conversion contract:
every repo whose api-gateway lambdas need a non-200 / non-json wire response re-writes this same
adapter. a shipped sdk feature eliminates the per-repo duplication + keeps the error-conversion
contract consistent.
scope / caveat
svc-notifications omitted those deliberately (these webhooks configured no cors, and neither
twilio nor cloudfront depend on the security headers for the wire contract). the sdk feature
can offer those as opt-in, but the core ask is just: let the handler own the wire response.
refs
beav/feat-declapract-upgrade,src/contract/handlers/utils/forApiGatewayPlain.ts(the hand-rolled adapter this would retire).