Using clients
Configuration
Supported protocols
Connect-Dart currently supports 3 protocols:
- The new Connect protocol, a simple, HTTP-based protocol that works over HTTP/1.1 or HTTP/2. It takes the best parts of gRPC/gRPC-Web, including streaming, and packages them into a protocol that works well on all platforms, including mobile and web. JSON- and binary-encoded Protobuf is supported out of the box.
- The gRPC protocol: Allows clients to communicate with existing gRPC services.
- The gRPC-Web protocol: Allows clients to communicate with existing gRPC-Web services. The main difference between gRPC and gRPC-Web is that gRPC-Web does not utilize HTTP trailers in the protocol.
If your backend services are already using gRPC today, Envoy provides support for converting requests made using the Connect and gRPC-Web protocols to gRPC, enabling you to use Connect-Swift without the SwiftNIO dependency.
Switching between the Connect and gRPC/gRPC-Web protocols is a simple 2-line change
when configuring the Transport
:
import 'package:connectrpc/http2.dart';
import 'package:connectrpc/connect.dart';
import 'package:connectrpc/protobuf.dart';
import 'package:connectrpc/protocol/connect.dart' as protocol;
// import 'package:connectrpc/protocol/grpc.dart' as protocol;
// import 'package:connectrpc/protocol/grpc_web.dart' as protocol;
final transport = protocol.Transport(
baseUrl: "https://demo.connectrpc.com",
codec: const ProtoCodec(), // Or JsonCodec()
httpClient: createHttpClient(),
// statusParser: StatusParser(), // This is required for gRPC and gRPC-Web
);
Note that these options are mutually exclusive. If you'd like to use
different protocols with different APIs, create one Transport
for each
protocol.
Compression
Request compression and response decompression can be enabled for io platforms by providing the
compression
transport option. Gzip compression is provided in package:connectrpc/io.dart
, and
support for other compression algorithms can be added by implementing the Compression
interface.
HTTP stack
Connect-Dart provides three different HTTP client implementations out of the box.
dart:io
based HTTP/1 client. This supports Connect and gRPC-Web protocols. Full duplex Bidi streaming is not supported. Exported frompackage:connectrpc/io.dart
.dart:js_interop
powered andfetch
based implementation to use on web platforms. It supports Connect and gRPC-Web protocols, limited to unary and server streaming RPCs. Exported frompackage:connectrpc/web.dart
.http2
package based HTTP/2 client. This supports all three protocols and all four RPC types. This is not available on the web platforms. Exported frompackage:connectrpc/http2.dart
.
All of them export a function called createHttpClient
that accepts options for configuring each of
them.
Using generated clients
Generated clients take adavantage of Dart's Future
and Stream
types to provide
idiomatic APIs.
import './gen/eliza.pb.dart';
import './gen/eliza.connect.client.dart';
import 'package:connectrpc/http2.dart';
import 'package:connectrpc/connect.dart';
import 'package:connectrpc/protobuf.dart';
import 'package:connectrpc/protocol/connect.dart' as protocol;
final transport = protocol.Transport(
baseUrl: "https://demo.connectrpc.com",
codec: const ProtoCodec(), // Or JsonCodec()
httpClient: createHttpClient(),
);
final elizaClient = ElizaServiceClient(transport);
...
// In an async function
final response = await elizaClient.say(SayRequest(sentence: 'hello, world'));
print(response.message.sentence);
For server-streaming RPCs, the corresponding method on the client returns
a Stream
object which allows the caller to iterate over updates from
the server using the await for
:
final stream = elizaClient.introduce(IntroduceRequest());
await for (final next in stream) {
print(next);
}
For client-streaming and bidi-streaming RPCs, the corresponding method on the client
accepts a Stream
. For bidi it also returns a stream:
final stream = elizaClient.converse(Stream.fromIterable([ConverseRequest()]));
await for (final next in stream) {
print(next);
}
Headers and Trailers
Headers can be sent using the optional headers
parameter of client methods:
elizaClient.say(
SayRequest(),
headers: Headers()..['authorization'] = 'Bearer <token>',
);
Response headers and trailers can be accessed using the onHeader
and onTrailer
parameters:
elizaClient.say(
SayRequest(),
onHeader: (headers) {
print(headers);
},
onTrailer: (trailer) {
print(trailer);
},
);
Timeouts and Cancellation
The methods on the generated client accept an optional signal
parameter that can be used to configure
timeouts:
final response = await elizaClient.say(
SayRequest(),
signal: TimeoutSignal(Duration(milliseconds: 200)), // Or a DeadlineSignal that accepts a DateTime
);
They can also accept a CancelableSignal
that can be cancelled adhoc:
final signal = CancelableSignal();
final stream = elizaClient.introduce(
IntroduceRequest(),
signal: signal,
);
var count = 0;
await for (final next in stream) {
count++;
if (count == 5) {
break;
}
print(next);
}
signal.cancel();