-
Notifications
You must be signed in to change notification settings - Fork 60
Twirp Clients
Clients may be defined using the Twirp::Client
DSL, which can be auto-generated from a .proto file (same as service definitions).
class HelloWorldClient < Twirp::Client
package "example"
service "HelloWorld"
rpc :Hello, HelloRequest, HelloResponse, :ruby_method => :hello
end
Clients can be instantiated with the service base url.
base_url = "http://localhost:3000/twirp"
client = HelloWorldClient.new(base_url)
For more advanced HTTP setup, use a Faraday connection. For example:
conn = Faraday.new(:url => 'http://localhost:3000/twirp') do |c|
c.use Faraday::Request::Retry
c.use Faraday::Request::BasicAuthentication, 'login', 'pass'
c.use Faraday::Response::Logger # log to STDOUT
c.use Faraday::Adapter::NetHttp # can use different HTTP libraries
end
client = HelloWorldClient.new(conn)
Faraday middleware is very powerful and can substantially modify HTTP requests and responses. Make sure not to use middleware that conflicts with the way Twirp clients encode and serialize requests. For example, do not use Faraday::Request::UrlEncoded
, Faraday::Request::Multipart
, FaradayMiddleware::ParseJson
, Faraday::Response::RaiseError
, etc.
Use the rpc
method with the request object, or the ruby method. The following calls are equivalent for a HelloWorld
client:
# Equivalent RPC calls
client.rpc(:Hello, HelloRequest.new(name: "World")) # generic rpc method
client.hello(HelloRequest.new(name: "World")) # DSL method with message object
client.hello(name: "World") # DSL method with message attributes
The response is a Twirp::ClientResponse object with the properties data
(success) and error
(failure).
resp = client.hello(name: "World")
if resp.error
puts resp.error # Twirp::Error
else
puts resp.data # HelloResponse (response message class)
end
The Twirp::Error has a well defined set of error codes useful for inspection:
resp = client.hello(name: "World")
if resp.error
case resp.error.code
when :not_found
puts "Not found: #{resp.error.msg}"
when :permission_denied
puts "Thou Shall No Pass!"
else
puts resp.error
end
end
Service 4xx and 5xx errors are returned as resp.error
values. But exceptions happening on the network level are raised as exceptions:
begin do
resp = client.rpc(:Hello, name: "World")
if resp.error
puts "Service failed with status 4xx or 5xx"
end
rescue StandardError => e
puts "Something crashed on the Client, Faraday connection or HTTP adapter"
end
Twirp services can serialize RPC messages with either Protobuf or JSON, depending on the Content-Type
header set by the client. Protobuf is used by default. To force-serialize with JSON, set the content_type
option as 2nd argument when initializing the client:
client = Example::HelloWorldClient.new(conn, content_type: "application/json")
resp = client.hello(name: "World") # request and response serialized as JSON
If you just want to make a few quick requests from the console and you don't have the proto file or code generator at hand, you can use a Twirp::ClientJSON
. This doesn't require the service definition DSL, requests are free form key-values.
require 'twirp'
host = 'http://localhost:3000/twirp'
client = Twirp::ClientJSON.new(host, package: "example", service: "HelloWorld", strict: true)
resp = client.rpc(:Hello, name: "World") # serialized as application/json; strict=true
if resp.error
puts resp.error # Twirp::Error
else
puts resp.data # Hash with plain attributes form the JSON response (untyped response)
end