TypeScript SDK middlewares for v2 client
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. Multiple middlewares can be added 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; // deprecatedgetAbortController?: () => 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 auth token lifecycle is managed by the SDK and there is usually no need to directly interact or access the authentication token. Expired tokens are automatically discarded and new tokens are generated (or refreshed if using withRefreshTokenFlow
) for new requests.
The following authentication flows can be used. The main difference in the authentication flows is the options
that can be passed to each of the middlewares.
withClientCredentialsFlow
Handles authentication for the client credentials flow of the Composable Commerce API.
type AuthMiddlewareOptions = {host: stringprojectKey: stringcredentials: Credentialsscopes?: Array<string>oauthUri?: stringfetch?: anytokenCache?: 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
CorrelationIdMiddleware adds the X-Correlation-ID
entry to the request headers.
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 importimport {createTelemetryMiddleware,TelemetryMiddlewareOptions,} from '@commercetools/ts-sdk-apm';const telemetryOptions: TelemetryMiddlewareOptions = {createTelemetryMiddleware,apm: () => typeof require('newrelic'), // installed npm `newrelic` packagetracer: () => typeof require('/absolute-path-to-a-tracer-module'),};const client = new ClientBuilder().withTelemetryMiddleware(telemetryOptions)// ....build();
LoggerMiddleware
Logs incoming requests and response objects. You can include an optional options
parameter which takes in a custom logger function and optional parameter that can be used within the 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 a call is made to 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 a call is made to the Composable Commerce API, and is useful when the response from the API needs to be checked 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);};}
This custom middleware can then be added using the .withMiddleware()
method. Using this method, your middleware will be called first before all 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
Option | Replaced/Removed | Reason |
---|---|---|
includeHeaders | Removed | This option was not used in v2. |
fetch | Replaced by httpClient | This option was replaced as 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. |
abortController | Removed | Removed because of deprecation in v2. |
retryOnAbort
is now by default set to true
.
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; // deprecatedgetAbortController?: () => 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();
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 configurationgetAbortController?: () => AbortController;};// using a proxy agentimport { 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 have been replaced with httpClient
.
ClientCredentialsFlow
Option | Replaced/Removed | Reason |
---|---|---|
fetch | Replaced by httpClient | This option was replaced as 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. |
type AuthMiddlewareOptions = {host: stringprojectKey: stringcredentials: Credentialsscopes?: Array<string>oauthUri?: stringfetch?: anytokenCache?: 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()
type AuthMiddlewareOptions = {host: stringprojectKey: stringcredentials: {clientId: stringclientSecret: string}scopes?: Array<string>oauthUri?: stringhttpClient: FunctionhttpClientOptions?: objecttokenCache?: 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
Option | Replaced/Removed | Reason |
---|---|---|
fetch | Replaced by httpClient | This option was replaced as 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. |
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();
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
Option | Replaced/Removed | Reason |
---|---|---|
fetch | Replaced by httpClient | This option was replaced as 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
.
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();
type AnonymousAuthMiddlewareOptions = {host: string;projectKey: string;credentials: {clientId: string;clientSecret: string;anonymousId?: string;};scopes?: Array<string>;oauthUri?: string;httpClient: Function<fetch | axiosInstance>;httpClientOptions?: objecttokenCache?: 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
Option | Replaced/Removed | Reason |
---|---|---|
fetch | Replaced by httpClient | This option was replaced as 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. |
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();
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
Option | Replaced/Removed | Reason |
---|---|---|
logger | Replaced by loggerFn | An optional custom logger function can now be passed to the .withLoggerMiddleware() builder method. The console.log() method is used if no logger function is passed. |
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();
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
Custom middleware must be rewritten to use the Promise-based v3 client.
function customV2Middleware() {return (next) => (request, response) => {const newRequest = {...request,headers: {...request.headers,'X-UserAgent': 'custom-user-agent',},};next(newRequest, response);};}
function customV3Middleware() {return (next) => (request) => {const newRequest = {...request,headers: {...request.headers,'X-UserAgent': 'custom-user-agent',},};return next(newRequest);};}