KVK API met Java en Spring Boot: Bedrijfsgegevens opvragen in je Java-applicatie
Integreer de KVKBase API in je Java Spring Boot project. Met codevoorbeelden voor RestClient, exception handling, caching en een herbruikbare service-klasse, geschikt voor enterprise Java-developers.
Java blijft een van de dominante talen in enterprise software. Van grote webshops tot fintech-platforms en HR-systemen, Java en Spring Boot vormen de ruggengraat van veel kritische systemen in Nederland. Als je in zo’n omgeving werkt en bedrijfsgegevens uit de KVK nodig hebt, laten we in deze gids zien hoe je de KVKBase API netjes integreert met moderne Spring Boot-patronen.
Vereisten
- Java 21 of hoger (LTS)
- Spring Boot 3.3+
- Maven of Gradle
- Een API-key van KVKBase
Voeg de Spring Web starter toe aan je project als die er nog niet in zit:
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Stap 1: Configuratie
Voeg je API-key toe aan application.yml of application.properties. Zet de key nooit hardcoded in je code.
# application.yml
kvkbase:
api-key: ${KVKBASE_API_KEY}
base-url: https://api.kvkbase.nl/api/v1
timeout-ms: 5000
Maak vervolgens een configuratieklasse aan:
// KvkbaseProperties.java
package nl.jouwbedrijf.kvk.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "kvkbase")
public class KvkbaseProperties {
private String apiKey;
private String baseUrl;
private int timeoutMs = 5000;
// Getters en setters
public String getApiKey() { return apiKey; }
public void setApiKey(String apiKey) { this.apiKey = apiKey; }
public String getBaseUrl() { return baseUrl; }
public void setBaseUrl(String baseUrl) { this.baseUrl = baseUrl; }
public int getTimeoutMs() { return timeoutMs; }
public void setTimeoutMs(int timeoutMs) { this.timeoutMs = timeoutMs; }
}
Stap 2: Data-klassen (Records)
Gebruik Java records voor de API-responses. Ze zijn immutable, compact en passen goed bij het lezen van JSON-data.
// KvkBedrijf.java
package nl.jouwbedrijf.kvk.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public record KvkBedrijf(
String kvkNummer,
String naam,
String rechtsvorm,
boolean actief,
String startdatum,
String sbiCode,
String sbiOmschrijving,
KvkAdres adres
) {}
// KvkAdres.java
package nl.jouwbedrijf.kvk.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public record KvkAdres(
String straat,
String huisnummer,
String postcode,
String plaats,
String land
) {}
Stap 3: RestClient configureren
Spring Boot 3.2+ heeft de nieuwe RestClient die RestTemplate vervangt. Configureer een bean met de juiste headers en timeout:
// KvkbaseClientConfig.java
package nl.jouwbedrijf.kvk.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestClient;
@Configuration
public class KvkbaseClientConfig {
@Bean
public RestClient kvkbaseRestClient(KvkbaseProperties props) {
var factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(props.getTimeoutMs());
factory.setReadTimeout(props.getTimeoutMs());
return RestClient.builder()
.baseUrl(props.getBaseUrl())
.defaultHeader("x-api-key", props.getApiKey())
.defaultHeader("Accept", "application/json")
.requestFactory(factory)
.build();
}
}
Stap 4: KvkbaseService
De service-klasse bevat alle businesslogica rondom KVK-ophalen. Centraliseer hier de foutafhandeling zodat de rest van je applicatie niet hoeft na te denken over HTTP-details.
// KvkbaseService.java
package nl.jouwbedrijf.kvk.service;
import nl.jouwbedrijf.kvk.exception.BedrijfNietGevondenException;
import nl.jouwbedrijf.kvk.exception.KvkbaseApiException;
import nl.jouwbedrijf.kvk.model.KvkBedrijf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatusCode;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
@Service
public class KvkbaseService {
private static final Logger log = LoggerFactory.getLogger(KvkbaseService.class);
private final RestClient restClient;
public KvkbaseService(RestClient kvkbaseRestClient) {
this.restClient = kvkbaseRestClient;
}
/**
* Haalt bedrijfsgegevens op via KVK-nummer.
*
* @throws BedrijfNietGevondenException als het KVK-nummer niet bestaat
* @throws KvkbaseApiException bij overige API-fouten
*/
public KvkBedrijf ophalenOpKvkNummer(String kvkNummer) {
log.debug("KVK-opzoeking voor nummer: {}", kvkNummer);
return restClient.get()
.uri("/kvk/{kvkNummer}", kvkNummer)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, (request, response) -> {
if (response.getStatusCode().value() == 404) {
throw new BedrijfNietGevondenException(kvkNummer);
}
throw new KvkbaseApiException(
"Client-fout bij KVK-opzoeking: " + response.getStatusCode()
);
})
.onStatus(HttpStatusCode::is5xxServerError, (request, response) -> {
throw new KvkbaseApiException(
"Server-fout bij KVK-opzoeking: " + response.getStatusCode()
);
})
.body(KvkBedrijf.class);
}
/**
* Controleert of een bedrijf actief is ingeschreven.
* Geeft false terug bij een onbekend KVK-nummer.
*/
public boolean isActiefIngeschreven(String kvkNummer) {
try {
KvkBedrijf bedrijf = ophalenOpKvkNummer(kvkNummer);
return bedrijf.actief();
} catch (BedrijfNietGevondenException e) {
return false;
}
}
}
Stap 5: Custom exceptions
Maak aparte exception-klassen zodat je stroomafwaarts onderscheid kunt maken tussen “niet gevonden” en “API-probleem”:
// BedrijfNietGevondenException.java
package nl.jouwbedrijf.kvk.exception;
public class BedrijfNietGevondenException extends RuntimeException {
private final String kvkNummer;
public BedrijfNietGevondenException(String kvkNummer) {
super("Geen bedrijf gevonden met KVK-nummer: " + kvkNummer);
this.kvkNummer = kvkNummer;
}
public String getKvkNummer() { return kvkNummer; }
}
// KvkbaseApiException.java
package nl.jouwbedrijf.kvk.exception;
public class KvkbaseApiException extends RuntimeException {
public KvkbaseApiException(String message) {
super(message);
}
}
Stap 6: Caching met Spring Cache
KVK-data verandert zelden. Sla resultaten op in een lokale cache zodat je API-calls beperkt en latency laag blijft. Spring Cache werkt goed voor dit patroon.
Voeg de cache starter toe:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Optioneel: Caffeine voor een in-memory LRU-cache -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
Activeer caching en configureer TTL:
// CacheConfig.java
package nl.jouwbedrijf.kvk.config;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager manager = new CaffeineCacheManager("kvkBedrijven");
manager.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(6, TimeUnit.HOURS)
.maximumSize(10_000));
return manager;
}
}
Annoteer de service-methode met @Cacheable:
@Cacheable(value = "kvkBedrijven", key = "#kvkNummer")
public KvkBedrijf ophalenOpKvkNummer(String kvkNummer) {
// ... bestaande implementatie
}
Nu wordt een KVK-nummer maximaal eens per 6 uur opgezocht bij de API. Perfecte balans tussen versheid en efficiency.
Stap 7: REST Controller
Stel dat je een interne API-endpoint wilt aanbieden zodat front-end of andere services bedrijfsdata kunnen ophalen:
// KvkController.java
package nl.jouwbedrijf.kvk.controller;
import nl.jouwbedrijf.kvk.exception.BedrijfNietGevondenException;
import nl.jouwbedrijf.kvk.model.KvkBedrijf;
import nl.jouwbedrijf.kvk.service.KvkbaseService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/bedrijven")
public class KvkController {
private final KvkbaseService kvkbaseService;
public KvkController(KvkbaseService kvkbaseService) {
this.kvkbaseService = kvkbaseService;
}
@GetMapping("/{kvkNummer}")
public ResponseEntity<KvkBedrijf> getBedrijf(@PathVariable String kvkNummer) {
try {
KvkBedrijf bedrijf = kvkbaseService.ophalenOpKvkNummer(kvkNummer);
return ResponseEntity.ok(bedrijf);
} catch (BedrijfNietGevondenException e) {
return ResponseEntity.notFound().build();
}
}
@GetMapping("/{kvkNummer}/actief")
public ResponseEntity<Boolean> isActief(@PathVariable String kvkNummer) {
boolean actief = kvkbaseService.isActiefIngeschreven(kvkNummer);
return ResponseEntity.ok(actief);
}
}
Stap 8: Unit tests met MockRestServiceServer
Test de service-klasse zonder echte HTTP-calls via MockRestServiceServer:
// KvkbaseServiceTest.java
package nl.jouwbedrijf.kvk.service;
import nl.jouwbedrijf.kvk.exception.BedrijfNietGevondenException;
import nl.jouwbedrijf.kvk.model.KvkBedrijf;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestClient;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
import static org.springframework.test.web.client.response.MockRestResponseCreators.*;
@SpringBootTest
class KvkbaseServiceTest {
@Autowired
private KvkbaseService service;
@Autowired
private RestClient kvkbaseRestClient;
@Test
void ophalenOpKvkNummer_geeftBedrijfTerug() {
MockRestServiceServer server = MockRestServiceServer.bindTo(kvkbaseRestClient).build();
server.expect(requestTo("https://api.kvkbase.nl/api/v1/kvk/12345678"))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("""
{
"kvkNummer": "12345678",
"naam": "Test BV",
"rechtsvorm": "Besloten Vennootschap",
"actief": true,
"startdatum": "2010-01-01"
}
""", MediaType.APPLICATION_JSON));
KvkBedrijf bedrijf = service.ophalenOpKvkNummer("12345678");
assertThat(bedrijf.naam()).isEqualTo("Test BV");
assertThat(bedrijf.actief()).isTrue();
}
@Test
void ophalenOpKvkNummer_gooidExceptionBijNietGevonden() {
MockRestServiceServer server = MockRestServiceServer.bindTo(kvkbaseRestClient).build();
server.expect(requestTo("https://api.kvkbase.nl/api/v1/kvk/99999999"))
.andRespond(withStatus(HttpStatus.NOT_FOUND));
assertThatThrownBy(() -> service.ophalenOpKvkNummer("99999999"))
.isInstanceOf(BedrijfNietGevondenException.class);
}
}
Praktijkscenario: KVK-validatie bij orderverwerking
Een veelvoorkomend use-case: controleer bij het aanmaken van een B2B-order of de klant een actief KVK-nummer heeft.
@Service
public class OrderService {
private final KvkbaseService kvkbaseService;
private final OrderRepository orderRepository;
public OrderService(KvkbaseService kvkbaseService, OrderRepository orderRepository) {
this.kvkbaseService = kvkbaseService;
this.orderRepository = orderRepository;
}
public Order maakOrderAan(OrderRequest request) {
// Verifieer KVK vóór order aanmaken
if (request.kvkNummer() != null) {
boolean actief = kvkbaseService.isActiefIngeschreven(request.kvkNummer());
if (!actief) {
throw new OngeldigBedrijfException(
"KVK-nummer " + request.kvkNummer() + " is niet actief"
);
}
// Verrijk order met bedrijfsnaam
KvkBedrijf bedrijf = kvkbaseService.ophalenOpKvkNummer(request.kvkNummer());
return orderRepository.save(new Order(
request.kvkNummer(),
bedrijf.naam(),
request.producten()
));
}
return orderRepository.save(new Order(null, null, request.producten()));
}
}
Samenvatting
Met een paar Spring Boot-componenten heb je een robuuste KVK-integratie:
KvkbasePropertiesvoor configuratie viaapplication.ymlRestClientbean met API-key en timeoutKvkbaseServicemet gecentraliseerde foutafhandeling@Cacheablevoor efficiënt hergebruik van resultaten- Custom exceptions voor duidelijk foutonderscheid
- Unit tests via
MockRestServiceServer
Lees ook: KVK API integreren met Node.js en TypeScript voor een vergelijkbare aanpak in JavaScript-omgevingen, en Bedrijfsgegevens opvragen voor je webshop voor checkout-integraties specifiek gericht op e-commerce.