Dart implementation of Twirp, a simple RPC framework with protobuf service definitions.
Add the depenendcy to your app's pubspec.yaml (and run an implicit flutter pub get):
dependencies:
twirpd: <latest_version>
Make sure you have protoc or buf installed.
Mac:
brew install protobuf
Linux:
apt-get install protobuf
twirpd relies on twirpd-protoc-plugin to generate protobuf message definitions and client code
First you need to install twirpd-protoc-plugin
by running:
dart pub global activate twirpd-protoc-plugin
Then you can use protoc
command to generate all needed files:
protoc \
--twirpd_out={PATH_TO_OUTPUT_DIR} \
-I {PATH_TO_DIR_WITH_PROTOS} {PROTO_FILE_NAMES}
For example:
protoc \
--twirpd_out=lib/src/generated \
-I proto my_service.proto
If you are using external Google proto messages, you can include them in the command, for example:
protoc \
--twirpd_out=lib/src/generated \
-I proto my_service.proto google/protobuf/timestamp.proto
This should generate all the code you need.
Let's consider HelloWorld service:
syntax = "proto3";
service HelloWorld {
rpc Hello(HelloReq) returns (HelloResp);
}
message HelloReq {
string subject = 1;
}
message HelloResp {
string text = 1;
}
The HelloWorldClient class is already generated for you and ready to use:
HelloWorldClient client = HelloWorldClient(
'https://yourbackend.com',
port: 8080,
);
It has all the methods defined in the proto
file:
HelloReq request = HelloReq(subject: 'Hey mom!');
HelloResp response = await client.hello(request);
print(response.text);
You can add your own client interceptors to change the request you're about to send.
HelloWorldClient client = HelloWorldClient(
'https://yourbackend.com',
port: 8080,
interceptors: [
MyCredentialsInterceptor(),
],
);
For example to add credentials headers:
class MyCredentialsInterceptor implements ClientInterceptor {
final MyCredentialsStorage storage;
const MyCredentialsInterceptor(this.storage);
@override
Future<R> intercept<Q, R>(
ClientMethod<Q, R> method,
Q request,
CallOptions options,
ClientCallInvoker<Q, R> invoker,
) {
return invoker(method, request, _addAuthToken(options));
}
CallOptions _addAuthToken(CallOptions options) {
return options.mergedWith(CallOptions(
metadata: {'authorization': 'Bearer ${storage.authToken}'},
));
}
}
twirpd uses plain Client
from http
package under the hood. By default it creates IOClient
but you can provide other implementation, for example RetryClient:
HelloWorldClient client = HelloWorldClient(
'https://yourbackend.com',
port: 8080,
options: ClientOptions(
client: RetryClient(), //<-- Any http client
),
);
You can also set extra metadata through CallOptions
if you want to change the behavior of a specific request invocation. The metadata
is added to the headers and can be also picked up by the interceptors.
await client.hello(
request,
options: CallOptions(
metadata: {'specific_header': 'Only for this call'},
),
);
Made with 💛 at Cheddar.