Interactive Queries
AdvancedInteractive Queries expose state store contents over a REST API, turning the Kafka Streams instance into a queryable read model without an external DB.
Overview
Interactive Queries (IQ) allow you to query Kafka Streams local state stores directly from outside the stream topology, turning a Kafka Streams application into a queryable, real-time read model. Rather than writing aggregated state to an external database, you query the local RocksDB-backed stores via the KafkaStreams.store() API. In a multi-instance deployment, each instance holds only the partitions assigned to it. To answer queries for any key, instances must discover each other (via application.server config) and proxy requests to the instance that owns the relevant partition. This is the "distributed queryable state" pattern that eliminates the external DB entirely.
Querying local state stores
Use KafkaStreams.store(StoreQueryParameters) to access a read-only view of a named state store. The store must be materialized with a name — anonymous stores are not queryable. Available store types: keyValueStore() (point lookup + range scan), windowStore() (windowed queries), sessionStore() (session window queries).
// 1. Materialize the state store with a name during topology build
StreamsBuilder builder = new StreamsBuilder();
KTable<String, Long> wordCounts = builder
.stream("text-input", Consumed.with(Serdes.String(), Serdes.String()))
.flatMapValues(v -> Arrays.asList(v.split("\\s+")))
.groupBy((k, word) -> word)
.count(Materialized.as("word-count-store")); // named → queryable
// 2. Query the store from a REST endpoint
@RestController
public class WordCountController {
private final KafkaStreams streams;
@GetMapping("/counts/{word}")
public Long getCount(@PathVariable String word) {
ReadOnlyKeyValueStore<String, Long> store = streams.store(
StoreQueryParameters.fromNameAndType(
"word-count-store",
QueryableStoreTypes.keyValueStore())
);
Long count = store.get(word);
return count != null ? count : 0L;
}
@GetMapping("/counts")
public Map<String, Long> getAllCounts() {
ReadOnlyKeyValueStore<String, Long> store = streams.store(
StoreQueryParameters.fromNameAndType(
"word-count-store", QueryableStoreTypes.keyValueStore()));
Map<String, Long> result = new HashMap<>();
try (KeyValueIterator<String, Long> it = store.all()) {
it.forEachRemaining(kv -> result.put(kv.key, kv.value));
}
return result;
}
}Distributed queries across instances
When running multiple Kafka Streams instances, each holds a subset of partitions (and thus keys). To answer a query for any key, the receiving instance must discover which instance owns the partition for that key, then proxy the request. Configure application.server so instances can discover each other via metadata.
# application.properties — advertise this instance's REST endpoint
spring.kafka.streams.properties.application.server=192.168.1.10:8080
// Find which instance owns a given key
@GetMapping("/counts/{word}")
public Long getCountDistributed(@PathVariable String word) {
// Find the owner of this key's partition
KeyQueryMetadata metadata = streams.queryMetadataForKey(
"word-count-store", word, Serdes.String().serializer());
HostInfo activeHost = metadata.activeHost();
String thisHost = "192.168.1.10";
int thisPort = 8080;
if (activeHost.host().equals(thisHost) && activeHost.port() == thisPort) {
// This instance owns the key — query locally
ReadOnlyKeyValueStore<String, Long> store = streams.store(
StoreQueryParameters.fromNameAndType(
"word-count-store", QueryableStoreTypes.keyValueStore()));
return store.get(word);
} else {
// Proxy to the owning instance
String url = String.format("http://%s:%d/counts/%s",
activeHost.host(), activeHost.port(), word);
return restClient.get().uri(url).retrieve().body(Long.class);
}
}Windowed store queries
Windowed state stores (from tumbling/hopping/session window aggregations) require a WindowStore or SessionStore query. You can retrieve results for a key within a specific time range. This enables real-time dashboards showing metrics over the last N minutes without a separate TSDB.
// Windowed aggregation — count per word per 1-minute tumbling window
KTable<Windowed<String>, Long> windowedCounts = builder
.stream("text-input", Consumed.with(Serdes.String(), Serdes.String()))
.flatMapValues(v -> Arrays.asList(v.split("\\s+")))
.groupBy((k, word) -> word)
.windowedBy(TimeWindows.ofSizeWithNoGrace(Duration.ofMinutes(1)))
.count(Materialized.as("windowed-word-count"));
// Query windowed store — get counts for "kafka" in last 5 minutes
@GetMapping("/windowed-counts/{word}")
public List<Map<String, Object>> getWindowedCounts(@PathVariable String word) {
ReadOnlyWindowStore<String, Long> windowStore = streams.store(
StoreQueryParameters.fromNameAndType(
"windowed-word-count", QueryableStoreTypes.windowStore()));
Instant now = Instant.now();
Instant from = now.minus(Duration.ofMinutes(5));
List<Map<String, Object>> results = new ArrayList<>();
try (WindowStoreIterator<Long> it = windowStore.fetch(word, from, now)) {
it.forEachRemaining(kv -> results.add(Map.of(
"windowStart", Instant.ofEpochMilli(kv.key),
"count", kv.value
)));
}
return results;
}Key Points to Remember
- 1State stores must be materialized with a name (Materialized.as("name")) to be queryable — anonymous stores are not accessible
- 2streams.store(StoreQueryParameters.fromNameAndType(...)) returns a read-only view of the local state store
- 3In multi-instance deployments, each instance owns only its assigned partitions — use queryMetadataForKey to find the owner
- 4Configure application.server=host:port so instances can discover each other for distributed query proxying
- 5WindowStore supports time-ranged fetch queries — retrieve aggregations for a key over a specific time range
- 6Interactive Queries eliminate the need to sink aggregated state to an external database for simple read models
Interview Questions
Sign in to ask AriaWhat does a Kafka Streams state store need to be queryable via Interactive Queries?
In a 3-instance Kafka Streams deployment, how does an instance answer a query for a key it does not own?
What is the purpose of the application.server configuration in Kafka Streams?
How would you query a windowed state store to retrieve per-minute counts for the last hour?
What are the trade-offs of using Interactive Queries as a read model vs sinking state to a database?
Ask Aria about Interactive Queries
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.