Kafka Streams
REST API
API Development
Application Programming
Backend Development

How to make REST API calls in kafka streams application/

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Integrating REST API calls in a Kafka Streams application isn't a native capability of Kafka Streams, but it can often be necessary to enrich or transform the streamed data using external data sources. This article aims to guide you through the best practices for making REST API calls within a Kafka Streams application, showcased with appropriate technical details and examples.

Core Components of a Kafka Streams Application

Before discussing the REST API integration, let's briefly revise the core components of a Kafka Streams application:

  1. Stream: A Stream is a sequence of immutable data records, where each record is a key-value pair.
  2. KStream and KTable: KStream represents a record stream where each data item is a key-value pair and KTable represents a changelog stream, which can be thought of as a table with upsert capabilities.
  3. Topology: This is the processing logic of the application. It defines how streams and tables are processed, including transformations and aggregations.

Why Make REST API Calls?

REST API calls within Kafka Streams applications are typically used to:

  • Enrich streaming data by adding external data.
  • Validate data against external systems.
  • Write results to external systems or trigger actions based on streaming data analysis.

How to Perform REST API Calls in Kafka Streams

Integration of REST API calls in Kafka Streams can complicate the application due to the synchronous and potentially slow nature of HTTP requests. Below are some strategies to manage these calls effectively:

1. Use Processor API

The low-level Processor API allows you greater control over stream processing. You can maintain state and perform asynchronous operations.

java
1public class ApiCallProcessor extends AbstractProcessor<String, String> {
2    private ProcessorContext context;
3
4    @Override
5    public void init(ProcessorContext context) {
6        this.context = context;
7    }
8
9    @Override
10    public void process(String key, String value) {
11        HttpResponse<String> response = Unirest.get("http://example.com/api").asString();
12        if (response.getStatus() == 200) {
13            String enrichedValue = value + " " + response.getBody();
14            context.forward(key, enrichedValue);
15        }
16    }
17
18    @Override
19    public void close() {}
20}

2. Asynchronous Processing using Futures

Handling the REST API calls asynchronously prevents the blocking of streams processing.

java
1public class AsyncApiCallProcessor extends AbstractProcessor<String, String> {
2    private ExecutorService executor;
3
4    @Override
5    public void init(ProcessorContext context) {
6        super.init(context);
7        this.executor = Executors.newFixedThreadPool(10);
8    }
9
10    @Override
11    public void process(String key, String value) {
12        CompletableFuture.supplyAsync(() -> {
13            try {
14                return Unirest.get("http://example.com/api").asString();
15            } catch (Exception e) {
16                return null;
17            }
18        }, executor).thenAccept(response -> {
19            if (response != null && response.getStatus() == 200) {
20                context().forward(key, value + " enriched with " + response.getBody());
21            }
22        });
23    }
24
25    @Override
26    public void close() {
27        executor.shutdown();
28    }
29}

Application Configuration

When configuring your streams to handle REST calls, consider settings like request timeouts and concurrency parameters to ensure that your system can handle potential bottlenecks or failures.

Summary Table

ConsiderationDetail
API Integration PointUse the Processor API or handle asynchronously using Futures.
Non-blockingEssential for high throughput and low latency.
Fault toleranceHandle API failures gracefully to prevent stream interruptions.
PerformanceAsynchronous operations can help maintain performance, but may increase complexity.

Further Enhancements

  • Circuit Breaker: Implement circuit breaker patterns to gracefully handle failed external service calls.
  • Backpressure: Consider backpressure mechanisms if the API calls can't keep up with the stream's data rate.
  • Caching: Add caching mechanisms to reduce the number of API calls for frequently requested data.

Conclusion

While Kafka Streams doesn't directly support making REST API calls, using the Processor API or asynchronous processing pattern can effectively integrate external API calls without significantly affecting the performance. Properly handling these operations whilst considering fault tolerance and system performance is critical for building robust streaming applications.


Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.