Created
November 17, 2020 04:24
-
-
Save gpoitch/128c7676649a0061de48159b2cb28aa3 to your computer and use it in GitHub Desktop.
Node API Gateway Middleware
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Convert a nodejs http request into an AWS API Gateway 'event' object. | |
* Or apply middleware to automatically convert requests/responses. | |
* Useful for testing your API Gateway lambda handler locally. | |
*/ | |
import { IncomingMessage, IncomingHttpHeaders, ServerResponse } from 'http' | |
import { URL, URLSearchParams } from 'url' | |
import { APIGatewayProxyHandler, APIGatewayProxyResult } from 'aws-lambda' | |
const noop = () => {} | |
const MockApiGatewayContext = { | |
callbackWaitsForEmptyEventLoop: false, | |
functionName: '', | |
functionVersion: '', | |
invokedFunctionArn: '', | |
awsRequestId: '', | |
logGroupName: '', | |
memoryLimitInMB: '', | |
logStreamName: '', | |
getRemainingTimeInMillis: () => 100, | |
done: noop, | |
fail: noop, | |
succeed: noop | |
} | |
function normalizeIncomingHeaders(inParams: IncomingHttpHeaders) { | |
return Object.keys(inParams).reduce((params: Record<string, string>, key) => { | |
const value = inParams[key] | |
params[key] = (Array.isArray(value) ? value[0] : value) || '' | |
return params | |
}, {}) | |
} | |
function normalizeOutgoingHeaders(outParams: APIGatewayProxyResult['headers']) { | |
return Object.keys(outParams || {}).reduce((params: Record<string, string>, key) => { | |
params[key] = '' + outParams?.[key] | |
return params | |
}, {}) | |
} | |
function normalizeIncomingQueryParams(params: URLSearchParams) { | |
return [...params].reduce((obj, [key, val]) => ({ ...obj, [key]: val }), {}) | |
} | |
export function nodeRequestToApiGatewayEvent(request: IncomingMessage) { | |
const parsedUrl = new URL(request.url || '', 'x://x.x') | |
const httpMethod = request.method || 'GET' | |
const path = parsedUrl.pathname || '/' | |
return { | |
httpMethod, | |
path, | |
headers: normalizeIncomingHeaders(request.headers), | |
queryStringParameters: normalizeIncomingQueryParams(parsedUrl.searchParams), | |
multiValueQueryStringParameters: null, | |
multiValueHeaders: {}, | |
resource: '$default', | |
body: null, | |
pathParameters: null, | |
stageVariables: null, | |
isBase64Encoded: false, | |
requestContext: { | |
path, | |
httpMethod, | |
protocol: request.httpVersion, | |
accountId: '', | |
apiId: '', | |
domainName: '', | |
domainPrefix: '', | |
extendedRequestId: '', | |
authorizer: null, | |
requestId: '', | |
requestTime: '', | |
requestTimeEpoch: Date.now(), | |
resourceId: '$default', | |
resourcePath: '$default', | |
stage: '$default', | |
identity: { | |
accessKey: null, | |
accountId: null, | |
apiKey: null, | |
apiKeyId: null, | |
apiId: null, | |
caller: null, | |
cognitoAmr: null, | |
cognitoAuthenticationProvider: null, | |
cognitoAuthenticationType: null, | |
cognitoIdentityId: null, | |
cognitoIdentityPoolId: null, | |
principalOrgId: null, | |
sourceIp: '', | |
user: null, | |
userAgent: 'Mozilla/5.0', | |
userArn: null | |
} | |
} | |
} | |
} | |
export function applyApiGatewayResultToNodeResponse(result: APIGatewayProxyResult, response: ServerResponse) { | |
const responseHeaders = normalizeOutgoingHeaders({ ...result.headers, 'content-type': 'application/json' }) | |
response.writeHead(result.statusCode, responseHeaders) | |
response.end(result.body) | |
} | |
export function apiGatewayMiddleware(handler: APIGatewayProxyHandler) { | |
return async function ( | |
request: IncomingMessage, | |
response: ServerResponse, | |
next?: (request: IncomingMessage, response: ServerResponse) => void | |
) { | |
const result = (await handler( | |
nodeRequestToApiGatewayEvent(request), | |
MockApiGatewayContext, | |
noop | |
)) as APIGatewayProxyResult | |
applyApiGatewayResultToNodeResponse(result, response) | |
next?.(request, response) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment