or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-middy--core

The stylish Node.js middleware engine for AWS Lambda (core package)

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@middy/core@6.4.x

To install, run

npx @tessl/cli install tessl/npm-middy--core@6.4.0

index.mddocs/

Middy Core

Middy Core is the stylish Node.js middleware engine for AWS Lambda. It provides a fluent, plugin-based architecture that enables developers to create composable and reusable middleware patterns for serverless applications. The core package handles before, after, and error middleware execution, supports both traditional and streaming response patterns, includes built-in timeout handling, and provides a clean API for creating maintainable Lambda functions.

Package Information

  • Package Name: @middy/core
  • Package Type: npm
  • Language: JavaScript (ES Module)
  • Installation: npm install @middy/core
  • Node.js: >=20
  • Types: Full TypeScript support included

Core Imports

import middy from "@middy/core";

For CommonJS (also supported):

const middy = require("@middy/core");

Basic Usage

import middy from "@middy/core";

// Basic Lambda handler
const lambdaHandler = async (event, context) => {
  return { statusCode: 200, body: "Hello World" };
};

// Create middlewared handler
const handler = middy(lambdaHandler)
  .use(someMiddleware())
  .before(async (request) => {
    console.log("Before handler", request.event);
  })
  .after(async (request) => {
    console.log("After handler", request.response);
  })
  .onError(async (request) => {
    console.log("Error occurred", request.error);
  });

export { handler };

Architecture

Middy Core is built around several key components:

  • Factory Function: The main middy() function creates middlewared Lambda handlers
  • Middleware Chain: Before, after, and error middleware executed in sequence
  • Request Object: Shared state object passed through the middleware chain
  • Plugin System: Lifecycle hooks for extending functionality and performance monitoring
  • Streaming Support: Native AWS Lambda streaming response support
  • Timeout Handling: Early timeout detection with configurable responses
  • Abort Signals: Built-in AbortSignal support for cancellation

Capabilities

Factory Function

Creates a middlewared Lambda handler with fluent API for attaching middleware.

/**
 * Middy factory function. Use it to wrap your existing handler to enable middlewares on it.
 * @param handler - Your original AWS Lambda function or PluginObject
 * @param plugin - Plugin configuration for lifecycle hooks
 * @returns MiddyfiedHandler with middleware methods
 */
function middy<TEvent = unknown, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext, TInternal extends Record<string, unknown> = {}>(
  handler?: LambdaHandler<TEvent, TResult> | MiddlewareHandler<LambdaHandler<TEvent, TResult>, TContext, TResult, TEvent> | PluginObject,
  plugin?: PluginObject
): MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>;

Middlewared Handler Interface

The enhanced Lambda handler returned by middy() with middleware attachment methods. It extends both MiddyInputHandler and MiddyInputPromiseHandler to support both callback and Promise-based execution patterns.

interface MiddyfiedHandler<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext, TInternal extends Record<string, unknown> = {}> extends MiddyInputHandler<TEvent, TResult, TContext>, MiddyInputPromiseHandler<TEvent, TResult, TContext> {
  /** Attach middleware objects or arrays of middleware objects */
  use: (middlewares: MiddlewareObj | MiddlewareObj[]) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>;
  /** Attach before middleware function */
  before: (middleware: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>;
  /** Attach after middleware function */
  after: (middleware: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>;
  /** Attach error middleware function */
  onError: (middleware: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>;
  /** Replace the Lambda handler function */
  handler: <TInputHandlerEventProps = TEvent, TInputHandlerResultProps = TResult>(
    handler: MiddyInputHandler<TInputHandlerEventProps, TInputHandlerResultProps, TContext>
  ) => MiddyfiedHandler<TInputHandlerEventProps, TInputHandlerResultProps, TErr, TContext, TInternal>;
}

Request Object

The request object passed to all middleware functions containing event, context, response, and internal state.

interface Request<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext, TInternal extends Record<string, unknown> = {}> {
  /** The Lambda event object */
  event: TEvent;
  /** The Lambda context object */
  context: TContext;
  /** Handler response (null initially, set by handler or middleware) */
  response: TResult | null;
  /** Optional early response value for short-circuiting */
  earlyResponse?: TResult | null | undefined;
  /** Error object (null initially, set if error occurs) */
  error: TErr | null;
  /** Internal state object for sharing data between middleware */
  internal: TInternal;
}

Middleware Function

Function signature for individual middleware functions.

/**
 * Middleware function signature
 * @param request - The request object containing event, context, response, and internal state
 * @returns Any value (can be Promise)
 */
type MiddlewareFn<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext, TInternal extends Record<string, unknown> = {}> = (
  request: Request<TEvent, TResult, TErr, TContext, TInternal>
) => any;

Middleware Object

Object structure for defining middleware with before, after, and error hooks.

interface MiddlewareObj<TEvent = unknown, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext, TInternal extends Record<string, unknown> = {}> {
  /** Optional before middleware function */
  before?: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>;
  /** Optional after middleware function */
  after?: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>;
  /** Optional error middleware function */
  onError?: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>;
  /** Optional middleware name for debugging */
  name?: string;
}

Plugin Configuration

Configuration object for lifecycle hooks and advanced functionality.

interface PluginObject {
  /** Internal state object shared across middleware */
  internal?: any;
  /** Hook called before prefetch operations */
  beforePrefetch?: PluginHook;
  /** Hook called at request start */
  requestStart?: PluginHook;
  /** Hook called before each middleware */
  beforeMiddleware?: PluginHookWithMiddlewareName;
  /** Hook called after each middleware */
  afterMiddleware?: PluginHookWithMiddlewareName;
  /** Hook called before handler execution */
  beforeHandler?: PluginHook;
  /** Timeout in milliseconds before Lambda timeout for early response */
  timeoutEarlyInMillis?: number;
  /** Function to call when timeout occurs */
  timeoutEarlyResponse?: PluginHook;
  /** Hook called after handler execution */
  afterHandler?: PluginHook;
  /** Hook called at request end */
  requestEnd?: PluginHookPromise;
  /** Enable AWS Lambda streaming response support */
  streamifyResponse?: boolean;
}

Handler Object

Object passed to Lambda handlers containing abort signal for timeout handling.

interface MiddyHandlerObject {
  /**
   * An abort signal that will be canceled just before the lambda times out.
   * Use this to cancel long-running operations gracefully.
   */
  signal: AbortSignal;
}

Usage Examples

Multiple Middleware Objects

import middy from "@middy/core";

const middleware1 = {
  before: async (request) => {
    console.log("Middleware 1 before");
  },
  after: async (request) => {
    console.log("Middleware 1 after");
  },
  name: "middleware1"
};

const middleware2 = {
  before: async (request) => {
    console.log("Middleware 2 before");
  },
  onError: async (request) => {
    console.log("Middleware 2 error handler");
  },
  name: "middleware2"
};

const handler = middy(lambdaHandler)
  .use([middleware1, middleware2]);

Plugin Configuration

import middy from "@middy/core";

const pluginConfig = {
  timeoutEarlyInMillis: 1000, // Timeout 1 second before Lambda timeout
  timeoutEarlyResponse: () => {
    throw new Error("Function timed out");
  },
  beforeMiddleware: (name) => console.log(`Starting ${name}`),
  afterMiddleware: (name) => console.log(`Completed ${name}`),
  internal: { startTime: Date.now() }
};

const handler = middy(lambdaHandler, pluginConfig);

Handler Replacement

import middy from "@middy/core";

// Create handler without initial function
const handler = middy()
  .use(someMiddleware())
  .handler(async (event, context, { signal }) => {
    // Use abort signal for cancellation
    const controller = new AbortController();
    signal.addEventListener('abort', () => controller.abort());
    
    return await fetch('https://api.example.com', {
      signal: controller.signal
    });
  });

// Or replace an existing handler
const existingHandler = middy(lambdaHandler);
existingHandler.handler(async (event, context, { signal }) => {
  // Completely replace the original handler logic
  return { statusCode: 200, body: "New handler implementation" };
});

Streaming Response

import middy from "@middy/core";

const pluginConfig = {
  streamifyResponse: true
};

const handler = middy(async (event, responseStream, context) => {
  responseStream.write("Streaming data chunk 1\n");
  responseStream.write("Streaming data chunk 2\n");
  responseStream.end();
}, pluginConfig);

Early Response

import middy from "@middy/core";

const authMiddleware = {
  before: async (request) => {
    if (!request.event.headers.authorization) {
      // Short-circuit the execution chain
      request.earlyResponse = {
        statusCode: 401,
        body: JSON.stringify({ error: "Unauthorized" })
      };
      return;
    }
  }
};

const handler = middy(lambdaHandler)
  .use(authMiddleware);

Error Handling

import middy from "@middy/core";

const errorHandlerMiddleware = {
  onError: async (request) => {
    console.error("Error occurred:", request.error);
    
    // Modify response based on error
    request.response = {
      statusCode: 500,
      body: JSON.stringify({
        error: "Internal Server Error",
        requestId: request.context.awsRequestId
      })
    };
  }
};

const handler = middy(lambdaHandler)
  .use(errorHandlerMiddleware);

Constants

Package constants available for reference:

/** Default no-op Lambda handler */
declare const defaultLambdaHandler: () => void;

/** Default plugin configuration object */
declare const defaultPluginConfig: PluginObject;

/** Chunk size for string iteration in streaming responses */
declare const stringIteratorSize: number; // 16384

The defaultPluginConfig object contains these default values:

const defaultPluginConfig = {
  timeoutEarlyInMillis: 5,
  timeoutEarlyResponse: () => {
    const err = new Error("[AbortError]: The operation was aborted.", {
      cause: { package: "@middy/core" }
    });
    err.name = "TimeoutError";
    throw err;
  },
  streamifyResponse: false
};

Types

All TypeScript types and interfaces are exported in the middy namespace:

declare namespace middy {
  export type {
    Request,
    PluginHook,
    PluginHookWithMiddlewareName,
    PluginObject,
    MiddlewareFn,
    MiddlewareObj,
    MiddyfiedHandler,
  };
}

/** Plugin hook function signature */
type PluginHook = () => void;

/** Plugin hook function with middleware name parameter */
type PluginHookWithMiddlewareName = (middlewareName: string) => void;

/** Plugin hook function that can return a promise */
type PluginHookPromise = (request: Request) => Promise<unknown> | unknown;

/** Handler attachment function signature */
type AttachMiddlewareFn<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext, TInternal extends Record<string, unknown> = {}> = (
  middleware: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal>
) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>;

/** Middleware use function signature */
type UseFn<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext, TInternal extends Record<string, unknown> = {}> = <TMiddleware extends MiddlewareObj<any, any, Error, any, any>>(
  middlewares: TMiddleware | TMiddleware[]
) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal>;

/** Handler function with optional MiddyHandlerObject parameter */
type MiddyInputHandler<TEvent, TResult, TContext extends LambdaContext = LambdaContext> = (
  event: TEvent,
  context: TContext,
  opts: MiddyHandlerObject
) => undefined | Promise<TResult> | TResult;

/** Standard Promise-based handler function */
type MiddyInputPromiseHandler<TEvent, TResult, TContext extends LambdaContext = LambdaContext> = (
  event: TEvent,
  context: TContext
) => Promise<TResult>;

Key Features

  1. Fluent API: Chainable methods for attaching middleware
  2. Middleware Support: Before, after, and error middleware execution
  3. Plugin System: Lifecycle hooks for custom behaviors and monitoring
  4. Streaming Response: Native AWS Lambda streaming response support
  5. Timeout Handling: Early timeout detection with configurable responses
  6. Abort Signal: Built-in AbortSignal support for graceful cancellation
  7. Early Response: Middleware can short-circuit execution
  8. Type Safety: Full TypeScript support with generic types
  9. ES Module: Modern JavaScript module format with CommonJS compatibility
  10. Minimal Dependencies: Lightweight core with no external dependencies