# TypeScript SDK best practices Recommended patterns for building reliable applications with the TypeScript SDK. ## Client initialization Use the `ClientBuilder` to create a client. Always load credentials from environment variables rather than hardcoding them in your application. ```typescript 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() ``` Never expose credentials such as `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. ```typescript // 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 }) ``` Import and use the exported `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. For details on the available authentication flows, see [AuthMiddleware](/dev-tooling/ts-sdk-middleware.md#authmiddleware). ## Configure retries and timeouts Enable and tune retries and timeouts through the [HttpMiddleware options](/dev-tooling/ts-sdk-middleware.md#httpmiddleware-options). Focus on these decisions: - Enable `enableRetry` only if your application can safely handle retried requests. - Set a `timeout` so stalled connections are retried (`retryConfig.retryOnAbort`). - Add transient status codes such as `[500, 502, 504]` to `retryConfig.retryCodes`. The `503` status code is always retried. - Tune `retryConfig.maxRetries`, `retryConfig.retryDelay`, and `retryConfig.maxDelay` to your latency budget. For every option and its default, see the [retryConfig options](/dev-tooling/ts-sdk-middleware.md#retryconfig-options). ```typescript const httpMiddlewareOptions = { host: process.env.CTP_API_HOST, httpClient: fetch, timeout: 10000, enableRetry: true, retryConfig: { maxRetries: 5, retryDelay: 200, backoff: true, maxDelay: 5000, retryOnAbort: true, retryCodes: [500, 502, 504], }, } ``` This retries transient `5xx` responses and connection-level failures (timed-out or aborted requests) with exponential backoff, as recommended in [Error handling through timeout and retries](/api/error-handling.md). Choose timeout and retry values that match your use case. If requests frequently time out due to network issues, consider increasing `timeout` and reducing `retryDelay`. ## Customize responses You can control what the SDK includes in successful and error responses using the following [HttpMiddleware options](/dev-tooling/ts-sdk-middleware.md#httpmiddleware-options): - `includeOriginalRequest`: Include the original client request in successful responses. `true` by default. - `includeRequestInErrorResponse`: Include the original request in error responses. `false` by default. - `maskSensitiveHeaderData`: Redact sensitive headers (such as the Authorization header) from included request data. `true` by default. ```typescript const httpMiddlewareOptions = { host: process.env.CTP_API_HOST, includeOriginalRequest: true, includeRequestInErrorResponse: true, maskSensitiveHeaderData: true, httpClient: fetch, } ``` ## Concurrent requests Use [QueueMiddleware](/dev-tooling/ts-sdk-middleware.md#queuemiddleware) to throttle the number of concurrent HTTP requests. This prevents overloading the API and can improve overall throughput. ```typescript const client = new ClientBuilder() .withClientCredentialsFlow(authMiddlewareOptions) .withHttpMiddleware(httpMiddlewareOptions) .withQueueMiddleware({ concurrency: 5 }) // defaults to 20 .build() ``` ## Configure proxies Configure proxies at the HTTP client level by providing a custom `httpClient` function to [HttpMiddleware options](/dev-tooling/ts-sdk-middleware.md#httpmiddleware-options): ```typescript 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 If the SDK does not provide a method for a specific endpoint, use the `execute` function to construct and send requests directly: ```typescript 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 The SDK exposes a `Process` function for processing paginated or batch requests. It takes a request, a callback invoked for each batch, and an options object. ```typescript 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) ``` See the [SDK test suite](https://github.com/commercetools/commercetools-sdk-typescript/blob/master/packages/sdk-client-v3/tests/client.test/client.test.ts) for further examples. ## HTTP client The SDK uses the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and compatible implementations such as [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch) or [unfetch](https://github.com/developit/unfetch). Pass the HTTP client explicitly to both `authMiddlewareOptions` and `httpMiddlewareOptions`: ```typescript const authMiddlewareOptions = { // ... httpClient: fetch, } const httpMiddlewareOptions = { // ... httpClient: fetch, } ``` Due to breaking changes in `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: ```typescript import { ErrorObject } from '@commercetools/platform-sdk' type ExtendedErrorObject = ErrorObject & { customField: string } ``` Fields may be absent from the SDK types when they are not part of the official API documentation. See this [GitHub issue](https://github.com/commercetools/commercetools-sdk-typescript/issues/247#issuecomment-1103705075) for further context. ## Error handling The SDK has built-in error handling. No additional error handling setup is required. For custom behavior, use [ErrorMiddleware](/dev-tooling/ts-sdk-middleware.md#errormiddleware). ## Logging The `withLoggerMiddleware()` can be added at multiple points in the middleware chain to log the request and response at each stage: ```typescript const client = new ClientBuilder() .withLoggerMiddleware() // log before the HTTP middleware .withHttpMiddleware(httpMiddlewareOptions) .withLoggerMiddleware() // log after the HTTP middleware .build() ``` For more details, see [LoggerMiddleware](/dev-tooling/ts-sdk-middleware.md#loggermiddleware). ## Keep the SDK up to date Always use the latest version of the SDK packages to ensure you have the latest features, bug fixes, and security patches. Subscribe to [GitHub releases](https://github.com/commercetools/commercetools-sdk-typescript/releases.atom) to be notified of new versions. ## Related pages - [Area overview page with navigation](/dev-tooling.md) - [Previous page: Middleware](/dev-tooling/ts-sdk-middleware.md) - [Search documentation and API specs](/search.md)