0Pricing
C# Academy · Lesson

Unary & Server Streaming RPCs

Implement unary calls and server-side streaming to push data from server to client in real time.

Unary & Server Streaming RPCs is a free C# Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C# Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

gRPC RPC Types

gRPC supports four RPC patterns: Unary (request/response), Server Streaming (client sends one, server sends many), Client Streaming, and Bidirectional Streaming. This lesson covers the first two.

Unary RPC: The Basic Pattern

Unary is the simplest pattern — one request, one response. It looks like a regular function call but travels over HTTP/2 with Protobuf encoding.

// .proto definition
service OrderService {
  rpc GetOrder (GetOrderRequest) returns (OrderResponse);
}

message GetOrderRequest { int32 id = 1; }
message OrderResponse   { int32 id = 1; string status = 2; double total = 3; }

Implementing a Unary RPC

Override the generated base method. The ServerCallContext provides metadata, cancellation token, deadline, and peer info.

public class OrderService : OrderService.OrderServiceBase
{
    private readonly IOrderRepository _repo;
    public OrderService(IOrderRepository repo) => _repo = repo;

    public override async Task<OrderResponse> GetOrder(
        GetOrderRequest request,
        ServerCallContext context)
    {
        var order = await _repo.GetByIdAsync(request.Id, context.CancellationToken);
        if (order is null)
            throw new RpcException(new Status(StatusCode.NotFound, "Order not found"));

        return new OrderResponse
        {
            Id     = order.Id,
            Status = order.Status.ToString(),
            Total  = (double)order.Total
        };
    }
}

Calling a Unary RPC from a Client

The generated client provides both synchronous-looking and truly async methods. Always use the Async variant in production.

var client = new OrderService.OrderServiceClient(channel);

// Async call with cancellation
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var reply = await client.GetOrderAsync(
    new GetOrderRequest { Id = 42 },
    cancellationToken: cts.Token);

Console.WriteLine($"Order {reply.Id}: {reply.Status} - ${reply.Total}");

Server Streaming: Proto Definition

Server streaming adds the stream keyword before the response type. The server sends multiple messages on the same connection before closing the stream.

service StockService {
  // Client sends one symbol, server streams price updates
  rpc WatchStock (WatchRequest) returns (stream StockUpdate);
}

message WatchRequest  { string symbol = 1; }
message StockUpdate   { string symbol = 1; double price = 2; int64 timestamp = 3; }

Implementing Server Streaming

Use IServerStreamWriter<T> to send messages. The method returns a Task — write until done, then return.

public override async Task WatchStock(
    WatchRequest request,
    IServerStreamWriter<StockUpdate> responseStream,
    ServerCallContext context)
{
    while (!context.CancellationToken.IsCancellationRequested)
    {
        var price = await _market.GetPriceAsync(request.Symbol);

        await responseStream.WriteAsync(new StockUpdate
        {
            Symbol    = request.Symbol,
            Price     = price,
            Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
        });

        await Task.Delay(1000, context.CancellationToken);
    }
}

Consuming a Server Stream from the Client

Use await foreach on ResponseStream.ReadAllAsync() to process each message as it arrives.

using var call = client.WatchStock(
    new WatchRequest { Symbol = "MSFT" });

await foreach (var update in call.ResponseStream.ReadAllAsync())
{
    Console.WriteLine($"{update.Symbol}: ${update.Price:F2}");
}
// Continues until server closes the stream or client cancels

Cancelling a Server Stream

Pass a CancellationToken when opening the call to stop the stream client-side. The server receives the cancellation via its own token.

var cts = new CancellationTokenSource();

// Cancel after 30 seconds
cts.CancelAfter(TimeSpan.FromSeconds(30));

using var call = client.WatchStock(
    new WatchRequest { Symbol = "AAPL" },
    cancellationToken: cts.Token);

try
{
    await foreach (var update in
        call.ResponseStream.ReadAllAsync(cts.Token))
    {
        Console.WriteLine($"Price: {update.Price}");
    }
}
catch (OperationCanceledException)
{
    Console.WriteLine("Stream cancelled.");
}

Metadata: Request Headers and Trailers

gRPC supports metadata (key-value pairs) sent as headers before the response and trailers after the response. Use them for auth tokens, tracing IDs, or pagination cursors.

// Server: send initial metadata
await context.WriteResponseHeadersAsync(new Metadata
{
    { "x-correlation-id", Guid.NewGuid().ToString() }
});

// Server: set trailing metadata
context.ResponseTrailers.Add("x-total-count", "1500");

// Client: read headers
var headers  = await call.ResponseHeadersAsync;
var trailerId = headers.GetValue("x-correlation-id");

gRPC-Web for Browser Clients

Browsers can't use raw gRPC (HTTP/2 trailers not supported). Use Grpc.AspNetCore.Web and the grpc-web JavaScript client or Blazor's Grpc.Net.Client.Web.

// dotnet add package Grpc.AspNetCore.Web

app.UseGrpcWeb();
app.MapGrpcService<GreeterService>().EnableGrpcWeb();

// Blazor client:
var handler = new GrpcWebHandler(GrpcWebMode.GrpcWeb,
    new HttpClientHandler());
using var channel = GrpcChannel.ForAddress(
    "https://localhost:7042",
    new GrpcChannelOptions { HttpHandler = handler });

Real-World: Log Streaming Service

A log aggregation service streams application log entries to connected monitoring clients in real time using server streaming.

public override async Task StreamLogs(
    LogStreamRequest request,
    IServerStreamWriter<LogEntry> responseStream,
    ServerCallContext context)
{
    await foreach (var log in _logChannel.Reader.ReadAllAsync(
        context.CancellationToken))
    {
        if (log.Level >= request.MinLevel)
        {
            await responseStream.WriteAsync(new LogEntry
            {
                Level   = log.Level.ToString(),
                Message = log.Message,
                Timestamp = Timestamp.FromDateTime(log.Timestamp)
            });
        }
    }
}

Quick Check

What keyword in a .proto file indicates a server streaming RPC?

Recap: Unary & Server Streaming RPCs

Key takeaways:

  • Unary RPC: one request, one response — the most common pattern
  • Server streaming: one request, multiple response messages over time
  • Implement server streaming by writing to IServerStreamWriter<T>
  • Consume server streams with await foreach on ReadAllAsync()
  • Use CancellationToken to stop streams cleanly from either side
  • gRPC-Web allows browser clients to call gRPC services

Frequently asked questions

Is the “Unary & Server Streaming RPCs” lesson free?

Yes — the full text of “Unary & Server Streaming RPCs” is free to read here on the web, and the C# Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C# Academy course, upgrade to CoddyKit PRO.

What will I learn in “Unary & Server Streaming RPCs”?

Implement unary calls and server-side streaming to push data from server to client in real time. You practise C# Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start C# Academy?

No prior experience is required. C# Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Unary & Server Streaming RPCs” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this C# Academy lesson?

Yes. Every C# Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. gRPC & Protobuf Fundamentals
  2. Unary & Server Streaming RPCs
  3. Client & Bidirectional Streaming
  4. Deadlines, Cancellation & Interceptors
← Back to C# Academy