protorails
is a toolkit to build type-safe API on Rails with Protocol Buffers (protobuf).
It's built on twitch's battle-tested Twirp protocol.
Write API schema in protobuf, and it can generate API client for TypeScript, Ruby or Java etc.
The deployment is easy. Twirp protocol is over HTTP 1.1. The server is just an ordinary Rails application. You can integrate it easily onto an existing Rails application.
- Generators to generate protobuf definitions from models
- Auto-reloading protobuf definitions in development
- Zeitwerk support (Rails 6)
First, you need to define API interfaces. (They can be defined with generators)
In twirp, schema of API endpoints is defined by service and rpc.
// app/protos/minishop/order/order_service.proto
syntax = "proto3";
import "minishop/order/show_request.proto";
import "minishop/order/order_resource.proto";
package minishop.order;
service Order {
rpc Show(ShowRequest) returns (OrderResource);
}
And schema of request and response is protobuf message.
// app/protos/minishop/order/show_request.proto
syntax = "proto3";
package minishop.order;
message ShowRequest {
string id = 1;
}
// app/protos/minishop/order/order_resource.proto
syntax = "proto3";
import "google/protobuf/timestamp.proto";
package minishop.order;
message OrderResource {
enum Status {
CART = 0;
ORDERED = 1;
}
string id = 1;
Status status = 2;
google.protobuf.Timestamp created_at = 5;
google.protobuf.Timestamp updated_at = 6;
}
And compile the protobuf files to Ruby:
$ bin/rails proto:compile
# This runs protobuf compilers (protoc)
Implement API actions just like usual Rails controllers:
Return value of an action must match the schema of API response (minishop.order.OrderResource
).
# app/controllers/orders_controller.rb
class OrdersController < Protorails::BaseController
# ::Minishop::Order::OrderService is generated by protoc in: app/gens/minishop/order/order_service_twirp.rb
service ::Minishop::Order::OrderService
def show
order = Order.find(rpc_request.id)
order.as_json(only: [:id, :status, :created_at, :updated_at])
end
end
The routing is defined automatically.
# config/routes.rb
Rails.application.routes.draw do
define_protorails_routes
# This automatically defines: POST /twirp/minishop.order.Order/Show => orders#show
end
You can generate API clients from these definitions with protoc plugins.
If you want one in TypeScript, combine protoc and its ts plugin with it:
protoc -I=../app/protos --ts_out=front/src/gen $(find ../app/protos -name '*.proto')
It generates api clients and type interface like:
const client = new OrderClient(transport)
const { response } = await client.show({ id: orderId })
/**
* @generated from protobuf message minishop.order.OrderResource
*/
export interface OrderResource {
/**
* @generated from protobuf field: string id = 1;
*/
id: string
/**
* @generated from protobuf field: minishop.order.OrderResource.Status status = 2;
*/
status: OrderResource_Status
/**
* @generated from protobuf field: int32 amount = 3;
*/
amount: number
}
By default, this gem assumes a project has:
- protobuf files under
app/protos
- compiled Ruby protobuf files in
app/gens
Use generator to bootstrap service:
$ bin/rails g proto:service order show
create app/protos/minishop/order/order_service.proto
create app/protos/minishop/order/show_request.proto
create app/protos/minishop/order/show_response.proto
You can specify package name:
$ bin/rails g proto:service order show --package minishop.v1.order
create app/protos/minishop/v1/order/order_service.proto
create app/protos/minishop/v1/order/show_request.proto
create app/protos/minishop/v1/order/show_response.proto
Generate an equivalent protobuf definition from a model:
$ bin/rails g proto:resource order
alpha: prototype for proof of concept
There's a sample application: https://github.com/uiur/minishop
Add this line to your application's Gemfile:
gem 'protorails'
And then execute:
$ bundle
Or install it yourself as:
$ gem install protorails
Contribution directions go here.
The gem is available as open source under the terms of the MIT License.