# Getting Started with RPC (/learn/rpc/getting-started)



<Callout type="info">
  **Prerequisites:** KubeMQ server running on `localhost:50000` and your SDK installed. See [Getting Started](/deploy) for setup.
</Callout>

## What You Will Build [#what-you-will-build]

A command sender that creates an order and a query sender that retrieves order status — each paired with a responder that handles the request and returns a response.

## Steps [#steps]

<Steps>
  <Step>
    ### Install the SDK [#install-the-sdk]

    <Tabs groupId="language" items="['Go', 'Python', 'Node.js', 'Java', 'C#', 'Kotlin', 'C++', 'Rust', 'Ruby', 'Elixir']">
      <Tab value="Go">
        ```bash
        go get github.com/kubemq-io/kubemq-go/v2
        ```
      </Tab>

      <Tab value="Python">
        ```bash
        pip install kubemq
        ```
      </Tab>

      <Tab value="Node.js">
        ```bash
        npm install kubemq-js
        ```
      </Tab>

      <Tab value="Java">
        ```xml
        <dependency>
            <groupId>io.kubemq.sdk</groupId>
            <artifactId>kubemq-sdk-Java</artifactId>
            <version>2.1.1</version>
        </dependency>
        ```
      </Tab>

      <Tab value="C#">
        ```bash
        dotnet add package KubeMQ.SDK.CSharp
        ```
      </Tab>

      <Tab value="Kotlin">
        ```kotlin
        implementation("io.kubemq.sdk:kubemq-sdk-kotlin:2.1.0")
        ```
      </Tab>

      <Tab value="C++">
        ```bash
        vcpkg install kubemq
        ```
      </Tab>

      <Tab value="Rust">
        ```toml
        [dependencies]
        kubemq = "1.0"
        tokio = { version = "1", features = ["full"] }
        ```
      </Tab>

      <Tab value="Ruby">
        ```bash
        gem install kubemq
        ```
      </Tab>

      <Tab value="Elixir">
        ```elixir
        def deps do
          [{:kubemq, "~> 1.0"}]
        end
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step>
    ### Create a Command Responder [#create-a-command-responder]

    Start the responder first. It subscribes to incoming commands on `orders.process`, processes them, and sends back an execution status.

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

        import (
            "context"
            "fmt"
            "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()

            sub, err := client.SubscribeToCommands(ctx, "orders.process", "",
                kubemq.WithOnCommandReceive(func(cmd *kubemq.CommandReceive) {
                    fmt.Printf("Received command: %s\n", cmd.Body)
                    resp := kubemq.NewCommandReply().
                        SetRequestId(cmd.Id).
                        SetResponseTo(cmd.ResponseTo).
                        SetExecutedAt(time.Now())
                    _ = client.SendCommandResponse(ctx, resp)
                }),
                kubemq.WithOnError(func(err error) {
                    log.Println("Error:", err)
                }),
            )
            if err != nil {
                log.Fatal(err)
            }
            defer sub.Unsubscribe()

            fmt.Println("Command responder listening on 'orders.process'...")
            <-ctx.Done()
        }
        ```
      </Tab>

      <Tab value="Python">
        ```python title="command_responder.py"
        import time
        from kubemq.cq import Client as CQClient
        from kubemq.cq import CommandsSubscription, CommandReceived, CommandResponse, CancellationToken

        def on_command(request: CommandReceived) -> None:
            print(f"Received command: {request.body.decode('utf-8')}")
            client.send_response_message(
                CommandResponse(command_received=request, is_executed=True)
            )

        def on_error(err):
            print(f"Error: {err}")

        client = CQClient(address="localhost:50000")
        cancel = CancellationToken()

        client.subscribe_to_commands(
            subscription=CommandsSubscription(
                channel="orders.process",
                on_receive_command_callback=on_command,
                on_error_callback=on_error,
            ),
            cancel=cancel,
        )
        print("Command responder listening on 'orders.process'...")
        time.sleep(3600)
        ```
      </Tab>

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

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

        client.subscribeToCommands({
          channel: "orders.process",
          onCommand: (cmd) => {
            console.log("Received command:", Buffer.from(cmd.body).toString());
            client.sendCommandResponse({ requestId: cmd.id, isExecuted: true });
          },
          onError: (err) => console.error("Error:", err.message),
        });

        console.log("Command responder listening on 'orders.process'...");
        ```
      </Tab>

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

        client.subscribeToCommands(CommandsSubscription.builder()
            .channel("orders.process")
            .onReceiveCommandCallback(cmd -> {
                System.out.println("Received: " + new String(cmd.getBody()));
                return CommandResponseMessage.builder()
                    .requestId(cmd.getId())
                    .isExecuted(true)
                    .build();
            })
            .onErrorCallback(err -> System.err.println("Error: " + err.getMessage()))
            .build());

        System.out.println("Command responder listening on 'orders.process'...");
        Thread.sleep(3600000);
        client.close();
        ```
      </Tab>

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

        Console.WriteLine("Command responder listening on 'orders.process'...");
        await foreach (var cmd in client.SubscribeToCommandsAsync(
            new CommandsSubscription { Channel = "orders.process" }))
        {
            Console.WriteLine($"Received: {Encoding.UTF8.GetString(cmd.Body.Span)}");
            await client.SendCommandResponseAsync(new CommandResponse
            {
                RequestId = cmd.Id,
                IsExecuted = true
            });
        }
        ```
      </Tab>

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

        client.subscribeToCommands(
            channel = "orders.process",
            onCommand = { cmd ->
                println("Received command: ${String(cmd.body)}")
                client.sendCommandResponse(
                    requestId = cmd.id, isExecuted = true
                )
            },
            onError = { err -> System.err.println("Error: ${err.message}") }
        )

        println("Command responder listening on 'orders.process'...")
        Thread.sleep(3600000)
        client.close()
        ```
      </Tab>

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

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

        client.subscribeToCommands("orders.process", "",
            [&client](const kubemq::CommandReceive& cmd) {
                std::cout << "Received command: " << cmd.body << std::endl;
                client.sendCommandResponse(cmd.id, true);
            },
            [](const std::string& err) {
                std::cerr << "Error: " << err << std::endl;
            }
        );

        std::cout << "Command responder listening on 'orders.process'..." << std::endl;
        std::this_thread::sleep_for(std::chrono::hours(1));
        ```
      </Tab>

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

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

            let rc = client.clone();
            let sub = client
                .subscribe_to_commands(
                    "orders.process",
                    "",
                    move |cmd| {
                        let c = rc.clone();
                        Box::pin(async move {
                            println!("Received command: {}", String::from_utf8_lossy(&cmd.body));
                            let reply = CommandReplyBuilder::new()
                                .request_id(&cmd.id)
                                .response_to(&cmd.response_to)
                                .build();
                            tokio::spawn(async move {
                                let _ = c.send_command_response(reply).await;
                            });
                        })
                    },
                    None,
                )
                .await?;

            println!("Command responder listening on 'orders.process'...");
            tokio::signal::ctrl_c().await.ok();
            sub.unsubscribe().await;
            client.close().await?;
            Ok(())
        }
        ```
      </Tab>

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

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

        sub = KubeMQ::CQ::CommandsSubscription.new(channel: 'orders.process')
        client.subscribe_to_commands(sub, cancellation_token: cancel,
          on_error: ->(e) { puts "Error: #{e.message}" }) do |cmd|
          puts "Received command: #{cmd.body}"
          response = KubeMQ::CQ::CommandResponseMessage.new(
            request_id: cmd.id,
            reply_channel: cmd.reply_channel,
            executed: true
          )
          client.send_response(response)
        end

        puts "Command responder listening on 'orders.process'..."
        cancel.wait
        ```
      </Tab>

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

        {:ok, _sub} =
          KubeMQ.Client.subscribe_to_commands(client, "orders.process",
            on_command: fn cmd ->
              IO.puts("Received command: #{cmd.body}")

              KubeMQ.CommandReply.new(
                request_id: cmd.id,
                response_to: cmd.reply_channel,
                executed: true
              )
            end,
            on_error: fn err -> IO.puts("Error: #{err.message}") end
          )

        IO.puts("Command responder listening on 'orders.process'...")
        Process.sleep(:infinity)
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step>
    ### Send a Command [#send-a-command]

    In a separate terminal, send a command. The sender blocks until the responder replies or the timeout expires.

    <Tabs groupId="language" items="['Go', 'Python', 'Node.js', 'Java', 'C#', 'Kotlin', 'C++', 'Rust', 'Ruby', 'Elixir']">
      <Tab value="Go">
        ```go title="send_command.go"
        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("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

        with CQClient(address="localhost:50000") as client:
            response = client.send_command(
                CommandMessage(
                    channel="orders.process",
                    body=b'{"action":"create","orderId":"ORD-1234"}',
                    timeout_in_seconds=10,
                )
            )
            print(f"Executed: {response.is_executed}")
        ```
      </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("Executed:", response.isExecuted);
        ```
      </Tab>

      <Tab value="Java">
        ```java title="SendCommand.java"
        CQClient client = CQClient.builder()
            .address("localhost:50000")
            .clientId("order-sender")
            .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("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 << "Executed: " << response.isExecuted << std::endl;
        ```
      </Tab>

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

        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!("Executed: {}", response.executed);
        client.close().await?;
        ```
      </Tab>

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

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

        msg = KubeMQ::CQ::CommandMessage.new(
          channel: 'orders.process',
          timeout: 10,
          body: '{"action":"create","orderId":"ORD-1234"}'
        )
        result = client.send_command(msg)
        puts "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-sender")

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

        {:ok, response} = KubeMQ.Client.send_command(client, command)
        IO.puts("Executed: #{response.executed}")
        KubeMQ.Client.close(client)
        ```
      </Tab>
    </Tabs>

    <Callout type="info">
      **Expected output (sender):** `Executed: true`

      **Expected output (responder):** `Received command: {"action":"create","orderId":"ORD-1234"}`
    </Callout>
  </Step>

  <Step>
    ### Create a Query Responder [#create-a-query-responder]

    Queries work like commands but the responder can include data in the response body. Set up a query responder on `orders.lookup`.

    <Tabs groupId="language" items="['Go', 'Python', 'Node.js', 'Java', 'C#', 'Kotlin', 'C++', 'Rust', 'Ruby', 'Elixir']">
      <Tab value="Go">
        ```go title="query_responder.go"
        sub, err := client.SubscribeToQueries(ctx, "orders.lookup", "",
            kubemq.WithOnQueryReceive(func(query *kubemq.QueryReceive) {
                fmt.Printf("Query for: %s\n", query.Body)
                resp := kubemq.NewQueryReply().
                    SetRequestId(query.Id).
                    SetResponseTo(query.ResponseTo).
                    SetBody([]byte(`{"orderId":"ORD-1234","status":"shipped","total":99.99}`)).
                    SetExecutedAt(time.Now())
                _ = client.SendQueryResponse(ctx, resp)
            }),
            kubemq.WithOnError(func(err error) {
                log.Println("Error:", err)
            }),
        )
        ```
      </Tab>

      <Tab value="Python">
        ```python title="query_responder.py"
        from kubemq.cq import QueriesSubscription, QueryReceived, QueryResponse

        def on_query(request: QueryReceived) -> None:
            print(f"Query for: {request.body.decode('utf-8')}")
            client.send_response_message(
                QueryResponse(
                    query_received=request,
                    is_executed=True,
                    body=b'{"orderId":"ORD-1234","status":"shipped","total":99.99}',
                )
            )

        client.subscribe_to_queries(
            subscription=QueriesSubscription(
                channel="orders.lookup",
                on_receive_query_callback=on_query,
                on_error_callback=on_error,
            ),
            cancel=cancel,
        )
        ```
      </Tab>

      <Tab value="Node.js">
        ```javascript title="query_responder.js"
        client.subscribeToQueries({
          channel: "orders.lookup",
          onQuery: (query) => {
            console.log("Query for:", Buffer.from(query.body).toString());
            client.sendQueryResponse({
              requestId: query.id,
              isExecuted: true,
              body: Buffer.from(
                JSON.stringify({ orderId: "ORD-1234", status: "shipped", total: 99.99 })
              ),
            });
          },
          onError: (err) => console.error("Error:", err.message),
        });
        ```
      </Tab>

      <Tab value="Java">
        ```java title="QueryResponder.java"
        client.subscribeToQueries(QueriesSubscription.builder()
            .channel("orders.lookup")
            .onReceiveQueryCallback(query -> {
                System.out.println("Query for: " + new String(query.getBody()));
                return QueryResponseMessage.builder()
                    .requestId(query.getId())
                    .isExecuted(true)
                    .body("{\"orderId\":\"ORD-1234\",\"status\":\"shipped\",\"total\":99.99}".getBytes())
                    .build();
            })
            .onErrorCallback(err -> System.err.println("Error: " + err.getMessage()))
            .build());
        ```
      </Tab>

      <Tab value="C#">
        ```csharp title="QueryResponder.cs"
        await foreach (var query in client.SubscribeToQueriesAsync(
            new QueriesSubscription { Channel = "orders.lookup" }))
        {
            Console.WriteLine($"Query for: {Encoding.UTF8.GetString(query.Body.Span)}");
            await client.SendQueryResponseAsync(new QueryResponse
            {
                RequestId = query.Id,
                IsExecuted = true,
                Body = Encoding.UTF8.GetBytes(
                    "{\"orderId\":\"ORD-1234\",\"status\":\"shipped\",\"total\":99.99}")
            });
        }
        ```
      </Tab>

      <Tab value="Kotlin">
        ```kotlin title="QueryResponder.kt"
        client.subscribeToQueries(
            channel = "orders.lookup",
            onQuery = { query ->
                println("Query for: ${String(query.body)}")
                client.sendQueryResponse(
                    requestId = query.id,
                    isExecuted = true,
                    body = """{"orderId":"ORD-1234","status":"shipped","total":99.99}""".toByteArray()
                )
            },
            onError = { err -> System.err.println("Error: ${err.message}") }
        )
        ```
      </Tab>

      <Tab value="C++">
        ```cpp title="query_responder.cpp"
        client.subscribeToQueries("orders.lookup", "",
            [&client](const kubemq::QueryReceive& query) {
                std::cout << "Query for: " << query.body << std::endl;
                client.sendQueryResponse(query.id, true,
                    R"({"orderId":"ORD-1234","status":"shipped","total":99.99})");
            },
            [](const std::string& err) {
                std::cerr << "Error: " << err << std::endl;
            }
        );
        ```
      </Tab>

      <Tab value="Rust">
        ```rust title="query_responder.rs"
        use kubemq::QueryReplyBuilder;

        let rc = client.clone();
        let sub = client
            .subscribe_to_queries(
                "orders.lookup",
                "",
                move |query| {
                    let c = rc.clone();
                    Box::pin(async move {
                        println!("Query for: {}", String::from_utf8_lossy(&query.body));
                        let reply = QueryReplyBuilder::new()
                            .request_id(&query.id)
                            .response_to(&query.response_to)
                            .body(br#"{"orderId":"ORD-1234","status":"shipped","total":99.99}"#.to_vec())
                            .build();
                        tokio::spawn(async move {
                            let _ = c.send_query_response(reply).await;
                        });
                    })
                },
                None,
            )
            .await?;
        ```
      </Tab>

      <Tab value="Ruby">
        ```ruby title="query_responder.rb"
        sub = KubeMQ::CQ::QueriesSubscription.new(channel: 'orders.lookup')
        client.subscribe_to_queries(sub, cancellation_token: cancel,
          on_error: ->(e) { puts "Error: #{e.message}" }) do |query|
          puts "Query for: #{query.body}"
          response = KubeMQ::CQ::QueryResponseMessage.new(
            request_id: query.id,
            reply_channel: query.reply_channel,
            executed: true,
            body: '{"orderId":"ORD-1234","status":"shipped","total":99.99}'
          )
          client.send_response(response)
        end
        ```
      </Tab>

      <Tab value="Elixir">
        ```elixir title="query_responder.exs"
        {:ok, _sub} =
          KubeMQ.Client.subscribe_to_queries(client, "orders.lookup",
            on_query: fn query ->
              IO.puts("Query for: #{query.body}")

              KubeMQ.QueryReply.new(
                request_id: query.id,
                response_to: query.reply_channel,
                executed: true,
                body: ~s({"orderId":"ORD-1234","status":"shipped","total":99.99})
              )
            end,
            on_error: fn err -> IO.puts("Error: #{err.message}") end
          )
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step>
    ### Send a Query [#send-a-query]

    Send a query to retrieve order data. Unlike commands, the response body is preserved.

    <Tabs groupId="language" items="['Go', 'Python', 'Node.js', 'Java', 'C#', 'Kotlin', 'C++', 'Rust', 'Ruby', 'Elixir']">
      <Tab value="Go">
        ```go title="send_query.go"
        resp, err := client.SendQuery(ctx, kubemq.NewQuery().
            SetChannel("orders.lookup").
            SetBody([]byte("ORD-1234")).
            SetTimeout(10 * time.Second))
        if err != nil {
            log.Fatal(err)
        }
        log.Printf("Order data: %s", resp.Body)
        ```
      </Tab>

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

        with CQClient(address="localhost:50000") as client:
            response = client.send_query(
                QueryMessage(
                    channel="orders.lookup",
                    body=b"ORD-1234",
                    timeout_in_seconds=10,
                )
            )
            print(f"Order data: {response.body.decode('utf-8')}")
        ```
      </Tab>

      <Tab value="Node.js">
        ```javascript title="send_query.js"
        const response = await client.sendQuery({
          channel: "orders.lookup",
          body: Buffer.from("ORD-1234"),
          timeoutInSeconds: 10,
        });
        console.log("Order data:", Buffer.from(response.body).toString());
        ```
      </Tab>

      <Tab value="Java">
        ```java title="SendQuery.java"
        QueryResponseMessage response = client.sendQueryRequest(
            QueryMessage.builder()
                .channel("orders.lookup")
                .body("ORD-1234".getBytes())
                .timeout(10000)
                .build());
        System.out.println("Order data: " + new String(response.getBody()));
        ```
      </Tab>

      <Tab value="C#">
        ```csharp title="SendQuery.cs"
        var response = await client.SendQueryAsync(new QueryMessage
        {
            Channel = "orders.lookup",
            Body = Encoding.UTF8.GetBytes("ORD-1234"),
            Timeout = TimeSpan.FromSeconds(10)
        });
        Console.WriteLine($"Order data: {Encoding.UTF8.GetString(response.Body.Span)}");
        ```
      </Tab>

      <Tab value="Kotlin">
        ```kotlin title="SendQuery.kt"
        val response = client.sendQuery(QueryMessage(
            channel = "orders.lookup",
            body = "ORD-1234".toByteArray(),
            timeout = 10000
        ))
        println("Order data: ${String(response.body)}")
        ```
      </Tab>

      <Tab value="C++">
        ```cpp title="send_query.cpp"
        kubemq::QueryMessage query;
        query.channel = "orders.lookup";
        query.body = "ORD-1234";
        query.timeout = 10000;

        auto response = client.sendQuery(query);
        std::cout << "Order data: " << response.body << std::endl;
        ```
      </Tab>

      <Tab value="Rust">
        ```rust title="send_query.rs"
        use kubemq::QueryBuilder;
        use std::time::Duration;

        let query = QueryBuilder::new()
            .channel("orders.lookup")
            .body(b"ORD-1234".to_vec())
            .timeout(Duration::from_secs(10))
            .build();

        let response = client.send_query(query).await?;
        println!("Order data: {}", String::from_utf8_lossy(&response.body));
        ```
      </Tab>

      <Tab value="Ruby">
        ```ruby title="send_query.rb"
        msg = KubeMQ::CQ::QueryMessage.new(
          channel: 'orders.lookup',
          timeout: 10,
          body: 'ORD-1234'
        )
        result = client.send_query(msg)
        puts "Order data: #{result.body}"
        ```
      </Tab>

      <Tab value="Elixir">
        ```elixir title="send_query.exs"
        query =
          KubeMQ.Query.new(
            channel: "orders.lookup",
            body: "ORD-1234",
            timeout: 10_000
          )

        {:ok, response} = KubeMQ.Client.send_query(client, query)
        IO.puts("Order data: #{response.body}")
        ```
      </Tab>
    </Tabs>

    <Callout type="info">
      Unlike commands, query responses preserve the full response body and metadata. This makes queries ideal for data retrieval operations.
    </Callout>
  </Step>

  <Step>
    ### Run the Example [#run-the-example]

    1. Start the **responder** in one terminal (handles both commands and queries)
    2. Run the **command sender** in a separate terminal
    3. Run the **query sender** in a separate terminal
    4. Observe the responses in each terminal
  </Step>
</Steps>

## What Just Happened [#what-just-happened]

<Mermaid
  chart="sequenceDiagram
    participant S as Sender
    participant K as KubeMQ
    participant R as Responder

    R->>K: subscribe(&#x22;orders.process&#x22;)
    S->>K: sendCommand(&#x22;orders.process&#x22;, body)
    K->>R: deliver command
    R->>K: response(Executed: true)
    K->>S: deliver response

    R->>K: subscribe(&#x22;orders.lookup&#x22;)
    S->>K: sendQuery(&#x22;orders.lookup&#x22;, &#x22;ORD-1234&#x22;)
    K->>R: deliver query
    R->>K: response(body: order data)
    K->>S: deliver response with body"
/>

1. The **responder** subscribed to both command and query channels
2. The **sender** sent a command — the responder processed it and returned `Executed: true` (response body stripped)
3. The **sender** sent a query — the responder returned the full order data in the response body (preserved)

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

| Aspect                | Command                    | Query                          |
| --------------------- | -------------------------- | ------------------------------ |
| **Response body**     | Stripped (always `nil`)    | Preserved                      |
| **Response metadata** | Stripped                   | Preserved                      |
| **CacheHit field**    | Stripped                   | Preserved                      |
| **Use for**           | Writes, mutations, actions | Reads, lookups, data retrieval |

## Next Steps [#next-steps]

<Cards>
  <Card title="Send Commands" href="/learn/rpc/tutorials/send-commands" description="Deep dive into command patterns with error handling." />

  <Card title="Send Queries" href="/learn/rpc/tutorials/send-queries" description="Query patterns with response data handling." />

  <Card title="Query Caching" href="/learn/rpc/tutorials/query-caching" description="Enable server-side response caching." />

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