API KVK avec C# et .NET : données d'entreprises néerlandaises
KVKBase Team

API KVK avec C# et .NET : données d'entreprises néerlandaises

Intégrez l'API KVKBase dans votre projet C# ou .NET : HttpClient, injection de dépendances, gestion des erreurs et cache, avec du code prêt pour la production.

kvkapicsharpdotnetdéveloppeursintégration

C# et .NET sont la référence du développement logiciel d’entreprise aux Pays-Bas et bien au-delà. Des ERP de grande envergure aux plateformes SaaS modernes et aux microservices, .NET est partout. Si vous travaillez dans un tel environnement et avez besoin des données d’entreprises néerlandaises issues de la Chambre de commerce, ce guide vous montre comment intégrer proprement l’API KVKBase dans votre projet C# avec les patterns modernes de .NET.

Prérequis

  • .NET 8 ou supérieur (LTS)
  • Une clé d’API KVKBase

Aucun SDK externe n’est nécessaire : l’API KVKBase fonctionne avec du HTTP standard et renvoie du JSON propre. System.Net.Http.HttpClient et System.Text.Json suffisent.

Étape 1 : configuration

Ajoutez votre clé d’API dans appsettings.json. Ne l’écrivez jamais en dur dans votre code source.

{
  "KVKBase": {
    "ApiKey": "",
    "BaseUrl": "https://api.kvkbase.nl/api/v1"
  }
}

Créez ensuite une classe d’options fortement typée :

// KVKBaseOptions.cs
public class KVKBaseOptions
{
    public const string SectionName = "KVKBase";
    public string ApiKey { get; set; } = string.Empty;
    public string BaseUrl { get; set; } = "https://api.kvkbase.nl/api/v1";
}

Enregistrez les options dans Program.cs :

builder.Services.Configure<KVKBaseOptions>(
    builder.Configuration.GetSection(KVKBaseOptions.SectionName));

Étape 2 : modèles de réponse

Définissez des classes C# qui correspondent à la réponse JSON de l’API. Vous obtenez ainsi un typage complet et l’autocomplétion de votre IDE :

// Models/KvkCompany.cs
using System.Text.Json.Serialization;

public class KvkCompany
{
    [JsonPropertyName("kvkNummer")]
    public string KvkNumber { get; set; } = string.Empty;

    [JsonPropertyName("naam")]
    public string Name { get; set; } = string.Empty;

    [JsonPropertyName("rechtsvorm")]
    public string LegalForm { get; set; } = string.Empty;

    [JsonPropertyName("actief")]
    public bool IsActive { get; set; }

    [JsonPropertyName("adres")]
    public KvkAddress? Address { get; set; }

    [JsonPropertyName("btwNummer")]
    public string? VatNumber { get; set; }

    [JsonPropertyName("sbiCodes")]
    public List<SbiCode> SbiCodes { get; set; } = [];
}

public class KvkAddress
{
    [JsonPropertyName("straat")]
    public string Street { get; set; } = string.Empty;

    [JsonPropertyName("huisnummer")]
    public string HouseNumber { get; set; } = string.Empty;

    [JsonPropertyName("postcode")]
    public string PostalCode { get; set; } = string.Empty;

    [JsonPropertyName("plaats")]
    public string City { get; set; } = string.Empty;
}

public class SbiCode
{
    [JsonPropertyName("code")]
    public string Code { get; set; } = string.Empty;

    [JsonPropertyName("omschrijving")]
    public string Description { get; set; } = string.Empty;
}

Étape 3 : enregistrer HttpClient

L’approche recommandée dans .NET est un HttpClient typé ou nommé, via IHttpClientFactory :

// Program.cs
builder.Services.AddHttpClient("KVKBase", (sp, client) =>
{
    var options = sp.GetRequiredService<IOptions<KVKBaseOptions>>().Value;
    client.BaseAddress = new Uri(options.BaseUrl);
    client.DefaultRequestHeaders.Add("Authorization", $"Bearer {options.ApiKey}");
    client.Timeout = TimeSpan.FromSeconds(10);
});

Étape 4 : le service KVKBase

Créez une classe de service avec injection de dépendances. Vos contrôleurs et vos handlers restent ainsi propres :

// Services/KvkBaseService.cs
using System.Net.Http.Json;
using Microsoft.Extensions.Options;

public interface IKvkBaseService
{
    Task<KvkCompany?> GetCompanyAsync(string kvkNumber, CancellationToken ct = default);
    Task<List<KvkCompany>> SearchCompaniesAsync(string query, CancellationToken ct = default);
}

public class KvkBaseService : IKvkBaseService
{
    private readonly HttpClient _http;
    private readonly ILogger<KvkBaseService> _logger;

    public KvkBaseService(IHttpClientFactory factory, ILogger<KvkBaseService> logger)
    {
        _http = factory.CreateClient("KVKBase");
        _logger = logger;
    }

    public async Task<KvkCompany?> GetCompanyAsync(string kvkNumber, CancellationToken ct = default)
    {
        try
        {
            var response = await _http.GetAsync($"/api/v1/lookup/{kvkNumber}", ct);
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadFromJsonAsync<KvkCompany>(cancellationToken: ct);
        }
        catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
        {
            _logger.LogWarning("KVK number {KvkNumber} not found", kvkNumber);
            return null;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error fetching company data for {KvkNumber}", kvkNumber);
            throw;
        }
    }

    public async Task<List<KvkCompany>> SearchCompaniesAsync(string query, CancellationToken ct = default)
    {
        var url = $"/api/v1/search?q={Uri.EscapeDataString(query)}";
        var response = await _http.GetAsync(url, ct);
        response.EnsureSuccessStatusCode();

        var result = await response.Content.ReadFromJsonAsync<KvkSearchResult>(cancellationToken: ct);
        return result?.Items ?? [];
    }
}

public class KvkSearchResult
{
    [System.Text.Json.Serialization.JsonPropertyName("items")]
    public List<KvkCompany> Items { get; set; } = [];
}

Enregistrez le service dans Program.cs :

builder.Services.AddScoped<IKvkBaseService, KvkBaseService>();

Étape 5 : utiliser le service dans un contrôleur

Utilisez le service dans un endpoint Minimal API ou dans un contrôleur MVC :

// Exemple avec Minimal API
app.MapGet("/api/company/{kvkNumber}", async (
    string kvkNumber,
    IKvkBaseService kvkService,
    CancellationToken ct) =>
{
    var company = await kvkService.GetCompanyAsync(kvkNumber, ct);
    return company is null ? Results.NotFound() : Results.Ok(company);
})
.WithName("GetCompany")
.WithOpenApi();

Dans le style MVC :

[ApiController]
[Route("api/[controller]")]
public class CompanyController : ControllerBase
{
    private readonly IKvkBaseService _kvk;

    public CompanyController(IKvkBaseService kvk) => _kvk = kvk;

    [HttpGet("{kvkNumber}")]
    public async Task<ActionResult<KvkCompany>> Get(string kvkNumber, CancellationToken ct)
    {
        var company = await _kvk.GetCompanyAsync(kvkNumber, ct);
        return company is null ? NotFound() : Ok(company);
    }
}

Mise en cache avec IMemoryCache

Les données KVK changent rarement. Ajoutez un cache en mémoire pour réduire les coûts d’API et la latence :

public class KvkBaseService : IKvkBaseService
{
    private readonly HttpClient _http;
    private readonly IMemoryCache _cache;
    private readonly ILogger<KvkBaseService> _logger;
    private static readonly TimeSpan CacheDuration = TimeSpan.FromHours(24);

    public KvkBaseService(IHttpClientFactory factory, IMemoryCache cache, ILogger<KvkBaseService> logger)
    {
        _http = factory.CreateClient("KVKBase");
        _cache = cache;
        _logger = logger;
    }

    public async Task<KvkCompany?> GetCompanyAsync(string kvkNumber, CancellationToken ct = default)
    {
        var cacheKey = $"kvk:{kvkNumber}";

        if (_cache.TryGetValue(cacheKey, out KvkCompany? cached))
            return cached;

        var company = await FetchFromApiAsync(kvkNumber, ct);

        if (company is not null)
            _cache.Set(cacheKey, company, CacheDuration);

        return company;
    }

    private async Task<KvkCompany?> FetchFromApiAsync(string kvkNumber, CancellationToken ct)
    {
        try
        {
            var response = await _http.GetAsync($"/api/v1/lookup/{kvkNumber}", ct);
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadFromJsonAsync<KvkCompany>(cancellationToken: ct);
        }
        catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
        {
            _logger.LogWarning("KVK number {KvkNumber} not found", kvkNumber);
            return null;
        }
    }
}

Enregistrez IMemoryCache dans Program.cs :

builder.Services.AddMemoryCache();

Validation du numéro de TVA

KVKBase prend aussi en charge la validation des numéros de TVA via VIES. Voici comment procéder en C# :

public async Task<bool> ValidateVatNumberAsync(string vatNumber, CancellationToken ct = default)
{
    var response = await _http.GetAsync(
        $"/api/v1/vat/validate/{Uri.EscapeDataString(vatNumber)}", ct);

    if (!response.IsSuccessStatusCode)
        return false;

    var result = await response.Content.ReadFromJsonAsync<VatValidationResult>(cancellationToken: ct);
    return result?.IsValid ?? false;
}

public class VatValidationResult
{
    [System.Text.Json.Serialization.JsonPropertyName("valid")]
    public bool IsValid { get; set; }
}

Erreurs courantes à éviter

Instancier HttpClient directement : n’utilisez jamais new HttpClient() dans une classe de service. Passez toujours par IHttpClientFactory. Une instanciation directe provoque un épuisement des sockets sous forte charge.

Clé d’API dans le code source : utilisez toujours appsettings.json combiné à des variables d’environnement ou à Azure Key Vault. N’écrivez jamais une clé d’API en dur.

Aucun timeout défini : une API externe peut répondre lentement. Définissez toujours un timeout sur votre HttpClient (les 100 secondes par défaut sont beaucoup trop longues pour un appel de recherche).

Ne pas transmettre le CancellationToken : dans ASP.NET Core, vous disposez toujours d’un CancellationToken issu du contexte de la requête. Transmettez-le : il annule automatiquement les appels HTTP en cours lorsque le client se déconnecte.

Prochaines étapes

Maintenant que vous avez intégré l’API KVKBase, il reste beaucoup à explorer :

Obtenez une clé d’API gratuite sur kvkbase.nl et lancez-vous dès aujourd’hui.