@scloud/lambda-api
Version:
Lambda handler for API Gateway proxy requests
111 lines • 13.3 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.apiHandler = apiHandler;
const types_1 = require("./types");
const helpers_1 = require("./helpers");
const v4_1 = __importDefault(require("zod/v4"));
function apiErrorResponse(e) {
// Intentional API error response
if (e instanceof types_1.ApiError) {
return {
statusCode: e.statusCode,
body: e.body,
};
}
// Unhandled error
if (e)
console.error(e instanceof Error ? e.stack : e);
return {
statusCode: 500,
body: 'Internal server error',
};
}
/**
* API route handler
*/
async function apiHandler(event, context, routes, errorHandler = undefined, catchAll = undefined) {
const request = (0, helpers_1.parseRequest)(event);
let response;
try {
const match = (0, helpers_1.matchRoute)(routes, request.path);
if (match.params)
request.pathParameters = match.params;
if (match.methods) {
const route = match.methods[request.method];
if (!route)
throw new types_1.ApiError(405, 'Method not allowed');
// Verify request body
if (route.request?.body) {
const parsed = route.request.body.safeParse(request.body);
if (!parsed.success) {
throw new types_1.ApiError(400, v4_1.default.treeifyError(parsed.error));
}
request.body = parsed.data;
}
response = await route.handler(request);
// Verify response body
if (route.response?.body) {
const parsed = route.response.body.safeParse(response.body);
if (!parsed.success) {
console.error('Invalid response body:', request.method, request.path, JSON.stringify(v4_1.default.treeifyError(parsed.error), null, 2));
response = undefined; // Remove the response so it can be replaced by the error handler
throw new types_1.ApiError(500, 'Internal server error');
}
response.body = parsed.data;
}
}
else if (catchAll) {
// Catch-all / 404
response = await catchAll.handler(request);
}
else {
throw new types_1.ApiError(404, 'Not found');
}
}
catch (e) {
if (errorHandler) {
try {
// errorHandler can optionally return undefined to request standard error handling:
response = await errorHandler(request, e);
}
catch (ee) {
response = apiErrorResponse(ee);
}
}
// Standard error handling
response = response ?? apiErrorResponse(e);
}
// Translate the response to an API Gateway Proxy result
let body;
const headers = response.headers || {};
if (typeof response.body === 'string') {
// Use the body as-is
// Add text/plain if no Content-Type header is set:
if (!(0, helpers_1.getHeader)('Content-Type', headers))
(0, helpers_1.setHeader)('Content-Type', 'text/plain', headers);
body = response.body;
}
else if (response.body) {
// Stringify the response object
// API Gateway returns application/json by default
body = JSON.stringify(response.body);
}
// Prepare response
const result = {
statusCode: response.statusCode ?? 200,
headers,
body: body || '',
};
// Add cookie headers
const cookieHeaders = (0, helpers_1.buildCookie)(response);
if (cookieHeaders) {
result.multiValueHeaders = {
'Set-Cookie': cookieHeaders,
};
}
return result;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9oYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBZ0NBLGdDQXlGQztBQXBIRCxtQ0FHaUI7QUFDakIsdUNBQXdGO0FBQ3hGLGdEQUF1QjtBQUV2QixTQUFTLGdCQUFnQixDQUFDLENBQVc7SUFDbkMsaUNBQWlDO0lBQ2pDLElBQUksQ0FBQyxZQUFZLGdCQUFRLEVBQUUsQ0FBQztRQUMxQixPQUFPO1lBQ0wsVUFBVSxFQUFFLENBQUMsQ0FBQyxVQUFVO1lBQ3hCLElBQUksRUFBRSxDQUFDLENBQUMsSUFBSTtTQUNiLENBQUM7SUFDSixDQUFDO0lBRUQsa0JBQWtCO0lBQ2xCLElBQUksQ0FBQztRQUFFLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDdkQsT0FBTztRQUNMLFVBQVUsRUFBRSxHQUFHO1FBQ2YsSUFBSSxFQUFFLHVCQUF1QjtLQUM5QixDQUFDO0FBQ0osQ0FBQztBQUVEOztHQUVHO0FBQ0ksS0FBSyxVQUFVLFVBQVUsQ0FDOUIsS0FBMkIsRUFDM0IsT0FBZ0IsRUFDaEIsTUFBYyxFQUNkLGVBQTRGLFNBQVMsRUFDckcsV0FBZ0MsU0FBUztJQUV6QyxNQUFNLE9BQU8sR0FBRyxJQUFBLHNCQUFZLEVBQUMsS0FBSyxDQUFDLENBQUM7SUFFcEMsSUFBSSxRQUE4QixDQUFDO0lBQ25DLElBQUksQ0FBQztRQUNILE1BQU0sS0FBSyxHQUFHLElBQUEsb0JBQVUsRUFBQyxNQUFNLEVBQUUsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQy9DLElBQUksS0FBSyxDQUFDLE1BQU07WUFBRSxPQUFPLENBQUMsY0FBYyxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUM7UUFFeEQsSUFBSSxLQUFLLENBQUMsT0FBTyxFQUFFLENB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QUEsbUJBQVMsRUFBQyxjQUFjLEVBQUUsWUFBWSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQzFGLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDO0lBQ3ZCLENBQUM7U0FBTSxJQUFJLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUN6QixnQ0FBZ0M7UUFDaEMsa0RBQWtEO1FBQ2xELElBQUksR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN2QyxDQUFDO0lBRUQsbUJBQW1CO0lBQ25CLE1BQU0sTUFBTSxHQUEwQjtRQUNwQyxVQUFVLEVBQUUsUUFBUSxDQUFDLFVBQVUsSUFBSSxHQUFHO1FBQ3RDLE9BQU87UUFDUCxJQUFJLEVBQUUsSUFBSSxJQUFJLEVBQUU7S0FDakIsQ0FBQztJQUVGLHFCQUFxQjtJQUNyQixNQUFNLGFBQWEsR0FBRyxJQUFBLHFCQUFXLEVBQUMsUUFBUSxDQUFDLENBQUM7SUFDNUMsSUFBSSxhQUFhLEVBQUUsQ0FBQztRQUNsQixNQUFNLENBQUMsaUJBQWlCLEdBQUc7WUFDekIsWUFBWSxFQUFFLGFBQWE7U0FDNUIsQ0FBQztJQUNKLENBQUM7SUFFRCxPQUFPLE1BQU0sQ0FBQztBQUNoQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQVBJR2F0ZXdheVByb3h5RXZlbnQsXG4gIEFQSUdhdGV3YXlQcm94eVJlc3VsdCxcbiAgQ29udGV4dCxcbn0gZnJvbSAnYXdzLWxhbWJkYSc7XG5pbXBvcnQge1xuICBSZXF1ZXN0LCBSZXNwb25zZSwgSGFuZGxlciwgUm91dGUsIFJvdXRlcyxcbiAgQXBpRXJyb3IsXG59IGZyb20gJy4vdHlwZXMnO1xuaW1wb3J0IHsgYnVpbGRDb29raWUsIGdldEhlYWRlciwgbWF0Y2hSb3V0ZSwgcGFyc2VSZXF1ZXN0LCBzZXRIZWFkZXIgfSBmcm9tICcuL2hlbHBlcnMnO1xuaW1wb3J0IHogZnJvbSAnem9kL3Y0JztcblxuZnVuY3Rpb24gYXBpRXJyb3JSZXNwb25zZShlPzogdW5rbm93bik6IFJlc3BvbnNlIHtcbiAgLy8gSW50ZW50aW9uYWwgQVBJIGVycm9yIHJlc3BvbnNlXG4gIGlmIChlIGluc3RhbmNlb2YgQXBpRXJyb3IpIHtcbiAgICByZXR1cm4ge1xuICAgICAgc3RhdHVzQ29kZTogZS5zdGF0dXNDb2RlLFxuICAgICAgYm9keTogZS5ib2R5LFxuICAgIH07XG4gIH1cblxuICAvLyBVbmhhbmRsZWQgZXJyb3JcbiAgaWYgKGUpIGNvbnNvbGUuZXJyb3IoZSBpbnN0YW5jZW9mIEVycm9yID8gZS5zdGFjayA6IGUpO1xuICByZXR1cm4ge1xuICAgIHN0YXR1c0NvZGU6IDUwMCxcbiAgICBib2R5OiAnSW50ZXJuYWwgc2VydmVyIGVycm9yJyxcbiAgfTtcbn1cblxuLyoqXG4gKiBBUEkgcm91dGUgaGFuZGxlclxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gYXBpSGFuZGxlcihcbiAgZXZlbnQ6IEFQSUdhdGV3YXlQcm94eUV2ZW50LFxuICBjb250ZXh0OiBDb250ZXh0LFxuICByb3V0ZXM6IFJvdXRlcyxcbiAgZXJyb3JIYW5kbGVyOiAoKHJlcXVlc3Q6IFJlcXVlc3QsIGU6IEVycm9yKSA9PiBQcm9taXNlPFJlc3BvbnNlIHwgdW5kZWZpbmVkPikgfCB1bmRlZmluZWQgPSB1bmRlZmluZWQsXG4gIGNhdGNoQWxsOiBIYW5kbGVyIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkLFxuKTogUHJvbWlzZTxBUElHYXRld2F5UHJveHlSZXN1bHQ+IHtcbiAgY29uc3QgcmVxdWVzdCA9IHBhcnNlUmVxdWVzdChldmVudCk7XG5cbiAgbGV0IHJlc3BvbnNlOiBSZXNwb25zZSB8IHVuZGVmaW5lZDtcbiAgdHJ5IHtcbiAgICBjb25zdCBtYXRjaCA9IG1hdGNoUm91dGUocm91dGVzLCByZXF1ZXN0LnBhdGgpO1xuICAgIGlmIChtYXRjaC5wYXJhbXMpIHJlcXVlc3QucGF0aFBhcmFtZXRlcnMgPSBtYXRjaC5wYXJhbXM7XG5cbiAgICBpZiAobWF0Y2gubWV0aG9kcykge1xuICAgICAgY29uc3Qgcm91dGUgPSBtYXRjaC5tZXRob2RzW3JlcXVlc3QubWV0aG9kIGFzIGtleW9mIFJvdXRlXTtcbiAgICAgIGlmICghcm91dGUpIHRocm93IG5ldyBBcGlFcnJvcig0MDUsICdNZXRob2Qgbm90IGFsbG93ZWQnKTtcblxuICAgICAgLy8gVmVyaWZ5IHJlcXVlc3QgYm9keVxuICAgICAgaWYgKHJvdXRlLnJlcXVlc3Q/LmJvZHkpIHtcbiAgICAgICAgY29uc3QgcGFyc2VkID0gcm91dGUucmVxdWVzdC5ib2R5LnNhZmVQYXJzZShyZXF1ZXN0LmJvZHkpO1xuICAgICAgICBpZiAoIXBhcnNlZC5zdWNjZXNzKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEFwaUVycm9yKDQwMCwgei50cmVlaWZ5RXJyb3IocGFyc2VkLmVycm9yKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmVxdWVzdC5ib2R5ID0gcGFyc2VkLmRhdGE7XG4gICAgICB9XG5cbiAgICAgIHJlc3BvbnNlID0gYXdhaXQgcm91dGUuaGFuZGxlcihyZXF1ZXN0KTtcblxuICAgICAgLy8gVmVyaWZ5IHJlc3BvbnNlIGJvZHlcbiAgICAgIGlmIChyb3V0ZS5yZXNwb25zZT8uYm9keSkge1xuICAgICAgICBjb25zdCBwYXJzZWQgPSByb3V0ZS5yZXNwb25zZS5ib2R5LnNhZmVQYXJzZShyZXNwb25zZS5ib2R5KTtcbiAgICAgICAgaWYgKCFwYXJzZWQuc3VjY2Vzcykge1xuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ0ludmFsaWQgcmVzcG9uc2UgYm9keTonLCByZXF1ZXN0Lm1ldGhvZCwgcmVxdWVzdC5wYXRoLCBKU09OLnN0cmluZ2lmeSh6LnRyZWVpZnlFcnJvcihwYXJzZWQuZXJyb3IpLCBudWxsLCAyKSk7XG4gICAgICAgICAgcmVzcG9uc2UgPSB1bmRlZmluZWQ7IC8vIFJlbW92ZSB0aGUgcmVzcG9uc2Ugc28gaXQgY2FuIGJlIHJlcGxhY2VkIGJ5IHRoZSBlcnJvciBoYW5kbGVyXG4gICAgICAgICAgdGhyb3cgbmV3IEFwaUVycm9yKDUwMCwgJ0ludGVybmFsIHNlcnZlciBlcnJvcicpO1xuICAgICAgICB9XG4gICAgICAgIHJlc3BvbnNlLmJvZHkgPSBwYXJzZWQuZGF0YTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKGNhdGNoQWxsKSB7XG4gICAgICAvLyBDYXRjaC1hbGwgLyA0MDRcbiAgICAgIHJlc3BvbnNlID0gYXdhaXQgY2F0Y2hBbGwuaGFuZGxlcihyZXF1ZXN0KTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEFwaUVycm9yKDQwNCwgJ05vdCBmb3VuZCcpO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIGlmIChlcnJvckhhbmRsZXIpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIC8vIGVycm9ySGFuZGxlciBjYW4gb3B0aW9uYWxseSByZXR1cm4gdW5kZWZpbmVkIHRvIHJlcXVlc3Qgc3RhbmRhcmQgZXJyb3IgaGFuZGxpbmc6XG4gICAgICAgIHJlc3BvbnNlID0gYXdhaXQgZXJyb3JIYW5kbGVyKHJlcXVlc3QsIGUgYXMgRXJyb3IpO1xuICAgICAgfSBjYXRjaCAoZWUpIHtcbiAgICAgICAgcmVzcG9uc2UgPSBhcGlFcnJvclJlc3BvbnNlKGVlKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBTdGFuZGFyZCBlcnJvciBoYW5kbGluZ1xuICAgIHJlc3BvbnNlID0gcmVzcG9uc2UgPz8gYXBpRXJyb3JSZXNwb25zZShlKTtcbiAgfVxuXG4gIC8vIFRyYW5zbGF0ZSB0aGUgcmVzcG9uc2UgdG8gYW4gQVBJIEdhdGV3YXkgUHJveHkgcmVzdWx0XG4gIGxldCBib2R5OiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIGNvbnN0IGhlYWRlcnMgPSByZXNwb25zZS5oZWFkZXJzIHx8IHt9O1xuICBpZiAodHlwZW9mIHJlc3BvbnNlLmJvZHkgPT09ICdzdHJpbmcnKSB7XG4gICAgLy8gVXNlIHRoZSBib2R5IGFzLWlzXG4gICAgLy8gQWRkIHRleHQvcGxhaW4gaWYgbm8gQ29udGVudC1UeXBlIGhlYWRlciBpcyBzZXQ6XG4gICAgaWYgKCFnZXRIZWFkZXIoJ0NvbnRlbnQtVHlwZScsIGhlYWRlcnMpKSBzZXRIZWFkZXIoJ0NvbnRlbnQtVHlwZScsICd0ZXh0L3BsYWluJywgaGVhZGVycyk7XG4gICAgYm9keSA9IHJlc3BvbnNlLmJvZHk7XG4gIH0gZWxzZSBpZiAocmVzcG9uc2UuYm9keSkge1xuICAgIC8vIFN0cmluZ2lmeSB0aGUgcmVzcG9uc2Ugb2JqZWN0XG4gICAgLy8gQVBJIEdhdGV3YXkgcmV0dXJucyBhcHBsaWNhdGlvbi9qc29uIGJ5IGRlZmF1bHRcbiAgICBib2R5ID0gSlNPTi5zdHJpbmdpZnkocmVzcG9uc2UuYm9keSk7XG4gIH1cblxuICAvLyBQcmVwYXJlIHJlc3BvbnNlXG4gIGNvbnN0IHJlc3VsdDogQVBJR2F0ZXdheVByb3h5UmVzdWx0ID0ge1xuICAgIHN0YXR1c0NvZGU6IHJlc3BvbnNlLnN0YXR1c0NvZGUgPz8gMjAwLFxuICAgIGhlYWRlcnMsXG4gICAgYm9keTogYm9keSB8fCAnJyxcbiAgfTtcblxuICAvLyBBZGQgY29va2llIGhlYWRlcnNcbiAgY29uc3QgY29va2llSGVhZGVycyA9IGJ1aWxkQ29va2llKHJlc3BvbnNlKTtcbiAgaWYgKGNvb2tpZUhlYWRlcnMpIHtcbiAgICByZXN1bHQubXVsdGlWYWx1ZUhlYWRlcnMgPSB7XG4gICAgICAnU2V0LUNvb2tpZSc6IGNvb2tpZUhlYWRlcnMsXG4gICAgfTtcbiAgfVxuXG4gIHJldHVybiByZXN1bHQ7XG59XG4iXX0=
;