KubeMQ
Client SDKsNode.jsHow-to guidesRPC

Cached Query

Send a cached KubeMQ query in Node.js, serving repeat requests from the server cache to skip the handler and cut latency.

Overview

Query response caching lets the broker answer repeat requests without re-running your handler — useful when a query is expensive to compute (a database lookup, an aggregation, a downstream call) but the same input is asked for repeatedly in a short window. Only the first request pays the processing cost; every other caller gets the same answer straight from the broker.

Set cacheKey and cacheTtlInSeconds in createQuery(...). The first query with a given key is a miss: it reaches the handler, and the broker stores the response under that key for the TTL. A subsequent query with the same key is a hit — the broker returns the stored response directly without invoking the handler. cacheHit on the response tells you which happened.

Gotchas: the cache is keyed by the string you choose, not by the query body — if the underlying data changes mid-TTL, callers can get a stale answer until it expires. Keys are scoped per channel, so the same key on another channel is a separate entry. Caching only helps when requests genuinely repeat with the same key.

Prerequisites

  • KubeMQ server running on localhost:50000
  • Node.js SDK installed (npm install kubemq-js)

Code

cached-query.ts
/**
 * Example: Query with Cache Key and TTL
 *
 * Demonstrates sending a query with caching enabled. The first query is a
 * cache miss (the handler responds), and the second query is a cache hit
 * (served directly from the KubeMQ server cache without calling the handler).
 *
 * Prerequisites:
 *   - KubeMQ server running on localhost:50000
 *
 * Run: npx tsx examples/rpc/cached-query.ts
 */
import { KubeMQClient, createQuery } from 'kubemq-js';

async function main(): Promise<void> {
  const client = await KubeMQClient.create({
    address: 'localhost:50000',
    clientId: 'js-rpc-cached-query-client',
  });

  try {
    // Set up a query handler so there is a responder for the first (uncached) query.
    const sub = client.subscribeToQueries({
      channel: 'js-rpc.cached-query',
      onQuery: async (q) => {
        console.log('Handler called for query:', q.id);
        await client.sendQueryResponse({
          id: q.id,
          replyChannel: q.replyChannel,
          executed: true,
          body: new TextEncoder().encode(JSON.stringify({ sku: 'WIDGET-42', qty: 128 })),
        });
      },
      onError: (err) => {
        console.error('Subscription error:', err.message);
      },
    });

    // Wait for the subscription to register on the server.
    await new Promise((r) => setTimeout(r, 500));

    const query = createQuery({
      channel: 'js-rpc.cached-query',
      body: JSON.stringify({ sku: 'WIDGET-42', warehouse: 'east' }),
      timeoutInSeconds: 10,
      cacheKey: 'inventory:WIDGET-42:east',
      cacheTtlInSeconds: 60,
    });

    // First query hits the responder (cache miss).
    console.log('Sending first query (expect cache miss)...');
    const response1 = await client.sendQuery(query);
    console.log(
      'Response 1 — executed:',
      response1.executed,
      '| cacheHit:',
      response1.cacheHit ?? false,
    );

    // Second query returns cached response — handler is not called.
    console.log('Sending second query (expect cache hit)...');
    const response2 = await client.sendQuery(query);
    console.log(
      'Response 2 — executed:',
      response2.executed,
      '| cacheHit:',
      response2.cacheHit ?? false,
    );

    sub.cancel();
  } finally {
    await client.close();
  }
}

main().catch(console.error);

How It Works

  • cacheKey and cacheTtlInSeconds in createQuery() enable server-side response caching: the first query with this key calls the handler; subsequent queries within the TTL are served from the KubeMQ cache.
  • response1.cacheHit is false (or undefined) on the first call because the cache is cold; response2.cacheHit is true because the server returns the stored response without invoking the handler.
  • The handler logs 'Handler called for query:' only once — on the second query the handler is not invoked at all, demonstrating the cache bypass.
  • The same query object is reused for both calls because createEventMessage() / createQuery() produce immutable frozen objects; they are safe to send multiple times.

Was this page helpful?

On this page