Skip to content

Service Hooks

Mario Izquierdo edited this page Apr 16, 2019 · 3 revisions

Hooks allow injecting useful code during the lifecycle of a Twirp server request.

routing -> before -> handler -> on_success
                             -> on_error

One and only one of on_success or on_error is called per request. If exceptions are raised, the exception_raised hook is called before calling on_error.

routing -> before -> handler
                     ! exception_raised -> on_error

Available hooks

  • before: (block |rack_env, env|) Runs after the request is routed to an rpc method, but before calling the method handler. This is the only place to read the rack_env to access http request and middleware data, and can add more values to the Twirp env, for example authentication data like env[:user_id]. The Twirp env already has some routing info like env[:rpc_method], env[:input] and env[:input_class]. Returning a Twirp::Error from the before hook immediately cancels the request.
  • on_success: (block |env|) Runs after the rpc method is handled, unless it returned a Twirp:Error or raised an exception. The env[:output] contains the serialized message of class env[:ouput_class].
  • on_error: (block |twerr, env|) Runs on error responses, that is bad_route errors during routing, Twirp errors returned from before hooks or method handlers, or after an exception was raised. Raised exceptions are wrapped with Twirp::Error.internal_with(e), that has code internal and the original exception is stored in twerr.cause.
  • exception_raised: (block |e, env|) Runs if an exception was raised from the handler or any of the other hooks.

Example

svc = Example::HelloWorldService.new(handler)

svc.before do |rack_env, env|
  env[:user_id] = authenticate(rack_env)
  env[:enviornment] = (ENV['ENVIRONMENT'] || :local).to_sym
end

svc.on_success do |env|
  Stats.inc("requests.success")
end

svc.on_error do |twerr, env|
  Stats.inc("requests.error.#{twerr.code}")
end

svc.exception_raised do |e, env|
  if env[:environment] == :local
    puts "[Exception] #{e}"
    puts e.backtrace.join("\n")
  else
    ExceptionTracker.send(e)
  end
end

Before routed: Rack Middleware

If for some reason you need to run code before the Twirp service routes the request, then you should use standard Rack Middleware.

For example:

class MyMiddleware
  def initialize(app)
    @app = app       
  end                

  def call(rack_env)      
    # do some stuff and decide to return an early Twirp error
    if not_cool(env)
      twerr = Twirp::Error.invalid_argument("not cool", foo: "bar-meta")
      return Twirp::Service.error_response(twerr)
    end

    rack_env["mystaff"] = "foobar" # this is available in before hooks
    @app.call(rack_env)   
  end                
end

A Twirp::Service is also Rack Middleware, and the before hook has access to the rack_env. If your middleware adds data into the rack_env, that will be available in the before hook. The before hook could also add it to the Twirp env, so that data is also available in method handlers.

Clone this wiki locally