# RPC — Commands & Queries (/learn/rpc)



<RpcHero className="w-full max-h-[300px]" />

Think of RPC like a phone call — you dial a number, ask a question, and wait on the line for an answer. If nobody picks up within a set time, you hang up and try again. KubeMQ RPC brings this synchronous request-reply model to messaging infrastructure.

KubeMQ implements RPC through two complementary operation types following the CQRS (Command Query Responsibility Segregation) principle: **Commands** for writes and **Queries** for reads.

<Callout type="info">
  **The concept it implements.** RPC is KubeMQ's realization of the [request/reply interaction style](/learn/concepts/interaction-styles) — the sender blocks on a single matched response. The timeout-and-retry behavior maps to the [delivery guarantees](/learn/concepts/delivery-guarantees) you choose at the application level: a request either gets exactly one answer or a timeout error.
</Callout>

## Commands vs Queries [#commands-vs-queries]

| Aspect                | Commands                                         | Queries                                           |
| --------------------- | ------------------------------------------------ | ------------------------------------------------- |
| **Purpose**           | State-changing operations (writes, mutations)    | Read-only operations (data lookups)               |
| **Response body**     | Stripped — sender receives only execution status | Preserved — sender receives full response payload |
| **Response metadata** | Stripped                                         | Preserved                                         |
| **Caching**           | Not supported                                    | Supported via `CacheKey` / `CacheTTL`             |
| **Use cases**         | Order placement, device control, config changes  | Data lookups, status checks, service reads        |

Commands tell the system to **do something** and return only a success/failure indicator. Queries **ask for data** and return the full response body and metadata.

## How It Works [#how-it-works]

<Mermaid
  chart="sequenceDiagram
    participant Sender
    participant KubeMQ as KubeMQ Broker
    participant Responder

    Sender->>KubeMQ: SendRequest (Command or Query)
    KubeMQ->>Responder: Deliver request
    Responder->>Responder: Process request
    Responder->>KubeMQ: SendResponse
    KubeMQ->>Sender: Deliver response

    Note over Sender,Responder: Commands: response body stripped<br/>Queries: full response preserved"
/>

*Request/reply: the sender blocks while KubeMQ routes the request to a responder and delivers the single matched response back.*

The sender publishes a request to a named channel with a timeout. KubeMQ routes the request to a subscribed responder (or load-balances across a group of responders). The responder processes the request and sends a response back through KubeMQ. If no response arrives before the timeout expires, the sender receives a timeout error.

## Key Features [#key-features]

* **Synchronous request-reply** — sender blocks until a response arrives or timeout expires
* **Commands and Queries** — separate semantics for writes and reads following CQRS
* **Response caching** — server-side caching for queries with configurable TTL
* **Load balancing** — distribute requests across multiple responders using queue groups
* **Configurable timeouts** — per-request timeout in milliseconds
* **gRPC and REST** — use any transport protocol

## Quick Example [#quick-example]

<Tabs groupId="language" items="['Go', 'Python', 'Node.js', 'Java', 'C#', 'Kotlin', 'C++', 'Rust', 'Ruby', 'Elixir']">
  <Tab value="Go">
    ```go title="send_command.go"
    package main

    import (
        "context"
        "log"
        "time"

        "github.com/kubemq-io/kubemq-go/v2"
    )

    func main() {
        ctx := context.Background()
        client, err := kubemq.NewClient(ctx,
            kubemq.WithAddress("localhost", 50000),
        )
        if err != nil {
            log.Fatal(err)
        }
        defer client.Close()

        resp, err := client.SendCommand(ctx, kubemq.NewCommand().
            SetChannel("orders.process").
            SetBody([]byte(`{"action":"create","orderId":"ORD-1234"}`)).
            SetTimeout(10 * time.Second))
        if err != nil {
            log.Fatal(err)
        }
        log.Printf("Command executed: %v", resp.Executed)
    }
    ```
  </Tab>

  <Tab value="Python">
    ```python title="send_command.py"
    from kubemq.cq import Client as CQClient
    from kubemq.cq import CommandMessage

    client = CQClient(address="localhost:50000")
    response = client.send_command(
        CommandMessage(
            channel="orders.process",
            body=b'{"action":"create","orderId":"ORD-1234"}',
            timeout_in_seconds=10,
        )
    )
    print(f"Command executed: {response.is_executed}")
    client.close()
    ```
  </Tab>

  <Tab value="Node.js">
    ```javascript title="send_command.js"
    const { KubeMQClient } = require("kubemq-js");

    const client = new KubeMQClient({ address: "localhost:50000" });

    const response = await client.sendCommand({
      channel: "orders.process",
      body: Buffer.from(JSON.stringify({ action: "create", orderId: "ORD-1234" })),
      timeoutInSeconds: 10,
    });
    console.log("Command executed:", response.isExecuted);
    ```
  </Tab>

  <Tab value="Java">
    ```java title="SendCommand.java"
    CQClient client = CQClient.builder()
        .address("localhost:50000")
        .clientId("order-service")
        .build();

    CommandResponseMessage response = client.sendCommandRequest(
        CommandMessage.builder()
            .channel("orders.process")
            .body("{\"action\":\"create\",\"orderId\":\"ORD-1234\"}".getBytes())
            .timeout(10000)
            .build());
    System.out.println("Executed: " + response.isExecuted());
    client.close();
    ```
  </Tab>

  <Tab value="C#">
    ```csharp title="SendCommand.cs"
    await using var client = new KubeMQClient(new KubeMQClientOptions());
    await client.ConnectAsync();

    var response = await client.SendCommandAsync(new CommandMessage
    {
        Channel = "orders.process",
        Body = Encoding.UTF8.GetBytes("{\"action\":\"create\",\"orderId\":\"ORD-1234\"}"),
        Timeout = TimeSpan.FromSeconds(10)
    });
    Console.WriteLine($"Executed: {response.IsExecuted}");
    ```
  </Tab>

  <Tab value="Kotlin">
    ```kotlin title="SendCommand.kt"
    val client = CQClient("localhost:50000")

    val response = client.sendCommand(CommandMessage(
        channel = "orders.process",
        body = """{"action":"create","orderId":"ORD-1234"}""".toByteArray(),
        timeout = 10000
    ))
    println("Command executed: ${response.isExecuted}")
    client.close()
    ```
  </Tab>

  <Tab value="C++">
    ```cpp title="send_command.cpp"
    #include <kubemq/client.h>
    #include <iostream>

    auto client = kubemq::CQClient("localhost:50000");

    kubemq::CommandMessage cmd;
    cmd.channel = "orders.process";
    cmd.body = R"({"action":"create","orderId":"ORD-1234"})";
    cmd.timeout = 10000;

    auto response = client.sendCommand(cmd);
    std::cout << "Command executed: " << response.isExecuted << std::endl;
    ```
  </Tab>

  <Tab value="Rust">
    ```rust title="send_command.rs"
    use kubemq::prelude::*;
    use kubemq::CommandBuilder;
    use std::time::Duration;

    #[tokio::main]
    async fn main() -> kubemq::Result<()> {
        let client = KubemqClient::builder()
            .host("localhost")
            .port(50000)
            .build()
            .await?;

        let command = CommandBuilder::new()
            .channel("orders.process")
            .body(br#"{"action":"create","orderId":"ORD-1234"}"#.to_vec())
            .timeout(Duration::from_secs(10))
            .build();

        let response = client.send_command(command).await?;
        println!("Command executed: {}", response.executed);

        client.close().await?;
        Ok(())
    }
    ```
  </Tab>

  <Tab value="Ruby">
    ```ruby title="send_command.rb"
    require 'kubemq'

    client = KubeMQ::CQClient.new(address: 'localhost:50000', client_id: 'order-service')

    msg = KubeMQ::CQ::CommandMessage.new(
      channel: 'orders.process',
      body: '{"action":"create","orderId":"ORD-1234"}',
      timeout: 10
    )
    result = client.send_command(msg)
    puts "Command executed: #{result.executed}"

    client.close
    ```
  </Tab>

  <Tab value="Elixir">
    ```elixir title="send_command.exs"
    {:ok, client} =
      KubeMQ.Client.start_link(address: "localhost:50000", client_id: "order-service")

    command =
      KubeMQ.Command.new(
        channel: "orders.process",
        body: ~s({"action":"create","orderId":"ORD-1234"}),
        timeout: 10_000
      )

    case KubeMQ.Client.send_command(client, command) do
      {:ok, response} -> IO.puts("Command executed: #{response.executed}")
      {:error, err} -> IO.puts("Command failed: #{err.message}")
    end

    KubeMQ.Client.close(client)
    ```
  </Tab>
</Tabs>

## When to Use RPC [#when-to-use-rpc]

| Scenario                     | RPC                  | Events / Queues        |
| ---------------------------- | -------------------- | ---------------------- |
| Service-to-service API calls | ✅ Best choice        | Not suitable           |
| Data lookups and reads       | ✅ Queries            | Possible but awkward   |
| Write confirmations          | ✅ Commands           | Queues with ack        |
| CQRS implementation          | ✅ Commands + Queries | Events for projections |
| Fire-and-forget broadcasts   | ❌ Blocks on response | ✅ Use Events           |
| Reliable async processing    | ❌ Blocks on response | ✅ Use Queues           |

<Callout type="info">
  Need fire-and-forget delivery? Use [Events](/learn/events) for broadcasts or [Queues](/learn/queues) for reliable processing.
</Callout>

<Callout type="info">
  Commands and queries are also available via the [CloudEvents protocol](/connectors/cloudevents/how-to/commands-queries) — use any language with a CloudEvents SDK, no KubeMQ client library needed.
</Callout>

## Learn More [#learn-more]

<Cards>
  <Card title="Getting Started" href="/learn/rpc/getting-started" description="Send your first command and query in 5 minutes." />

  <Card title="Send Commands" href="/learn/rpc/tutorials/send-commands" description="Fire-and-confirm commands with execution status." />

  <Card title="Send Queries" href="/learn/rpc/tutorials/send-queries" description="Queries with full response data and optional caching." />

  <Card title="Handle Commands" href="/learn/rpc/tutorials/handle-commands" description="Build a command responder for incoming requests." />

  <Card title="Query Caching" href="/learn/rpc/tutorials/query-caching" description="Server-side response caching with configurable TTL." />

  <Card title="Load Balancing" href="/learn/rpc/how-to/load-balancing" description="Distribute requests across multiple responders." />

  <Card title="Reference" href="/learn/rpc/reference" description="Request/response structure, caching, and error codes." />
</Cards>
