TypeScript SDK middlewares for v2 client

Elevate, May 20-22-2025, Miami Beach, Florida

Add functionality to the request object in the TypeScript SDK.

The TypeScript SDK v2 client was deprecated on 30 October 2024.

You can add middleware when creating the TypeScript SDK client. You can add multiple middlewares by using a chain of middleware builder methods.

const client = new ClientBuilder()
  .withClientCredentialsFlow(authMiddlewareOptions)
  .withHttpMiddleware(httpMiddlewareOptions)
  .withLoggerMiddleware()
  // Chain additional middleware here
  .build();

HttpMiddleware

Handles sending the HTTP request to the Composable Commerce API.

type HttpMiddlewareOptions = {
  host: string;
  credentialsMode?: 'omit' | 'same-origin' | 'include';
  includeHeaders?: boolean;
  includeResponseHeaders?: boolean;
  includeOriginalRequest?: boolean;
  includeRequestInErrorResponse?: boolean;
  maskSensitiveHeaderData?: boolean;
  timeout?: number;
  enableRetry?: boolean;
  retryConfig?: {
    maxRetries?: number;
    retryDelay?: number;
    backoff?: boolean;
    maxDelay?: number;
    retryOnAbort?: boolean;
    retryCodes?: Array<number | string>;
  };
  fetch?: any;
  abortController?: AbortController; // deprecated
  getAbortController?: () => AbortController;
};

const options: HttpMiddlewareOptions = {
  host: 'https://api.europe-west1.gcp.commercetools.com',
  includeResponseHeaders: true,
  maskSensitiveHeaderData: true,
  includeOriginalRequest: false,
  includeRequestInErrorResponse: false,
  enableRetry: true,
  retryConfig: {
    maxRetries: 3,
    retryDelay: 200,
    backoff: false,
    retryCodes: [503],
  },
  fetch: fetchFn,
};

const client = new ClientBuilder()
  .withHttpMiddleware(options)
  // ...
  .build();

AuthMiddleware

Handles generating, authenticating, and refreshing auth tokens used when making authenticated requests to the Composable Commerce API.

The SDK manages the auth token lifecycle, and there is usually no need to interact or access the authentication token. Expired tokens are automatically discarded and new tokens are generated (or refreshed if using withRefreshTokenFlow) for new requests.
You can use any one of the following authentication flows. The main difference in the authentication flows is the options parameter, which you can pass to each middleware.

withClientCredentialsFlow

Handles authentication for the client credentials flow of the Composable Commerce API.
type AuthMiddlewareOptions = {
  host: string
  projectKey: string
  credentials: Credentials
  scopes?: Array<string>
  oauthUri?: string
  fetch?: any
  tokenCache?: TokenCache
}

const options: AuthMiddlewareOptions {
  host: 'https://auth.europe-west1.gcp.commercetools.com',
  projectKey: 'test-project-key',
  credentials: {
    clientId: process.env.CTP_CLIENT_ID,
    clientSecret: process.env.CTP_CLIENT_SECRET
  },
  scopes: [`manage_project:${projectKey}`],
  fetch: fetchFn
}


const client = new ClientBuilder()
  .withClientCredentialsFlow(options)
  // ...
  .build()

withPasswordFlow

Handles authentication for the password flow of the Composable Commerce API.
type PasswordAuthMiddlewareOptions = {
  host: string;
  projectKey: string;
  credentials: {
    clientId: string;
    clientSecret: string;
    user: {
      username: string;
      password: string;
    };
  };
  scopes?: Array<string>;
  tokenCache?: TokenCache;
  oauthUri?: string;
  fetch?: any;
};

const options: PasswordAuthMiddlewareOptions = {
  host: 'https://auth.europe-west1.gcp.commercetools.com',
  projectKey: 'test-project-key',
  credentials: {
    clientId: process.env.CTP_CLIENT_ID,
    clientSecret: process.env.CTP_CLIENT_SECRET,
    user: {
      username: process.env.USERNAME,
      password: process.env.PASSWORD,
    },
  },
  scopes: [`manage_project:${projectKey}`],
  fetch: fetchFn,
};

const client = new ClientBuilder()
  .withPasswordFlow(options)
  // ...
  .build();

withAnonymousSessionFlow

Handles authentication for the anonymous session flow of the Composable Commerce API.
type AnonymousAuthMiddlewareOptions = {
  host: string;
  projectKey: string;
  credentials: {
    clientId: string;
    clientSecret: string;
    anonymousId?: string;
  };
  scopes?: Array<string>;
  oauthUri?: string;
  fetch?: any;
  tokenCache?: TokenCache;
};

const options: AnonymousAuthMiddlewareOptions = {
  host: 'https://auth.europe-west1.gcp.commercetools.com',
  projectKey: 'test-project-key',
  credentials: {
    clientId: process.env.CTP_CLIENT_ID,
    clientSecret: process.env.CTP_CLIENT_SECRET,
    anonymousId: process.env.CTP_ANONYMOUS_ID, // a unique id
  },
  scopes: [`manage_project:${projectKey}`],
  fetch: fetchFn,
};

const client = new ClientBuilder()
  .withAnonymousSessionFlow(options)
  // ...
  .build();

withRefreshTokenFlow

Handles authentication for the refresh token flow of the Composable Commerce API.
type RefreshAuthMiddlewareOptions = {
  host: string;
  projectKey: string;
  credentials: {
    clientId: string;
    clientSecret: string;
  };
  refreshToken: string;
  tokenCache?: TokenCache;
  oauthUri?: string;
  fetch?: any;
};

const options: RefreshAuthMiddlewareOptions = {
  host: 'https://auth.europe-west1.gcp.commercetools.com',
  projectKey: 'test-project-key',
  credentials: {
    clientId: process.env.CTP_CLIENT_ID,
    clientSecret: process.env.CTP_CLIENT_SECRET,
  },
  refreshToken: 'bXvTyxc5yuebdvwTwyXn==',
  tokenCache: TokenCache,
  scopes: [`manage_project:${projectKey}`],
  fetch: fetchFn,
};

const client = new ClientBuilder()
  .withRefreshTokenFlow(options)
  // ...
  .build();

withExistingTokenFlow

Attaches an access token Authorization header.

type ExistingTokenMiddlewareOptions = {
  force?: boolean;
};

const authorization: string = 'Bearer G8GLDqrUMYzaOjhdFGfK1HRIOAtj7qQy';
const options: ExistingTokenMiddlewareOptions = {
  force: true,
};

const client = new ClientBuilder()
  .withExistingTokenFlow(authorization, options)
  // ...
  .build();

CorrelationIdMiddleware

type CorrelationIdMiddlewareOptions = {
  generate: () => string;
};

const options: CorrelationIdMiddlewareOptions = {
  generate: () => 'cd260fc9-c575-4ba3-8789-cc4c9980ee4e', // Replace with your own UUID or a generator function
};

const client = new ClientBuilder()
  .withCorrelationIdMiddleware(options)
  // ...
  .build();

UserAgentMiddleware

UserAgentMiddleware adds a customizable User-Agent header to every request. By default it adds the SDK (and its version) and the running process (and its version) to the request. For example:
'User-Agent': 'commercetools-sdk-javascript-v2/2.1.4 node.js/18.13.0'
type HttpUserAgentOptions = {
  name?: string;
  version?: string;
  libraryName?: string;
  libraryVersion?: string;
  contactUrl?: string;
  contactEmail?: string;
  customAgent?: string;
};

const options: HttpUserAgentOptions = {
  name: 'test-client-agent',
  version: '2.9.0',
};

const client = new ClientBuilder()
  .withUserAgentMiddleware(options)
  // ...
  .build();

QueueMiddleware

Use QueueMiddleware to reduce concurrent HTTP requests.

type QueueMiddlewareOptions = {
  concurrency: number;
};

const options: QueueMiddlewareOptions = {
  concurrency: 20,
};

const client = new ClientBuilder()
  .withQueueMiddleware(options)
  // ...
  .build();

withTelemetryMiddleware

Allows integrating analytics and monitoring services (such as New Relic or Dynatrace) into your TypeScript SDK applications. For more information, see Observability.
// Required import
import {
  createTelemetryMiddleware,
  TelemetryMiddlewareOptions,
} from '@commercetools/ts-sdk-apm';

const telemetryOptions: TelemetryMiddlewareOptions = {
  createTelemetryMiddleware,
  apm: () => typeof require('newrelic'), // installed npm `newrelic` package
  tracer: () => typeof require('/absolute-path-to-a-tracer-module'),
};

const client = new ClientBuilder()
  .withTelemetryMiddleware(telemetryOptions)
  // ...
  .build();

LoggerMiddleware

Logs incoming requests and response objects. You can add an optional options parameter, which accepts a custom logger function, and another optional parameter to be used within the custom logger function.
function logger(options?: LoggerMiddlewareOptions) {
  return (next: Next): Next => {
    return (req: MiddlewareRequest, res: MiddlewareResponse) => {
      console.log(options); // Logs all included options except the logger function output -> { level: 'debug', name: 'custom-logger-fn' }
      console.log(req, res);
      next(req, res);
    };
  };
}

const client: Client = new ClientBuilder()
  .withProjectKey('projectKey')
  .withLoggerMiddleware({
    level: 'debug',
    name: 'custom-logger-fn',
    logger,
  })
  // ...
  .build();

BeforeExecutionMiddleware

This middleware runs before the SDK calls the Composable Commerce API, and is useful for preprocessing requests. This middleware has access to the request and response object as well as the options included in .withBeforeExecutionMiddleware().
import {
  type Next,
  type Client,
  type MiddlewareRequest,
  type BeforeExecutionMiddlewareOptions,
  type MiddlewareResponse,
  ClientBuilder,
} from '@commercetools/sdk-client-v2';

function before(options: BeforeExecutionMiddlewareOptions) {
  return (next: Next): Next => {
    return (req: MiddlewareRequest, res: MiddlewareResponse) => {
      // Logic to be executed goes here
      // option will contain { name: 'before-middleware-fn' }
      console.log(options); // { name: 'before-middleware-fn' }
      next(req, res);
    };
  };
}

const client: Client = new ClientBuilder()
  .withProjectKey('projectKey')
  .withBeforeExecutionMiddleware({
    name: 'before-middleware-fn',
    middleware: before,
  })
  // ...
  .build();

AfterExecutionMiddleware

This middleware runs after the SDK calls the Composable Commerce API, and is useful for checking the API response for further actions (for example, custom retry implementations and post-response processing). This middleware has access to the request and response object as well as the options included in .withAfterExecutionMiddleware().
import {
  type Next,
  type Client,
  type MiddlewareRequest,
  type AfterExecutionMiddlewareOptions,
  type MiddlewareResponse,
  ClientBuilder,
} from '@commercetools/sdk-client-v2';

function after(options: AfterExecutionMiddlewareOptions) {
  return (next: Next): Next => {
    return (req: MiddlewareRequest, res: MiddlewareResponse) => {
      // Logic to be executed goes here
      // option will contain { name: 'after-middleware-fn' }
      console.log(options); // { name: 'after-middleware-fn' }
      next(req, res);
    };
  };
}

const client: Client = new ClientBuilder()
  .withProjectKey('projectKey')
  .withAfterExecutionMiddleware({
    name: 'after-middleware-fn',
    middleware: after,
  })
  // ...
  .build();

Custom middleware

Certain use cases, such as adding headers to API requests, may require you to create custom middleware.

The following code example demonstrates how to create custom middleware that includes a value for the header X-External-User-ID.
function customHeaderMiddleware() {
  return (next) => (request, response) => {
    const newRequest = {
      ...request,
      headers: {
        ...request.headers,
        'X-External-User-ID': 'custom-header-value',
      },
    };

    next(newRequest, response);
  };
}
You can add this custom middleware by using the .withMiddleware() method. Using this method, the SDK calls your middleware before calling other middlewares.
const client = new ClientBuilder()
  .withMiddleware(customHeaderMiddleware())
  // ...
  .build();

Migrate to the v3 client

The v3 client is similar to the v2 client in many ways. They are both based on a middleware call chain (the middleware pattern).

The main difference is that v3 client middleware only accepts a single request object and returns a response object. This compares to the v2 client middleware which accepts both a request and response object and returns void.
The v3 client is purely Promise-based, which makes the TypeScript SDK consistent with other Composable Commerce SDKs. This new structure also enables the use of new middleware like the concurrent modification middleware.

Consult the following tables and example code for information on migrating from v2 client middleware to v3 client middleware.

HttpMiddleware

OptionReplaced/RemovedReason
includeHeadersRemovedThis option was not used in v2.
fetchReplaced by httpClientWe replaced this option because we added support for Axios. You can now pass either fetch or axios to the HTTP client function. fetch is the default option, if not defined.
abortControllerRemovedRemoved because of deprecation in v2.
retryOnAbort is now by default set to true.
HttpMiddleware in the v2 clientts
type HttpMiddlewareOptions = {
  host: string;
  credentialsMode?: 'omit' | 'same-origin' | 'include';
  includeHeaders?: boolean;
  includeResponseHeaders?: boolean;
  includeOriginalRequest?: boolean;
  includeRequestInErrorResponse?: boolean;
  maskSensitiveHeaderData?: boolean;
  timeout?: number;
  enableRetry?: boolean;
  retryConfig?: {
    maxRetries?: number;
    retryDelay?: number;
    backoff?: boolean;
    maxDelay?: number;
    retryOnAbort?: boolean;
    retryCodes?: Array<number | string>;
  };
  fetch?: any;
  abortController?: AbortController; // deprecated
  getAbortController?: () => AbortController;
};

const options: HttpMiddlewareOptions = {
  host: 'https://api.europe-west1.gcp.commercetools.com',
  includeResponseHeaders: true,
  maskSensitiveHeaderData: true,
  includeOriginalRequest: false,
  includeRequestInErrorResponse: false,
  enableRetry: true,
  retryConfig: {
    maxRetries: 3,
    retryDelay: 200,
    backoff: false,
    retryCodes: [503],
  },
  fetch: fetchFn,
};

const client = new ClientBuilder()
  .withHttpMiddleware(options)
  // ...
  .build();
HttpMiddleware in the v3 clientts
type HttpMiddlewareOptions = {
  host: string;
  credentialsMode?: 'omit' | 'same-origin' | 'include';
  includeResponseHeaders?: boolean;
  includeOriginalRequest?: boolean;
  includeRequestInErrorResponse?: boolean;
  maskSensitiveHeaderData?: boolean;
  timeout?: number;
  enableRetry?: boolean;
  retryConfig?: {
    maxRetries?: number;
    retryDelay?: number;
    backoff?: boolean;
    maxDelay?: number;
    retryOnAbort?: boolean;
    retryCodes?: Array<number | string>;
  };
  httpClient: Function<fetch | axiosInstance>;
  httpClientOptions?: object; // will be passed as a second argument to your httpClient function for configuration
  getAbortController?: () => AbortController;
};

// using a proxy agent
import { HttpsProxyAgent } from 'https-proxy-agent';
const agent = new HttpsProxyAgent('http://8.8.8.8:8888');

const options: HttpMiddlewareOptions = {
  host: 'https://api.europe-west1.gcp.commercetools.com',
  includeResponseHeaders: true,
  maskSensitiveHeaderData: true,
  includeOriginalRequest: false,
  includeRequestInErrorResponse: false,
  enableRetry: true,
  retryConfig: {
    maxRetries: 3,
    retryDelay: 200,
    backoff: false,
    retryCodes: [503],
  },
  httpClient: fetch,
  httpClientOptions: { agent } // this will be passed to fetch ()
};

const client = new ClientBuilder()
  .withHttpMiddleware(options)
  // ...
  .build();

AuthMiddleware

All instances of the fetch property now use httpClient.

ClientCredentialsFlow

OptionReplaced/RemovedReason
fetchReplaced by httpClientWe replaced this option because we added support for Axios. You can now pass either fetch or axios to the HTTP client function. fetch is the default option, if not defined.
ClientCredentialsFlow in the v2 clientts
type AuthMiddlewareOptions = {
  host: string
  projectKey: string
  credentials: Credentials
  scopes?: Array<string>
  oauthUri?: string
  fetch?: any
  tokenCache?: TokenCache
}

const options: AuthMiddlewareOptions {
  host: 'https://auth.europe-west1.gcp.commercetools.com',
  projectKey: 'test-project-key',
  credentials: {
    clientId: process.env.CTP_CLIENT_ID,
    clientSecret: process.env.CTP_CLIENT_SECRET
  },
  scopes: [`manage_project:${projectKey}`],
  fetch: fetchFn
}


const client = new ClientBuilder()
  .withClientCredentialsFlow(options)
  // ...
  .build()
ClientCredentialsFlow in the v3 clientts
type AuthMiddlewareOptions = {
  host: string
  projectKey: string
  credentials: {
    clientId: string
    clientSecret: string
  }
  scopes?: Array<string>
  oauthUri?: string
  httpClient: Function
  httpClientOptions?: object
  tokenCache?: TokenCache
}

const options: AuthMiddlewareOptions {
  host: 'https://auth.europe-west1.gcp.commercetools.com',
  projectKey: 'test-project-key',
  credentials: {
    clientId: process.env.CTP_CLIENT_ID,
    clientSecret: process.env.CTP_CLIENT_SECRET
  },
  scopes: [`manage_project:${projectKey}`],
  httpClient: fetch
}


const client = new ClientBuilder()
  .withClientCredentialsFlow(options)
  // ...
  .build()

PasswordAuthMiddleware

OptionReplaced/RemovedReason
fetchReplaced by httpClientWe replaced this option because we added support for Axios. You can now pass either fetch or axios to the HTTP client function. fetch is the default option, if not defined.
PasswordAuthMiddleware in the v2 clientts
type PasswordAuthMiddlewareOptions = {
  host: string;
  projectKey: string;
  credentials: {
    clientId: string;
    clientSecret: string;
    user: {
      username: string;
      password: string;
    };
  };
  scopes?: Array<string>;
  tokenCache?: TokenCache;
  oauthUri?: string;
  fetch?: any;
};

const options: PasswordAuthMiddlewareOptions = {
  host: 'https://auth.europe-west1.gcp.commercetools.com',
  projectKey: 'test-project-key',
  credentials: {
    clientId: process.env.CTP_CLIENT_ID,
    clientSecret: process.env.CTP_CLIENT_SECRET,
    user: {
      username: process.env.USERNAME,
      password: process.env.PASSWORD,
    },
  },
  scopes: [`manage_project:${projectKey}`],
  fetch: fetchFn,
};

const client = new ClientBuilder()
  .withPasswordFlow(options)
  // ...
  .build();
PasswordAuthMiddleware in the v3 clientts
type PasswordAuthMiddlewareOptions = {
  host: string;
  projectKey: string;
  credentials: {
    clientId: string;
    clientSecret: string;
    user: {
      username: string;
      password: string;
    };
  };
  scopes?: Array<string>;
  tokenCache?: TokenCache;
  oauthUri?: string;
  httpClient: Function<fetch | axiosInstance>;
  httpClientOptions?: object;
};

const options: PasswordAuthMiddlewareOptions = {
  host: 'https://auth.europe-west1.gcp.commercetools.com',
  projectKey: 'test-project-key',
  credentials: {
    clientId: process.env.CTP_CLIENT_ID,
    clientSecret: process.env.CTP_CLIENT_SECRET,
    user: {
      username: process.env.USERNAME,
      password: process.env.PASSWORD,
    },
  },
  scopes: [`manage_project:${projectKey}`],
  httpClient: fetch,
};

const client = new ClientBuilder()
  .withPasswordFlow(options)
  // ...
  .build();

AnonymousSessionFlow

OptionReplaced/RemovedReason
fetchReplaced by httpClientWe replaced this option because we added support for Axios. You can now pass either fetch or axios to the HTTP client function. fetch is the default option, if not defined.
Use the dedicated AnonymousAuthMiddlewareOptions type for options instead of the generic AuthMiddlewareOptions.
AnonymousSessionFlow in the v2 clientts
type AnonymousAuthMiddlewareOptions = {
  host: string;
  projectKey: string;
  credentials: {
    clientId: string;
    clientSecret: string;
    anonymousId?: string;
  };
  scopes?: Array<string>;
  oauthUri?: string;
  fetch?: any;
  tokenCache?: TokenCache;
};

const options: AnonymousAuthMiddlewareOptions = {
  host: 'https://auth.europe-west1.gcp.commercetools.com',
  projectKey: 'test-project-key',
  credentials: {
    clientId: process.env.CTP_CLIENT_ID,
    clientSecret: process.env.CTP_CLIENT_SECRET,
    anonymousId: process.env.CTP_ANONYMOUS_ID, // a unique id
  },
  scopes: [`manage_project:${projectKey}`],
  fetch: fetchFn,
};

const client = new ClientBuilder()
  .withAnonymousSessionFlow(options)
  // ...
  .build();
AnonymousSessionFlow in the v3 clientts
type AnonymousAuthMiddlewareOptions = {
  host: string;
  projectKey: string;
  credentials: {
    clientId: string;
    clientSecret: string;
    anonymousId?: string;
  };
  scopes?: Array<string>;
  oauthUri?: string;
  httpClient: Function<fetch | axiosInstance>;
  httpClientOptions?: object
  tokenCache?: TokenCache;
};

const options: AnonymousAuthMiddlewareOptions = {
  host: 'https://auth.europe-west1.gcp.commercetools.com',
  projectKey: 'test-project-key',
  credentials: {
    clientId: process.env.CTP_CLIENT_ID,
    clientSecret: process.env.CTP_CLIENT_SECRET,
    anonymousId: process.env.CTP_ANONYMOUS_ID, // a unique id
  },
  scopes: [`manage_project:${projectKey}`],
  httpClient: fetch,
};

const client = new ClientBuilder()
  .withAnonymousSessionFlow(options)
  // ...
  .build();

RefreshAuthMiddleware

OptionReplaced/RemovedReason
fetchReplaced by httpClientWe replaced this option because we added support for Axios. You can now pass either fetch or axios to the HTTP client function. fetch is the default option, if not defined.
RefreshAuthMiddleware in the v2 clientts
type RefreshAuthMiddlewareOptions = {
  host: string;
  projectKey: string;
  credentials: {
    clientId: string;
    clientSecret: string;
  };
  refreshToken: string;
  tokenCache?: TokenCache;
  oauthUri?: string;
  fetch?: any;
};

const options: RefreshAuthMiddlewareOptions = {
  host: 'https://auth.europe-west1.gcp.commercetools.com',
  projectKey: 'test-project-key',
  credentials: {
    clientId: process.env.CTP_CLIENT_ID,
    clientSecret: process.env.CTP_CLIENT_SECRET,
  },
  refreshToken: 'bXvTyxc5yuebdvwTwyXn==',
  tokenCache: TokenCache,
  scopes: [`manage_project:${projectKey}`],
  fetch: fetchFn,
};

const client = new ClientBuilder()
  .withRefreshTokenFlow(options)
  // ...
  .build();
RefreshAuthMiddleware in the v3 clientts
type RefreshAuthMiddlewareOptions = {
  host: string;
  projectKey: string;
  credentials: {
    clientId: string;
    clientSecret: string;
  };
  refreshToken: string;
  tokenCache?: TokenCache;
  oauthUri?: string;
  httpClient: Function<fetch | axiosInstance>;
  httpClientOptions?: object;
};

const options: RefreshAuthMiddlewareOptions = {
  host: 'https://auth.europe-west1.gcp.commercetools.com',
  projectKey: 'test-project-key',
  credentials: {
    clientId: process.env.CTP_CLIENT_ID,
    clientSecret: process.env.CTP_CLIENT_SECRET,
  },
  refreshToken: 'bXvTyxc5yuebdvwTwyXn==',
  tokenCache: TokenCache,
  scopes: [`manage_project:${projectKey}`],
  httpClient: fetch,
};

const client = new ClientBuilder()
  .withRefreshTokenFlow(options)
  // ...
  .build();

LoggerMiddleware

OptionReplaced/RemovedReason
loggerReplaced by loggerFnYou can now pass an optional custom logger function to the .withLoggerMiddleware() builder method. If you do not pass a logger function, the SDK uses console.log().
LoggerMiddleware in the v2 clientts
function logger(options?: LoggerMiddlewareOptions) {
  return (next: Next): Next => {
    return (req: MiddlewareRequest, res: MiddlewareResponse) => {
      console.log(options); // Logs all included options except the logger function output -> { level: 'debug', name: 'custom-logger-fn' }
      console.log(req, res);
      next(req, res);
    };
  };
}

const client: Client = new ClientBuilder()
  .withProjectKey('projectKey')
  .withLoggerMiddleware({
    level: 'debug',
    name: 'custom-logger-fn',
    logger,
  })
  // ...
  .build();
LoggerMiddleware in the v3 clientts
type LoggerMiddlewareOptions = {
  loggerFn?: (options: MiddlewareResponse) => void
}

const loggerMiddlewareOptions: LoggerMiddlewareOptions = {
  loggerFn: (response: MiddlewareResponse) => {
    console.log('Response is: ', response)
  },
}

const client = new ClientBuilder()
  .withLoggerMiddleware(loggerMiddlewareOptions)
  // ...
  .build();

Custom middleware

You must rewrite custom middleware to use the Promise-based v3 client.

Custom middleware in the v2 clientts
function customV2Middleware() {
  return (next) => (request, response) => {
    const newRequest = {
      ...request,
      headers: {
        ...request.headers,
        'X-UserAgent': 'custom-user-agent',
      },
    };

    next(newRequest, response);
  };
}
Custom middleware in the v3 clientts
function customV3Middleware() {
  return (next) => (request) => {
    const newRequest = {
      ...request,
      headers: {
        ...request.headers,
        'X-UserAgent': 'custom-user-agent',
      },
    };

    return next(newRequest);
  };
}