{"id":17946,"library":"serverless-compose","title":"Serverless Compose (Lambda Middleware)","description":"serverless-compose is a lightweight, functional middleware framework specifically designed for AWS Lambda functions, enabling the creation of composable, \"onion-style\" middleware stacks. Currently at version 2.4.0, its release cadence is not explicitly stated in the provided documentation, but it appears actively maintained. A key differentiator is its zero-dependency philosophy, ensuring a minimal bundle size and avoiding dependency conflicts. Unlike more opinionated frameworks, serverless-compose focuses on providing a thin `compose` function and flexible patterns like `recoveryMiddleware` and `timingLogMiddleware` without enforcing specific architectural designs, allowing developers to define their middleware stack with explicit, functional wrappers around their Lambda handlers. It aims to prevent the \"fossilization\" of bad middleware habits by promoting a clear, functional approach for event processing.","status":"active","version":"2.4.0","language":"javascript","source_language":"en","source_url":"https://github.com/DavidJFelix/serverless-compose","tags":["javascript","typescript"],"install":[{"cmd":"npm install serverless-compose","lang":"bash","label":"npm"},{"cmd":"yarn add serverless-compose","lang":"bash","label":"yarn"},{"cmd":"pnpm add serverless-compose","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"The library primarily uses ES Modules (ESM). Using `require` will result in a runtime error in ESM-only environments.","wrong":"const { compose } = require('serverless-compose')","symbol":"compose","correct":"import { compose } from 'serverless-compose'"},{"note":"Prefer named ESM imports. Direct CommonJS `require` might fail if your Lambda environment is configured for ESM or if transpilation doesn't handle it correctly.","wrong":"const recoveryMiddleware = require('serverless-compose').recoveryMiddleware","symbol":"recoveryMiddleware","correct":"import { recoveryMiddleware } from 'serverless-compose'"},{"note":"All core utilities are named exports from the main package entry point, not default exports or sub-path imports.","wrong":"import timingLogMiddleware from 'serverless-compose/timingLogMiddleware'","symbol":"timingLogMiddleware","correct":"import { timingLogMiddleware } from 'serverless-compose'"}],"quickstart":{"code":"import { compose, recoveryMiddleware, timingLogMiddleware } from 'serverless-compose';\n\n// Suppose this is a client that fetches the weather from some external API\nclass WeatherClient {\n  static async askBadAPIForWeather() {\n    // Simulate a failing API call 50% of the time\n    if (Math.random() > 0.5) {\n      throw new Error('Weather API is down!');\n    }\n    return { temperature: 25, unit: 'C' };\n  }\n}\n\n// Your actual handler code\nasync function getWeather(request, context) {\n  const response = await WeatherClient.askBadAPIForWeather();\n  return {\n    statusCode: 200,\n    body: JSON.stringify(response),\n  };\n}\n\n// Your own custom logging function\nasync function logDuration(duration) {\n  console.log(`It took: ${duration}ms to return the weather`);\n}\n\n// Your own custom error handler\nasync function sendError(error) {\n  console.error('Handler Error:', error);\n  return {\n    statusCode: 500,\n    body: JSON.stringify({\n      error: `${error.message}`,\n      message: 'The darn weather API failed me again!',\n    }),\n  };\n}\n\nconst TimingMiddleware = timingLogMiddleware(logDuration);\nconst RecoveryMiddleware = recoveryMiddleware(sendError);\nconst MyMiddlewareStack = compose(\n  TimingMiddleware,\n  RecoveryMiddleware,\n);\n\nexport const lambdaHandler = MyMiddlewareStack(getWeather);\n","lang":"typescript","description":"Demonstrates basic setup with `compose`, `timingLogMiddleware` for logging duration, and `recoveryMiddleware` for graceful error handling in an AWS Lambda handler."},"warnings":[{"fix":"Carefully consider the execution flow. Middleware like `recoveryMiddleware` that needs to catch errors from subsequent middleware or the handler should typically be placed earlier in the `compose` argument list (further to the right), allowing it to wrap and protect the functions composed after it.","message":"Middleware order is crucial in `serverless-compose`'s onion-style architecture. Middleware functions are applied from right to left (outermost to innermost) in the `compose` function, but execute in an 'onion' fashion (outer wraps inner). Misordering can lead to unexpected behavior or missed error handling.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Ensure your project's `package.json` specifies `\"type\": \"module\"` for ESM, or use a build step (e.g., esbuild, webpack) to transpile your code to CJS if your environment strictly requires it. For hybrid setups, consider using dynamic `import()` within CJS modules to load ESM dependencies.","message":"When migrating older CommonJS (CJS) Lambda functions or integrating CJS dependencies, you might encounter issues with `serverless-compose` as it primarily uses ES Modules (ESM). AWS Lambda environments need proper configuration (`\"type\": \"module\"` in `package.json` or `.mjs` file extensions) to handle ESM correctly.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Always include and configure `recoveryMiddleware` (or similar custom error handling) as one of the outermost middleware in your `compose` stack. Ensure its error handling function returns a valid API Gateway proxy response object (e.g., `{ statusCode: 500, body: JSON.stringify({ message: 'Error' }) }`).","message":"Unhandled promise rejections or uncaught exceptions within your Lambda handler or custom middleware can lead to generic `502 Internal Server Error` responses from API Gateway without informative bodies. `recoveryMiddleware` is designed to mitigate this, but must be configured correctly.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Verify `handler` paths in `serverless.yml` are correct relative to the service's `package.json`. For monorepos, ensure your build process correctly copies or symlinks shared code, or configure bundling plugins to resolve modules from the root. Explicitly setting `srcDir` might be needed for some bundlers.","message":"While the library itself is functional, complex Serverless Framework deployments using shared source directories or monorepos can suffer from path resolution and bundling issues, especially when coupled with plugins like `serverless-esbuild` or `serverless-plugin-typescript`.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-23T00:00:00.000Z","next_check":"2026-07-22T00:00:00.000Z","problems":[{"fix":"Add `\"type\": \"module\"` to your `package.json`, or rename your file to `.mjs`, or ensure your build process (e.g., Babel, TypeScript compiler, bundler) transpiles ESM to CJS if your environment is CJS-only.","cause":"Your Node.js environment or AWS Lambda configuration is treating your code as CommonJS (CJS) but it uses ESM `import` statements.","error":"SyntaxError: Cannot use import statement outside a module"},{"fix":"Replace all `require()` calls with `import` statements for `serverless-compose` and other dependencies. If you need to load CJS modules from an ESM context, use `await import('your-cjs-module')`.","cause":"Your Node.js environment or AWS Lambda configuration is treating your code as an ES Module (ESM), but you are attempting to use the CommonJS `require` function.","error":"ReferenceError: require is not defined in ES module scope, you can use import instead"},{"fix":"Ensure `recoveryMiddleware` is included in your `compose` stack and its error handling function is correctly implemented to return a structured API Gateway proxy response object (e.g., `{ statusCode: 500, body: JSON.stringify({ message: 'Error' }) }`). Place `recoveryMiddleware` towards the 'right' in your `compose` arguments to wrap all preceding middleware and your handler.","cause":"Your Lambda handler or a middleware function threw an error or returned a rejected promise, and no subsequent middleware (like `recoveryMiddleware`) transformed it into a valid API Gateway response.","error":"502 Internal Server Error (from API Gateway) with errorMessage: \"Unhandled Promise Rejection\" in CloudWatch logs"}],"ecosystem":"npm","meta_description":null,"install_score":null,"install_tag":null,"quickstart_score":null,"quickstart_tag":null}