Recommended patterns for building reliable applications with the TypeScript SDK.
Client initialization
ClientBuilder to create a client. Always load credentials from environment variables rather than hardcoding them in your application.import { ClientBuilder } from '@commercetools/ts-client'
const projectKey = process.env.CTP_PROJECT_KEY
const authMiddlewareOptions = {
host: process.env.CTP_AUTH_HOST,
projectKey,
credentials: {
clientId: process.env.CTP_CLIENT_ID,
clientSecret: process.env.CTP_CLIENT_SECRET,
},
scopes: [process.env.CTP_SCOPES],
httpClient: fetch,
}
const httpMiddlewareOptions = {
host: process.env.CTP_API_HOST,
includeRequestInErrorResponse: true,
includeOriginalRequest: true,
httpClient: fetch,
}
const client = new ClientBuilder()
.withProjectKey(projectKey)
.withClientCredentialsFlow(authMiddlewareOptions)
.withHttpMiddleware(httpMiddlewareOptions)
.withUserAgentMiddleware()
.withLoggerMiddleware()
.build()
clientId and clientSecret in your source code or version control. Always use environment variables or a secrets manager to store sensitive values.Client reuse
The client acts as a connection pool. To prevent unnecessary network overhead and connection limits, create a single instance of the client and reuse it throughout your application's lifecycle.
// client.ts
import { ClientBuilder } from '@commercetools/ts-client'
import { createApiBuilderFromCtpClient } from '@commercetools/platform-sdk'
function getClient(options) {
return new ClientBuilder()
.withProjectKey(options.projectKey)
.withClientCredentialsFlow(options.authMiddlewareOptions)
.withHttpMiddleware(options.httpMiddlewareOptions)
.withUserAgentMiddleware()
.build()
}
const options = {
projectKey: process.env.CTP_PROJECT_KEY,
authMiddlewareOptions: { /* ... */ },
httpMiddlewareOptions: { /* ... */ },
}
export const apiRoot = createApiBuilderFromCtpClient(
getClient(options)
).withProjectKey({ projectKey: options.projectKey })
apiRoot throughout your application. This ensures only a single client instance is created for all requests.Token management
The SDK manages the full auth token lifecycle automatically. When a request is made, the SDK checks the token, generates a new one if expired or unavailable, and injects it into the request header. No manual token refresh is required.
Configure retries and timeouts
enableRetry: Enable retries on network errors and selected5xxresponses.falseby default to avoid unexpected behavior. Enable only if your application can tolerate retries.timeout: Maximum time in milliseconds to wait for a request. Requests exceeding this duration are aborted. Required whenenableRetryistrue.undefinedby default, which means no timeout is applied.maxRetries: Maximum number of retry attempts.10by default.backoff: Use exponential backoff between retries to avoid overloading the server.trueby default. Set tofalseto use a fixed delay between retries.retryDelay: Time in milliseconds to wait before the first retry.200by default.maxDelay: Maximum delay in milliseconds between retries, used to cap exponential backoff growth.undefinedby default, which means no maximum delay is applied.
const httpMiddlewareOptions = {
host: process.env.CTP_API_HOST,
enableRetry: true,
timeout: 10000,
maxRetries: 5,
retryDelay: 200,
maxDelay: 5000,
httpClient: fetch,
}
timeout and reducing retryDelay.Customize responses
includeOriginalRequest: Include the original client request in successful responses.trueby default.includeRequestInErrorResponse: Include the original request in error responses.falseby default.maskSensitiveHeaderData: Redact sensitive headers (such as the Authorization header) from included request data.trueby default.
const httpMiddlewareOptions = {
host: process.env.CTP_API_HOST,
includeOriginalRequest: true,
includeRequestInErrorResponse: true,
maskSensitiveHeaderData: true,
httpClient: fetch,
}
Concurrent requests
const client = new ClientBuilder()
.withClientCredentialsFlow(authMiddlewareOptions)
.withHttpMiddleware(httpMiddlewareOptions)
.withQueueMiddleware({ concurrency: 5 }) // defaults to 20
.build()
Configure proxies
httpClient function to HttpMiddleware options:import HttpsProxyAgent from 'https-proxy-agent'
const fetchWithProxy = (url, fetchOptions = {}) => {
fetchOptions.agent = new HttpsProxyAgent(process.env.HTTPS_PROXY)
return fetch(url, fetchOptions)
}
const httpMiddlewareOptions = {
host: process.env.CTP_API_HOST,
httpClient: fetchWithProxy,
}
Make direct requests
execute function to construct and send requests directly:const request = {
uri: `/${projectKey}/in-store/key=${storeKey}/customers/token`,
method: 'GET',
headers: {
Authorization: `Bearer ${token}`,
},
}
client
.execute(request)
.then((result) => { /* handle result */ })
.catch((error) => { /* handle error */ })
Process batch requests
Process function for processing paginated or batch requests. It takes a request, a callback invoked for each batch, and an options object.import { Process, ClientBuilder } from '@commercetools/ts-client'
import { createApiBuilderFromCtpClient } from '@commercetools/platform-sdk'
const ctpClient = new ClientBuilder()
.withProjectKey(projectKey)
.withClientCredentialsFlow(authMiddlewareOptions)
.withHttpMiddleware(httpMiddlewareOptions)
.build()
const apiRoot = createApiBuilderFromCtpClient(ctpClient)
.withProjectKey({ projectKey })
const request = await apiRoot.categories().get().clientRequest
const processBatch = (data) => data
Process(request, processBatch, {
total: 50, // total number of items to process
accumulate: true // accumulate all results into a single array (default: true)
})
.then((results) => {
// results is an array of all processed batches
})
.catch(console.error)
HTTP client
authMiddlewareOptions and httpMiddlewareOptions:const authMiddlewareOptions = {
// ...
httpClient: fetch,
}
const httpMiddlewareOptions = {
// ...
httpClient: fetch,
}
node-fetch v3, projects using node-fetch v3 inside the SDK must use ESM (.mjs) module format. node-fetch v2 is fully supported without restrictions.Client connection lifecycle
The SDK tears down connections automatically after each request-response cycle. You do not need to manually close or destroy client connections.
Read or write fields not in the SDK
In some cases, the platform API may return properties that are not part of the SDK's type definitions. You can extend an existing type to access those fields:
import { ErrorObject } from '@commercetools/platform-sdk'
type ExtendedErrorObject = ErrorObject & {
customField: string
}
Error handling
Logging
withLoggerMiddleware() can be added at multiple points in the middleware chain to log the request and response at each stage:const client = new ClientBuilder()
.withLoggerMiddleware() // log before the HTTP middleware
.withHttpMiddleware(httpMiddlewareOptions)
.withLoggerMiddleware() // log after the HTTP middleware
.build()