UNPKG

@node-idempotency/express

Version:

Express middleware to provide Race-Condition free idempotency for HTTP requests, preventing unintended duplicate operations.

101 lines 3.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.idempotencyAsMiddleware = void 0; const shared_1 = require("@node-idempotency/shared"); const core_1 = require("@node-idempotency/core"); const getIdempotencyInstance = async (options) => { const storageAdapter = await (0, shared_1.buildStorageAdapter)(options.storage); return new core_1.Idempotency(storageAdapter, options); }; const setHeaders = (response, headers) => { Object.keys(headers).forEach((key) => { if (headers[key]) { void response.header(key, headers[key]); } }); }; const handleResponse = async (idempotencyReq, nodeIdempotency, response, payload) => { const { statusCode } = response; const headers = response.getHeaders(); const additional = { statusCode }; Object.values(shared_1.headers2Cache).forEach((header) => { const head = headers[header] ?? headers[header.toLowerCase()]; if (head) { additional[header] = head; } }); try { const isJson = (headers["content-type"] ?? headers["Content-Type"]) ?.toString() ?.toLowerCase() ?.includes("application/json"); if (isJson && typeof payload === "string") { payload = JSON.parse(payload); } } catch { } const res = { additional, ...(statusCode < 400 ? { body: payload } : { error: payload }), }; await nodeIdempotency.onResponse(idempotencyReq, res); }; const successHandler = (nodeIdempotency) => { return async (request, response, next) => { const idempotencyReq = { headers: request.headers, body: request.body, path: request.url, method: request.method, }; try { const idempotencyResponse = await nodeIdempotency.onRequest(idempotencyReq); if (!idempotencyResponse) { const originalSend = response.send.bind(response); response.send = function (body) { void handleResponse(idempotencyReq, nodeIdempotency, response, body); return originalSend(body); }; next(); return; } if (idempotencyResponse.additional?.statusCode) { const { statusCode } = idempotencyResponse.additional; void response.status(statusCode); } const headers = Object.values(shared_1.headers2Cache).reduce((res, cur) => { if (idempotencyResponse?.additional?.[cur]) { res[cur] = idempotencyResponse.additional[cur]; } return res; }, {}); setHeaders(response, { ...headers, [shared_1.HTTPHeaderEnum.idempotentReplayed]: "true", }); if (idempotencyResponse.body) { response.send(idempotencyResponse.body); } else { next(idempotencyResponse.error); } } catch (err) { if (err instanceof core_1.IdempotencyError) { const status = shared_1.idempotency2HttpCodeMap[err.code] || 500; if (err.code === core_1.IdempotencyErrorCodes.REQUEST_IN_PROGRESS) { setHeaders(response, { [shared_1.HTTPHeaderEnum.retryAfter]: "1" }); } void response.status(status); next(err); } } }; }; const idempotencyAsMiddleware = async (options) => { const idempotency = await getIdempotencyInstance(options); return [successHandler(idempotency)]; }; exports.idempotencyAsMiddleware = idempotencyAsMiddleware; //# sourceMappingURL=middleware.js.map