gRPC Services

15 questions found

What is gRPC, and how does it differ from a traditional REST/JSON API?

Beginner
gRPC is a high-performance RPC (Remote Procedure Call) framework built on HTTP/2, using Protocol Buffers (protobuf) as its binary serialization format for both requests and responses, rather than JSON over HTTP/1.1 as typical REST APIs use. This gives gRPC significantly smaller message sizes (binary vs text), lower latency, native support for streaming (client, server, and bidirectional), and strongly-typed contracts generated from a .proto file -- at the cost of being less human-readable and requiring more tooling than plain REST/JSON, and having limited direct browser support without a proxy layer (gRPC-Web).
// order.proto -- defines the contract
service OrderService {
  rpc GetOrder (GetOrderRequest) returns (OrderResponse);
}
message GetOrderRequest { int32 order_id = 1; }
message OrderResponse { int32 id = 1; string status = 2; }
Real-world example A microservices platform uses gRPC for internal service-to-service communication (where performance and strong typing matter most) while still exposing a REST/JSON API at the edge for external, browser-based clients, using each protocol where it's best suited.

Common follow-ups: Why is gRPC less suitable for direct browser consumption without gRPC-Web?;How does protobuf's binary format achieve smaller payloads than JSON?

ASP.NET Core Middleware & Request Pipeline;Microservices & Distributed Architecture Patterns

How do you define a gRPC service contract using Protocol Buffers (.proto files), and how does code generation work in a .NET project?

Intermediate
A .proto file declares message types (structured data with numbered fields) and service definitions (RPC method signatures) using protobuf's interface definition language; the Grpc.Tools NuGet package's MSBuild integration automatically generates strongly-typed C# client and server base classes from the .proto file at build time, which you then implement (server) or call (client) directly in C#.
// Greeter.proto
syntax = "proto3";
service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest { string name = 1; }
message HelloReply { string message = 1; }

<!-- .csproj -->
<ItemGroup>
  <Protobuf Include="Protos\Greeter.proto" GrpcServices="Server" />
</ItemGroup>
Real-world example A team defines their service contract once in a shared .proto file, and both the C# server project and a separate C# client project reference it with different GrpcServices settings (Server vs Client), generating appropriately-typed code for each side automatically at build time.

Common follow-ups: What do the numbered fields in a proto message (like 'name = 1') actually mean and why do they matter for compatibility?

ASP.NET Core Middleware & Request Pipeline;.NET CLI SDK & Project Structure (csproj)

What are the four types of gRPC service methods (unary, server streaming, client streaming, bidirectional streaming), and when is each appropriate?

Advanced
Unary (single request, single response) is the most common, equivalent to a typical REST call. Server streaming (single request, stream of responses) suits scenarios like subscribing to live updates or paginating a large result set efficiently. Client streaming (stream of requests, single response) suits uploading data incrementally, like a large file in chunks. Bidirectional streaming (both sides stream independently and concurrently) suits real-time interactive scenarios like a chat application or live collaborative editing, where either side can send messages at any time.
service DataService {
  rpc GetItem (ItemRequest) returns (ItemResponse);                    // unary
  rpc StreamUpdates (SubscribeRequest) returns (stream Update);        // server streaming
  rpc UploadChunks (stream ChunkRequest) returns (UploadResult);       // client streaming
  rpc Chat (stream ChatMessage) returns (stream ChatMessage);          // bidirectional
}
Real-world example A real-time stock-trading platform uses server streaming for live price updates (one subscription, continuous stream of price ticks) and bidirectional streaming for an order-matching negotiation channel where both client and server need to send messages independently.

Common follow-ups: How does .NET's IAsyncEnumerable<T> map naturally to gRPC streaming methods?;What are the connection and resource lifetime implications of long-lived streaming calls?

Concurrency (asyncio/threading/multiprocessing);Diagnostics & Performance

How do you implement a gRPC server method in ASP.NET Core, and how does it integrate with the standard hosting model?

Intermediate
A gRPC service class extends the code-generated base class (from the .proto file) and overrides its virtual RPC methods, registered via MapGrpcService<T>() in the endpoint routing configuration -- gRPC services run on top of the same Kestrel/ASP.NET Core hosting infrastructure as regular web APIs, meaning they benefit from the same middleware pipeline, dependency injection, logging, and configuration systems.
public class GreeterService : Greeter.GreeterBase {
    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context) {
        return Task.FromResult(new HelloReply { Message = $"Hello, {request.Name}" });
    }
}

// Program.cs
builder.Services.AddGrpc();
app.MapGrpcService<GreeterService>();
Real-world example A team adds a gRPC service alongside their existing REST controllers in the same ASP.NET Core application, both benefiting from the same registered authentication middleware and dependency-injected services without any duplicated configuration.

Common follow-ups: Can a single ASP.NET Core app serve both REST and gRPC endpoints simultaneously?;How does dependency injection work within a gRPC service method?

Dependency Injection;ASP.NET Core Middleware & Request Pipeline

How does gRPC handle errors and status codes, and how does this differ from HTTP status codes in REST?

Advanced
gRPC uses its own set of standardized status codes (like NotFound, InvalidArgument, PermissionDenied, Internal, Unavailable) distinct from HTTP status codes, communicated via gRPC trailers rather than the HTTP status line -- a server method throws an RpcException with a specific StatusCode and optional detail message, which the client-side generated code surfaces as a corresponding exception, giving a richer, RPC-appropriate error vocabulary than REST's more general-purpose HTTP status codes.
public override Task<OrderResponse> GetOrder(GetOrderRequest request, ServerCallContext context) {
    var order = _repository.Find(request.OrderId);
    if (order == null) {
        throw new RpcException(new Status(StatusCode.NotFound, $"Order {request.OrderId} not found"));
    }
    return Task.FromResult(MapToResponse(order));
}

// Client-side:
try {
    var response = await client.GetOrderAsync(request);
} catch (RpcException ex) when (ex.StatusCode == StatusCode.NotFound) {
    // handle specifically
}
Real-world example A gRPC client library automatically translates a server-thrown RpcException with StatusCode.PermissionDenied into a client-side exception the calling application can catch and handle specifically, distinct from how it would handle a StatusCode.Unavailable indicating a transient network issue.

Common follow-ups: How do you attach custom error details (like field-level validation errors) to an RpcException?;How does gRPC status code mapping relate to HTTP status codes when gRPC runs over HTTP/2?

Exception Handling;Global Exception Handling & Middleware

What is gRPC-Web, and why is it needed for calling gRPC services directly from a browser-based JavaScript client?

Intermediate
Browsers don't provide the low-level HTTP/2 trailer access and full control needed for native gRPC's wire protocol, so gRPC-Web is a protocol variant designed to work within browser constraints (using standard HTTP requests the browser's fetch/XHR APIs can handle), requiring either a proxy (like Envoy) to translate between gRPC-Web and native gRPC, or ASP.NET Core's built-in Grpc.AspNetCore.Web middleware to natively accept gRPC-Web requests without a separate proxy.
builder.Services.AddGrpc();
var app = builder.Build();

app.UseGrpcWeb();  // enables gRPC-Web support directly, no separate proxy needed
app.MapGrpcService<GreeterService>().EnableGrpcWeb();
Real-world example A Blazor WebAssembly frontend calls a gRPC backend service directly using gRPC-Web support built into ASP.NET Core, avoiding the need to deploy and maintain a separate Envoy proxy just to bridge the browser-to-gRPC gap.

Common follow-ups: What specific browser limitations prevent native gRPC from working directly?;How does gRPC-Web's performance compare to native gRPC given the translation overhead?

ASP.NET Core Middleware & Request Pipeline;Blazor (Server & WebAssembly)

How does gRPC's use of HTTP/2 multiplexing provide performance advantages over HTTP/1.1-based REST APIs?

Advanced
HTTP/2 allows multiple concurrent requests and responses to be interleaved over a single TCP connection (multiplexing), eliminating HTTP/1.1's head-of-line blocking (where requests on the same connection must complete in order) and avoiding the overhead of establishing multiple TCP connections for concurrent calls -- combined with HTTP/2's header compression (HPACK) and gRPC's compact binary protobuf payloads, this results in meaningfully lower latency and higher throughput for high-volume service-to-service communication compared to typical REST/JSON over HTTP/1.1.
// A single HTTP/2 connection can carry many concurrent gRPC calls
// without head-of-line blocking, unlike sequential HTTP/1.1 requests:
var tasks = new[] {
    client.GetOrderAsync(request1),
    client.GetOrderAsync(request2),
    client.GetOrderAsync(request3)
};
await Task.WhenAll(tasks);  // all multiplexed over one connection efficiently
Real-world example A high-throughput internal service mesh migrates from REST/JSON to gRPC specifically to take advantage of HTTP/2 multiplexing, measurably reducing connection overhead and latency for services making hundreds of concurrent downstream calls per second.

Common follow-ups: What is HTTP/1.1 head-of-line blocking specifically, and how does HTTP/2 solve it?;Does gRPC's binary format meaningfully outperform JSON for typical payload sizes?

Diagnostics & Performance;Microservices & Distributed Architecture Patterns

How do you implement server-streaming gRPC using C#'s IAsyncEnumerable<T> or IServerStreamWriter<T>?

Intermediate
A server-streaming RPC method receives an IServerStreamWriter<TResponse> parameter, calling WriteAsync() repeatedly to send each item in the stream to the client as it becomes available, with the method completing (and thus the stream ending) when the method returns -- this maps naturally onto C#'s async streaming patterns, letting a server push results incrementally rather than requiring the client to wait for the entire result set to be computed before receiving anything.
public override async Task StreamUpdates(SubscribeRequest request, IServerStreamWriter<Update> responseStream, ServerCallContext context) {
    await foreach (var update in _updateSource.GetUpdatesAsync(context.CancellationToken)) {
        await responseStream.WriteAsync(update);
    }
}
Real-world example A live dashboard subscribes to a server-streaming gRPC method that pushes stock price updates to the client as they occur in real time, rather than requiring the client to repeatedly poll a unary endpoint for the latest price.

Common follow-ups: How does the client consume a server-streaming response using IAsyncEnumerable?;What happens to the stream if the client disconnects mid-stream?

Iterators & the Iterator Protocol;Concurrency (asyncio/threading/multiprocessing)

How does gRPC support interceptors for cross-cutting concerns like authentication, logging, and retries, similar to middleware in a REST pipeline?

Advanced
gRPC interceptors (implementing Interceptor, either server-side or client-side) wrap around RPC calls, letting you inject logic before and after the actual call executes -- server-side interceptors handle concerns like authentication/authorization and logging uniformly across all gRPC methods, while client-side interceptors handle concerns like automatic retry logic, adding auth headers, or client-side logging/metrics, registered globally so individual service methods don't need to repeat this logic.
public class LoggingInterceptor : Interceptor {
    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation) {
        _logger.LogInformation("Calling {Method}", context.Method);
        return await continuation(request, context);
    }
}

builder.Services.AddGrpc(options => options.Interceptors.Add<LoggingInterceptor>());
Real-world example A microservices platform registers a global server-side interceptor validating a JWT token on every incoming gRPC call, ensuring consistent authentication enforcement across dozens of gRPC service methods without duplicating auth-checking code in each one.

Common follow-ups: How do interceptors compare conceptually to ASP.NET Core middleware for REST APIs?;How do you chain multiple interceptors and control their execution order?

ASP.NET Core Middleware & Request Pipeline;Authentication & Authorization (Identity JWT OAuth)

What is protobuf field numbering, and why must you never reuse or renumber a field once a message definition has shipped to production?

Intermediate
Each field in a protobuf message has a unique number (like `string name = 1;`) that's actually what's encoded on the wire, not the field name itself -- protobuf's binary encoding uses these numbers for identification, so reusing a previously-used number for a different field, or renumbering an existing field, can cause old serialized data (or a client running an older version of the contract) to be misinterpreted as the wrong field entirely, silently corrupting data rather than failing loudly with a clear error.
// Original message (version 1)
message Order { int32 id = 1; string status = 2; }

// SAFE evolution: add new fields with NEW numbers, never reuse 1 or 2
message Order { int32 id = 1; string status = 2; string customer_email = 3; }

// DANGEROUS: never do this -- reusing field number 2 for a different purpose
// message Order { int32 id = 1; int32 status_code = 2; }  // BREAKS compatibility silently
Real-world example A subtle production data corruption bug traced back to a well-meaning developer who renamed and renumbered a deprecated protobuf field for 'cleanliness', unaware this broke binary compatibility with clients still running the previous contract version.

Common follow-ups: How does protobuf's `reserved` keyword help prevent accidental field number reuse?;What's the protobuf convention for marking a field as deprecated without removing it?

API Versioning;Assemblies & NuGet

Showing 1–10 of 15