An implementation of Twirp in Crystal.
-
Add the dependency to your
shard.yml:dependencies: twirp: github: mloughran/twirp.cr
-
Run
shards install
To see an example proto definition, generator, server, and client, see the included example.
Includes a protoc plugin for generating Twirp server and client implementations from a protobuf service definition.
protoc --twirp_crystal_out=src --plugin=bin/protoc-gen-twirp_crystal example.proto
Typically you will also want to generate protobuf message definitions:
protoc --crystal_out=src --plugin=bin/protoc-gen-crystal example.proto
For example:
require "./example.twirp.cr"
require "./example.pb.cr"
client = ExampleService::Client.new("localhost", 8080)
req = HelloWorldRequest.new(name: "twirp")
resp = client.hello_world(req)
puts resp.greetingI've elected to raise an error (which will be a subclass of Twirp::Error) if a Twirp error response is received, rather that take the ruby approach of returning a result object. Exceptions will also be raised by the HTTP::Client in case of transport errors.
Implement a handler (subclassing the generated abstract class):
class ExampleHandler < ExampleService
def hello_world(req : HelloWorldRequest) : HelloWorldResponse
HelloWorldResponse.new(greeting: "Hello #{req.name}")
end
endPass this to a Twirp server (which is itself a HTTP::Handler):
require "twirp/server"
twirp_server = Twirp::Server.new(ExampleHandler.new)Expose this via a HTTP::Server:
server = HTTP::Server.new(twirp_server)
address = server.bind_tcp 8080
puts "Listening on http://#{address}"
server.listenTo return a Twirp error, raise the appropriate Twirp::Error from your handler. For example:
class ExampleHandler < ExampleService
def hello_world(req : HelloWorldRequest) : HelloWorldResponse
raise Twirp::Error::Unauthenticated.new("Sorry!")
end
endAny other errors raised by handler code will be caught, logged, and an internal Twirp error will be returned to the client. Exceptions can be captured by passing an exception_handler proc to Twirp::Server.new.
Twirp::Server produces log output when handling handling requests. This can be customised, e.g.:
Twirp::Log.level = Log::Severity::WarnInstall shards and build
shards install
shards build
Verify that example generates cleanly
cd example && make clean generate && cd -
Run server and client:
crystal run example/server.cr
crystal run example/client.cr
There are currently no specs, sorry.
- Martyn Loughran - creator and maintainer
With thanks to https://github.com/jgaskins/grpc for the generator (which is more or less verbatim), and for inspiring the double-macro approach for the service DSL (since I'm still wrapping my head around crystal macros).