# .NET SDK best practices

**Recommended practices for configuring, running, and maintaining the .NET 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.

To stay informed about new releases, watch the [GitHub repository](https://github.com/commercetools/commercetools-dotnet-core-sdk-v2) or check [NuGet Gallery](https://www.nuget.org/packages/commercetools.Sdk.Api) for new versions.

## Reuse the client

`UseCommercetoolsApi` and `UseCommercetoolsImportApi` register the client with the dependency injection container, so ASP.NET Core creates and reuses a single instance for the application's lifetime. Inject the registered client into your services and controllers instead of constructing one manually:

```csharp title="Inject the registered client"
public class ProductsService : IProductsService
{
    private readonly ProjectApiRoot _api;

    public ProductsService(ProjectApiRoot api)
    {
        _api = api;
    }
}
```

Choose the lifetime of application services according to their state and dependencies. The injected SDK client remains reusable whether the consuming service is transient, scoped, or singleton.

## Configure retries and timeouts

The SDK does not retry requests and does not configure its own timeout. Requests use the default `HttpClient` timeout of 100 seconds, which is usually too long for interactive workloads. Add a [Polly](https://www.pollydocs.org/) policy to the SDK's `HttpClient` to retry transient network errors and `5xx` responses with exponential backoff, and to cancel requests that exceed a shorter timeout budget. For the full setup, see [Configure retries and timeouts](/dev-tooling/dotnet-sdk-getting-started.md#configure-retries-and-timeouts).

## Handle errors safely

The SDK throws `ApiHttpException` for HTTP responses with a status code of `400` or higher. Its `StatusCode` property contains the response status code. Catch this exception in a global exception handler and map it to a sanitized response. Avoid returning the exception object directly, since it can expose internal request and response details to API consumers.

```csharp title="Map SDK exceptions to a safe response"
using commercetools.Base.Client;
using Microsoft.AspNetCore.Diagnostics;

app.UseExceptionHandler(errorApp =>
{
    errorApp.Run(async context =>
    {
        var feature = context.Features.Get<IExceptionHandlerFeature>();

        if (feature?.Error is ApiHttpException apiException)
        {
            context.Response.StatusCode = apiException.StatusCode;
            await context.Response.WriteAsJsonAsync(new { message = "The commercetools API request failed." });
            return;
        }

        context.Response.StatusCode = 500;
        await context.Response.WriteAsJsonAsync(new { message = "An unexpected error occurred." });
    });
});
```

## Check whether a resource exists without fetching it

Use a `Head()` request instead of `Get()` when you only need to know whether a resource exists. This avoids transferring and deserializing the full resource body:

```csharp title="Check whether a Product exists"
public async Task<bool> CheckProductExistsAsync(string key)
{
    try
    {
        await _api.Products().WithKey(key).Head().ExecuteAsync();
        return true;
    }
    catch (ApiHttpException ex) when (ex.StatusCode == 404)
    {
        return false;
    }
}
```

## Logging

The SDK logs every request and response through [`Microsoft.Extensions.Logging`](https://learn.microsoft.com/dotnet/core/extensions/logging). `UseCommercetoolsApi` registers a logging handler automatically, so no extra setup is required. Configure the log level for the `commercetoolsLoggerHandler` category in `appsettings.json`:

```json title="Configure the SDK's log level"
{
  "Logging": {
    "LogLevel": {
      "commercetoolsLoggerHandler": "Debug"
    }
  }
}
```

To customize logging behavior, implement `ILoggerHandlerFactory` and register it after calling `UseCommercetoolsApi`, so your implementation replaces the default:

```csharp title="Replace the default logging handler"
services.UseCommercetoolsApi(configuration, "Client");
services.AddSingleton<ILoggerHandlerFactory, CustomLoggerHandlerFactory>();
```

## Further resources

- [Get started](/dev-tooling/dotnet-sdk-getting-started.md): client setup, multiple clients, and retry configuration
- [Query Predicate builders](/dev-tooling/dotnet-sdk-predicates.md): type-safe query predicates
- [Integration tests](https://github.com/commercetools/commercetools-dotnet-core-sdk-v2/tree/master/commercetools.Sdk/IntegrationTests): working examples for common SDK operations

## Related pages

- [Area overview page with navigation](/dev-tooling.md)
- [Previous page: Get started](/dev-tooling/dotnet-sdk-getting-started.md)
- [Next page: Query Predicate builders](/dev-tooling/dotnet-sdk-predicates.md)
- [Search documentation and API specs](/search.md)