Java Spring Boot - REST API and Kafka Message Publishing

🕓 40 minutes

What you’ll learn:

How to setup your application for :

  • connecting to Kafka and publishing messages to its’ topic,
  • getting data from REST API,
  • providing data to REST API.

In this tutorial, we will create a simple java component with the Java Spring Boot scaffolder. We want to expose a single REST endpoint for getting client data. Client data is provided by another REST component client-data-db, so we need to configure a spring rest call for it. Any access to client data should be logged in the Kafka topic, so we need a Kafka client configuration as well.

ClientDataServiceImage

Project source

This example project can be cloned from: http://gitlab.factory.codenow-control.codenow.com/public-docs/client-authorization-demo/client-data-service.git

Prerequisites

  • Prepare your local development environment for CodeNOW with Java Spring Boot.
  • Run Apache Kafka locally.
    • You can run Kafka directly or using docker compose.
    • Configuration file for docker-compose can be downloaded from the link that can be found in the section Docker compose and third-party tools of the Java Spring Boot Local Development tutorial.
  • Create a new component

Steps

Open your IDE, import created component and start coding:

  • Define the message payload. Here is an example of the Client, which is a simple POJO with basic client data:

    • generate getters and setters with your IDE

      01 package io.codenow.client.data.service.model;
      02
      03 import java.time.LocalDate;
      04
      05 public class Client {
      06 private String username;
      07 private String firstname;
      08 private String surname;
      09 private LocalDate birthdate;
      10
      11 }
  • Next prepare the configuration for the Kafka logging client:

    • Go to the Kafka administration console (http://localhost:9000 if using Kafdrop from our Java Spring Local Development manual.) and create a new topic client-logging

    • Add maven dependency to your pom.xml

      01 <dependency>
      02 <groupId>org.springframework.kafka</groupId>
      03 <artifactId>spring-kafka</artifactId>
      04 </dependency>
  • For more details about spring-kafka, see: https://spring.io/projects/spring-kafka

  • Now add the configuration for the Kafka template to your Application.java (package io.codenow.client.data.service):

    01
    02 @Value("${kafka.broker.url}") private String kafkaBrokerUrl;
    03
    04 @Bean
    05 public ProducerFactory<String, String> producerFactory() {
    06 return new DefaultKafkaProducerFactory<>(producerConfigs());
    07 }
    08
    09 @Bean
    10 public Map<String, Object> producerConfigs() {
    11 Map<String, Object> props = new HashMap<>();
    12 props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaBrokerUrl);
    13 props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
    14 props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
    15 // See https://kafka.apache.org/documentation/#producerconfigs for more properties
    16 return props;
    17 }
    18
    19 @Bean
    20 public KafkaTemplate<String, String> kafkaTemplate() {
    21 return new KafkaTemplate<String, String>(producerFactory());
    22 }
  • Next, create a new controller and put all the parts together

    • For more details about the spring REST controller, see: https://spring.io/guides/gs/rest-service/

      01 package io.codenow.client.data.service.controller;
      02
      03 import org.slf4j.Logger;
      04 import org.slf4j.LoggerFactory;
      05 import org.springframework.beans.factory.annotation.Value;
      06 import org.springframework.kafka.core.KafkaTemplate;
      07 import org.springframework.web.bind.annotation.GetMapping;
      08 import org.springframework.web.bind.annotation.PathVariable;
      09 import org.springframework.web.bind.annotation.RequestMapping;
      10 import org.springframework.web.bind.annotation.RestController;
      11 import org.springframework.web.reactive.function.client.WebClient;
      12
      13 import io.codenow.client.data.service.model.Client;
      14 import reactor.core.publisher.Flux;
      15
      16 @RestController
      17 @RequestMapping("/data")
      18 public class ClientDataController {
      19 private static final Logger LOG = LoggerFactory.getLogger(ClientDataController.class);
      20
      21 private String clientDataDBURL;
      22 private String kafkaTopicName;
      23 private String kafkaTopicKey;
      24 private KafkaTemplate<String, String> kafkaTemplate;
      25
      26
      27 public ClientDataController(@Value("${endpoint.client.data.db}") String clientDataDBURL, @Value("${kafka.topic.name}") String kafkaTopicName, KafkaTemplate<String, String> kafkaTemplate, @Value("${kafka.topic.key}") String kafkaTopicKey) {
      28 super();
      29 this.clientDataDBURL = clientDataDBURL;
      30 this.kafkaTopicName = kafkaTopicName;
      31 this.kafkaTemplate = kafkaTemplate;
      32 this.kafkaTopicKey = kafkaTopicKey;
      33 }
      34
      35 @GetMapping("/{username}")
      36 private Flux<Client> getClientData(@PathVariable String username) {
      37 LOG.info("Get data for username: {}", username);
      38 kafkaTemplate.send(kafkaTopicName, kafkaTopicKey, username);
      39
      40 Flux<Client> clientFlux = WebClient.create().get().uri(clientDataDBURL + "/db/clients/" + username).retrieve()
      .bodyToFlux(Client.class);
      41
      42 clientFlux.subscribe(client -> LOG.info(client.toString()));
      43 return clientFlux;
      44
      45 }
      46 }
  • Last but not least, append the configuration for Kafka to config/application.yaml

    • Note that this configuration depends on your local development setup for Kafka can differ case-by-case.

    • Make sure you follow yaml syntax (especially whitespaces)

      01 endpoint:
      02 client:
      03 data:
      04 db: http://client-data-db
      05 kafka:
      06 broker:
      07 url: localhost:29092
      08 topic:
      09 name: client-logging
      10 key: client-data-service
  • Try to build and run the application in your IDE. After startup, you should be able to access your new controller’s swagger: http://localhost:8080/swagger/index.html

ClientDataServiceImageSwagger

Deploy to CodeNOW

If your code works in the local development, you are ready to push your changes to GIT and try to build and deploy your new component version to the CodeNOW environment.

What`s next?

See our other developer guide: