PHP SDK best practices

Ask about this Page
Copy for AI
View as Markdown

Recommended practices for configuring, running, and maintaining the PHP SDK.

Keep the SDK up to date

Always use a recent version of the SDK to ensure you have the latest features, performance improvements, and security fixes.

Store credentials securely

Never hardcode credentials such as clientId and clientSecret in your source code. Always load them from environment variables or a secrets management service at runtime.
Load credentials from environment variablesphp
$authConfig = new ClientCredentialsConfig(
  new ClientCredentials(
    getenv('CTP_CLIENT_ID'),
    getenv('CTP_CLIENT_SECRET'),
    getenv('CTP_SCOPES')
  ),
  [],
  getenv('CTP_AUTH_URL') . '/oauth/token'
);

Reuse the client

Building a client sets up an HTTP client, OAuth configuration, and any middleware you add. Create it once and reuse the same instance across requests instead of building a new client every time.

Build the client once and cache the instancephp
class ClientService
{
    private ?ApiRequestBuilder $apiClient = null;

    public function getApiClient(): ResourceByProjectKey
    {
        if (!$this->apiClient) {
            $authConfig = new ClientCredentialsConfig(
                new ClientCredentials(
                    getenv('CTP_CLIENT_ID'),
                    getenv('CTP_CLIENT_SECRET'),
                    getenv('CTP_SCOPES')
                ),
                [],
                getenv('CTP_AUTH_URL') . '/oauth/token'
            );

            $client = ClientFactory::of()->createGuzzleClient(
                new Config([], getenv('CTP_API_URL')),
                $authConfig
            );

            $this->apiClient = new ApiRequestBuilder($client);
        }

        return $this->apiClient->withProjectKey(getenv('CTP_PROJECT_KEY'));
    }
}

If you're using a framework such as Laravel, register the class as a singleton so the container reuses one instance within the current application lifecycle:

Register the client service as a singleton in Laravelphp
$this->app->singleton(ClientService::class, function ($app) {
    return new ClientService();
});

If your application runs as a long-lived worker process, such as Swoole or RoadRunner, instead of a traditional per-request PHP process, a cached client can reuse connections across requests and encounter the same kind of stale-connection errors as long-running services in other languages. Guzzle's cURL handler generally recovers from a reset connection automatically. If you see intermittent connection errors, recreate the client periodically or turn off connection reuse for the affected requests.

Configure default middleware

The factory automatically installs correlation IDs. It adds authentication and reauthentication middleware when you pass a ClientCredentialsConfig, and logging middleware when you pass a PSR-3 logger as the third argument to createGuzzleClient. See the middleware reference for details.
Configure middleware when creating the clientphp
$client = ClientFactory::of()->createGuzzleClient(
  new Config([], getenv('CTP_API_URL')),
  $authConfig,
  $logger
);

Logging

The SDK accepts any PSR-3 compatible logger. If you're using a framework, pass its existing logger instead of creating a separate one, so SDK requests appear alongside the rest of your application logs:
Reuse your framework's loggerphp
use Illuminate\Support\Facades\Log;

$client = ClientFactory::of()->createGuzzleClient(
  new Config([], getenv('CTP_API_URL')),
  $authConfig,
  Log::getLogger()
);

Handle errors safely

The SDK throws ApiClientException for recoverable client errors (HTTP status codes 4xx) and ApiServerException for server errors (HTTP status codes 5xx). Both extend Guzzle's ClientException and ServerException, so getResponse()->getStatusCode() returns the HTTP status, and getResult() returns the deserialized error body.

Catch these exceptions and map them to your own response. Avoid returning the caught exception object directly, since it can expose internal details such as the original request to API consumers.

Map SDK exceptions to a safe responsephp
use Commercetools\Exception\ApiClientException;
use Commercetools\Exception\ApiServerException;

try {
    $product = $apiRoot->products()->withId($id)->get()->execute();
} catch (ApiClientException | ApiServerException $e) {
    return response()->json([
        'message' => 'The commercetools API request failed.',
    ], $e->getResponse()->getStatusCode());
}
For more specific error handling, catch one of the SDK's narrower exception subclasses instead, such as NotFoundException or ConcurrentModificationException.

Retrieve the raw API response

Use execute() when you want the SDK to deserialize the response into a model. Use send() and getBody() when you need the raw JSON, for example to log the full payload or pass it through to another system unchanged.
Get the raw API responsephp
$response = $apiRoot
  ->products()
  ->get()
  ->send();

$rawJson = (string) $response->getBody();

Further resources