How to use the gRPC Client in Delphi

· Components

The TsgcGRPCClient component brings gRPC to Delphi and C++Builder without any external runtime. It is part of the sgcWebSockets Enterprise edition and runs on Windows, macOS, Linux, iOS and Android. This guide explains how the client works, how to configure it, and how to make each of the four gRPC call types from Delphi.

How it works

gRPC is Protocol Buffers messages framed over HTTP/2. Each message is sent as a length-prefixed payload on an HTTP/2 stream, the request carries headers such as content-type: application/grpc and grpc-timeout, and the final status arrives as the grpc-status and grpc-message trailers.

sgcWebSockets already ships a complete HTTP/2 stack, so TsgcGRPCClient sits on top of a TsgcHTTP2Client transport. You give it your serialized request bytes, it does the framing, header and timeout work, opens the HTTP/2 stream, and parses the response and trailers back into a typed TsgcGRPCResponse. Because the client works with raw TBytes, you can pair it with any Protocol Buffers library you already use.

Setting up the client

A gRPC channel is an HTTP/2 connection. Create a TsgcHTTP2Client, point it at the host and port, then assign it to the Client property of the gRPC component. For TLS, the HTTP/2 client uses OpenSSL or Windows SChannel; for a local plaintext server (h2c), set TLS to False.

uses
  sgcHTTP2, sgcGRPC_Client, sgcGRPC_Classes, sgcGRPC_Types;

var
  HTTP2: TsgcHTTP2Client;
  GRPC: TsgcGRPCClient;
begin
  HTTP2 := TsgcHTTP2Client.Create(nil);
  HTTP2.Host := 'grpc.example.com';
  HTTP2.Port := 443;
  HTTP2.TLS  := True;                 // False for a local h2c server
  HTTP2.TLSOptions.IOHandler := iohOpenSSL;  // or iohSChannel on Windows

  GRPC := TsgcGRPCClient.Create(nil);
  GRPC.Client := HTTP2;
end;

Configuring the channel

Channel-wide settings live in ChannelOptions. You can switch on per-message gzip compression, choose the wire content type, and raise the message and metadata size limits.

// gzip-compress outgoing messages
GRPC.ChannelOptions.Compression := grpcGzip;          // grpcNoCompression, grpcGzip, grpcDeflate, grpcSnappy

// application/grpc+proto (default) or application/grpc+json
GRPC.ChannelOptions.ContentType := grpcProto;         // or grpcJSON

// raise the limits for large messages
GRPC.ChannelOptions.MaxMessageSize  := 16 * 1024 * 1024;
GRPC.ChannelOptions.MaxMetadataSize := 64 * 1024;

Metadata and authentication

Custom headers travel as gRPC metadata. Add them to DefaultMetadata to send them on every call, which is the usual place for an authorization token or a tracing header.

GRPC.DefaultMetadata.Add('authorization', 'Bearer eyJ...');
GRPC.DefaultMetadata.Add('x-api-key', 'my-api-key');

Deadlines and cancellation

A per-call timeout is sent as the standard grpc-timeout header, so the server can give up at the same moment your client does. An in-flight streaming call can be stopped at any time with CancelCall, passing the stream id returned when the stream was opened.

Unary calls

A unary call sends one request and gets one response. Call blocks until the reply arrives and returns a TsgcGRPCResponse with the StatusCode, StatusMessage, the raw Data bytes (or DataString) and the Trailers.

var
  oResponse: TsgcGRPCResponse;
begin
  oResponse := GRPC.Call('helloworld.Greeter', 'SayHello', RequestBytes);
  if oResponse.StatusCode = grpcOK then
    Memo1.Text := oResponse.DataString
  else
    ShowMessage('gRPC error ' + oResponse.StatusMessage);
end;

To keep the UI responsive, use CallAsync. It returns at once and delivers the reply through the OnGRPCResponse event.

GRPC.OnGRPCResponse := GRPCResponse;
GRPC.CallAsync('helloworld.Greeter', 'SayHello', RequestBytes);

procedure TForm1.GRPCResponse(Sender: TObject; const Response: TsgcGRPCResponse);
begin
  if Response.StatusCode = grpcOK then
    Memo1.Text := Response.DataString;
end;

Server streaming

With server streaming you send one request and the server returns a stream of messages. Each message raises OnGRPCStreamMessage, and OnGRPCStreamEnd fires once with the final status.

GRPC.OnGRPCStreamMessage := GRPCStreamMessage;
GRPC.OnGRPCStreamEnd := GRPCStreamEnd;

GRPC.ServerStreamingCall('chat.Feed', 'Subscribe', RequestBytes);

procedure TForm1.GRPCStreamMessage(Sender: TObject; aStreamId: Integer;
  const aData: TBytes);
begin
  Memo1.Lines.Add(DecodeMessage(aData));
end;

Client streaming

Client streaming is the mirror image: you push a stream of messages and the server replies once. Open the stream, send each message, then close it to read the single reply.

var
  vStreamId: Integer;
  oResponse: TsgcGRPCResponse;
begin
  vStreamId := GRPC.OpenClientStream('upload.Service', 'Send');
  GRPC.SendStreamMessage(vStreamId, ChunkBytes1);
  GRPC.SendStreamMessage(vStreamId, ChunkBytes2);
  oResponse := GRPC.CloseClientStream(vStreamId);
  if oResponse.StatusCode = grpcOK then
    ShowMessage('Upload accepted');
end;

Bidirectional streaming

Bidirectional streaming runs a full-duplex exchange over a single HTTP/2 stream: both sides send whenever they like. Open the stream, send messages as needed, handle incoming messages in OnGRPCStreamMessage, and close when finished.

var
  vStreamId: Integer;
begin
  vStreamId := GRPC.OpenBidiStream('chat.Room', 'Connect');
  GRPC.SendBidiMessage(vStreamId, EncodeMessage('hello'));
  // ... incoming messages arrive on OnGRPCStreamMessage ...
  GRPC.CloseBidiStream(vStreamId);
end;

Status codes and error handling

Every response carries a typed StatusCode from grpcOK through the standard gRPC set (grpcNOT_FOUND, grpcUNAVAILABLE, grpcDEADLINE_EXCEEDED, grpcUNAUTHENTICATED and the rest). For asynchronous and streaming calls, OnGRPCError reports a non-OK gRPC status and OnGRPCException reports a transport or connection failure.

GRPC.OnGRPCError := GRPCError;

procedure TForm1.GRPCError(Sender: TObject; aStreamId: Integer;
  aStatusCode: TsgcGRPCStatusCode; const aStatusMessage: string);
begin
  Memo1.Lines.Add(Format('gRPC error %d: %s', [Ord(aStatusCode), aStatusMessage]));
end;

Automatic retry

The client can retry failed calls for you. Enable RetryPolicy, set the attempt count and backoff, and list the status codes that should trigger a retry. This is the typical configuration for transient grpcUNAVAILABLE failures.

GRPC.RetryPolicy.Enabled := True;
GRPC.RetryPolicy.MaxAttempts := 4;
GRPC.RetryPolicy.InitialBackoff := 200;     // ms
GRPC.RetryPolicy.BackoffMultiplier := 2.0;
GRPC.RetryPolicy.RetryableStatusCodes :=
  GRPC.RetryPolicy.RetryableStatusCodes + [grpcUNAVAILABLE, grpcRESOURCE_EXHAUSTED];

For cross-cutting logic such as logging or refreshing a token, add an entry to the Interceptors chain, which wraps every call in one place.

Where to go next

On top of this generic client, sgcWebSockets ships typed clients for the Google Cloud gRPC services (Pub/Sub, Speech-to-Text, Translation, Vision, Natural Language, Cloud Storage, BigQuery and Vertex AI), so you call high-level methods instead of assembling protobufs by hand. A ready-to-run sample is in Demos\21.GRPC\01.GRPC_Client, and the full reference is on the gRPC Client product page.

Questions or help getting started? Get in touch. You will get a reply from the people who wrote the code.