How to Add Custom Middleware to AWS SDK v3 Clients to Intercept Requests

You can intercept every AWS SDK v3 request by calling client.middlewareStack.add() with a custom function that transforms arguments or responses before they reach the network layer.

The AWS SDK for JavaScript v3 provides a centralized middleware architecture that allows you to intercept, modify, and monitor requests across all API calls. By leveraging the middlewareStack property available on every client instance, you can inject custom logic without modifying individual command invocations. The agent-toolkit-for-aws repository documents this pattern extensively, providing production-ready examples for logging, header injection, and request transformation.

How the Middleware Stack Works in AWS SDK v3

The SDK processes every command through a fixed pipeline of five discrete steps. Each step represents a phase in the request lifecycle, allowing you to hook into specific points depending on your interception needs.

The execution order is:

  1. initialize – Resolves configuration values like region and credentials
  2. serialize – Converts command input into HTTP request format
  3. build – Applies user-defined transformations (logging, custom headers)
  4. finalizeRequest – Handles signing, retries, and endpoint resolution
  5. deserialize – Parses the HTTP response back into typed output

A custom middleware is a higher-order function receiving next and context parameters, returning an async function that processes args. The context object contains metadata such as commandName, while args carries the request payload. Calling await next(args) passes control to the next middleware in the chain.

Middleware Configuration Options

When adding middleware via client.middlewareStack.add(), you must specify:

  • name: A unique identifier for the middleware
  • step: The pipeline phase ("initialize", "serialize", "build", "finalizeRequest", or "deserialize")
  • override: Boolean flag to replace existing middleware with the same name (default: false)

Adding Custom Middleware to an AWS SDK v3 Client

Insert your middleware after constructing the client but before the first send() call. The stack becomes immutable once a request dispatches, so configure all middleware during client initialization.

import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";

// Create client outside handlers for Lambda container reuse
const client = new S3Client({ region: "us-east-1" });

// Add logging middleware to intercept all requests
client.middlewareStack.add(
  (next, context) => async (args) => {
    console.log("[Middleware] Command:", context.commandName);
    console.log("[Middleware] Input:", args.input);
    
    const result = await next(args);
    
    console.log("[Middleware] Result:", result);
    return result;
  },
  {
    name: "RequestLogger",
    step: "build",
    override: false,
  }
);

// Middleware runs automatically for every subsequent command
const output = await client.send(
  new GetObjectCommand({ Bucket: "my-bucket", Key: "example.txt" })
);

This example, found in skills/core-skills/aws-sdk-js-v3-usage/SKILL.md, demonstrates the "build" step which executes after serialization but before request signing.

Advanced Middleware Use Cases for AWS SDK v3

Different interception points serve different operational needs:

  • Request modification ("build" step): Inject headers like correlation IDs or authentication tokens by modifying args.request.headers
  • Payload transformation ("serialize" step): Encrypt or compress args.input before it becomes the HTTP body
  • Throttling ("initialize" step): Implement rate limiting by inspecting context and rejecting calls early
  • Tracing integration ("finalizeRequest" step): Add OpenTelemetry trace identifiers immediately before signing
  • Global error handling ("deserialize" step): Capture SDK errors and emit metrics after response parsing

According to skills/core-skills/aws-sdk-js-v3-usage/references/clients.md, the "finalizeRequest" step is ideal for operations that must occur after the SDK has resolved endpoints and calculated signatures but before network transmission.

Performance Considerations When Adding Middleware

The AWS SDK v3 enables cacheMiddleware: true by default, which caches the resolved middleware stack per client and command combination. When adding custom middleware, maintain this caching behavior to avoid rebuilding the stack on every invocation.

Critical constraints:

  • Immutable after dispatch: You cannot modify middlewareStack after the first send() call completes
  • Startup cost: Add middleware during cold start, not inside Lambda handlers
  • Memory: Long-lived clients with large middleware chains retain memory between invocations

Reference skills/core-skills/aws-sdk-js-v3-usage/references/performance.md for detailed guidance on optimizing middleware overhead in serverless environments.

Key Source Files in agent-toolkit-for-aws

The repository provides authoritative documentation for SDK v3 middleware patterns:

Summary

  • AWS SDK v3 clients expose a middlewareStack property that accepts custom interception functions
  • Five pipeline steps (initialize, serialize, build, finalizeRequest, deserialize) determine when your middleware executes
  • The "build" step is optimal for most request modifications like logging and header injection
  • Configure middleware immediately after client construction and before the first send() call, as the stack becomes immutable after dispatch
  • Enable caching (cacheMiddleware: true) to prevent performance degradation from stack reconstruction

Frequently Asked Questions

Can I add middleware to an existing AWS SDK v3 client after it has sent requests?

No. The middlewareStack becomes immutable once the client dispatches its first request. You must add all middleware immediately after constructing the client instance and before calling any send() methods. This design ensures deterministic request processing and enables internal caching optimizations.

Which middleware step should I use to add custom HTTP headers?

Use the "build" step. This phase executes after the SDK serializes the command input into an HTTP request object but before the "finalizeRequest" step applies signing and endpoint resolution. At this point, you can safely modify args.request.headers to inject correlation IDs, authentication tokens, or custom metadata.

How does custom middleware affect AWS SDK v3 performance?

When properly configured with cacheMiddleware: true (the default), custom middleware adds negligible overhead after the initial cold start. The SDK caches the resolved middleware chain per client/command combination. However, adding middleware inside Lambda handlers or disabling caching forces stack reconstruction on every invocation, significantly impacting latency.

Where can I find production-ready examples of AWS SDK v3 middleware?

The agent-toolkit-for-aws repository maintains canonical examples in skills/core-skills/aws-sdk-js-v3-usage/SKILL.md and references/clients.md. These files demonstrate logging middleware, header injection, and performance configurations tested against current SDK versions.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →