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.
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();