Top Related Projects
The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)
gRPC to JSON proxy generator following the gRPC HTTP spec
A simple RPC framework with protobuf service definitions
gRPC Web implementation for Golang and TypeScript
Generate message validators from .proto annotations.
Like cURL, but for gRPC: Command-line tool for interacting with gRPC servers
Quick Overview
The elixir-grpc/grpc repository is an Elixir implementation of gRPC, a high-performance, open-source universal RPC framework. It allows developers to build efficient and scalable distributed systems using Elixir, leveraging the power of Protocol Buffers for serialization and HTTP/2 for transport.
Pros
- Seamless integration with Elixir's ecosystem and OTP
- High performance and low latency communication
- Strong typing and code generation from Protocol Buffer definitions
- Support for bidirectional streaming
Cons
- Learning curve for developers new to gRPC or Protocol Buffers
- Limited ecosystem compared to REST APIs in Elixir
- Potential complexity in setup and configuration
- Less human-readable than JSON-based APIs
Code Examples
- Defining a simple gRPC service:
defmodule Greeter.Service do
use GRPC.Service, name: "Greeter"
rpc :SayHello, HelloRequest, HelloReply
end
defmodule HelloRequest do
use Protobuf, syntax: :proto3
@derive Jason.Encoder
defstruct [:name]
field :name, 1, type: :string
end
defmodule HelloReply do
use Protobuf, syntax: :proto3
@derive Jason.Encoder
defstruct [:message]
field :message, 1, type: :string
end
- Implementing the service:
defmodule Greeter.Server do
use GRPC.Server, service: Greeter.Service
@spec say_hello(HelloRequest.t(), GRPC.Server.Stream.t()) :: HelloReply.t()
def say_hello(request, _stream) do
HelloReply.new(message: "Hello #{request.name}")
end
end
- Starting the gRPC server:
defmodule MyApp do
use Application
def start(_type, _args) do
children = [
{GRPC.Server.Supervisor, {Greeter.Server, 50051}}
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
end
Getting Started
- Add dependencies to
mix.exs
:
defp deps do
[
{:grpc, "~> 0.5.0"},
{:protobuf, "~> 0.10.0"},
{:google_protos, "~> 0.1"}
]
end
- Define your service in a
.proto
file - Generate Elixir code from the
.proto
file:
protoc --elixir_out=plugins=grpc:./lib your_service.proto
- Implement your service and start the gRPC server as shown in the code examples above
- Run your application:
mix run --no-halt
Competitor Comparisons
The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)
Pros of grpc
- Broader language support, including C++, Java, Python, Go, and more
- More comprehensive documentation and examples
- Larger community and ecosystem, leading to better support and resources
Cons of grpc
- More complex setup and configuration
- Steeper learning curve for beginners
- Heavier footprint and potentially slower performance in some scenarios
Code Comparison
grpc (C++):
#include <grpcpp/grpcpp.h>
#include "helloworld.grpc.pb.h"
class GreeterServiceImpl final : public Greeter::Service {
Status SayHello(ServerContext* context, const HelloRequest* request,
HelloReply* reply) override {
std::string prefix("Hello ");
reply->set_message(prefix + request->name());
return Status::OK;
}
};
elixir-grpc (Elixir):
defmodule Greeter.Server do
use GRPC.Server, service: Helloworld.Greeter.Service
@spec say_hello(Helloworld.HelloRequest.t, GRPC.Server.Stream.t) ::
Helloworld.HelloReply.t
def say_hello(request, _stream) do
Helloworld.HelloReply.new(message: "Hello #{request.name}")
end
end
Both repositories provide gRPC implementations, but grpc offers a more comprehensive solution with wider language support, while elixir-grpc focuses specifically on Elixir integration. The choice between them depends on the project's requirements and the development team's expertise.
gRPC to JSON proxy generator following the gRPC HTTP spec
Pros of grpc-gateway
- Provides RESTful API endpoints for gRPC services, allowing easier integration with existing REST-based systems
- Supports automatic OpenAPI (Swagger) documentation generation
- Language-agnostic, can be used with any gRPC implementation
Cons of grpc-gateway
- Adds complexity to the overall architecture with an additional layer
- May introduce slight performance overhead due to protocol translation
- Requires additional configuration and maintenance
Code Comparison
grpc-gateway:
syntax = "proto3";
package example;
import "google/api/annotations.proto";
service ExampleService {
rpc Echo(StringMessage) returns (StringMessage) {
option (google.api.http) = {
post: "/v1/example/echo"
body: "*"
};
}
}
grpc (Elixir):
defmodule ExampleService do
use GRPC.Server, service: Example.ExampleService.Service
@spec echo(Example.StringMessage.t, GRPC.Server.Stream.t) :: Example.StringMessage.t
def echo(request, _stream) do
Example.StringMessage.new(value: request.value)
end
end
The grpc-gateway example shows how to define RESTful endpoints in the protobuf definition, while the Elixir gRPC example demonstrates the implementation of a gRPC service directly in Elixir code.
A simple RPC framework with protobuf service definitions
Pros of Twirp
- Simpler and more lightweight than gRPC
- Supports both Protocol Buffers and JSON encoding
- Easier to set up and use, especially for smaller projects
Cons of Twirp
- Less feature-rich compared to gRPC
- Limited language support (primarily Go and some community-driven implementations)
- Lacks built-in streaming capabilities
Code Comparison
Twirp (Go):
service Haberdasher {
rpc MakeHat(Size) returns (Hat);
}
gRPC (Elixir):
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
Key Differences
- Twirp is designed to be a simpler alternative to gRPC, focusing on ease of use and HTTP/1.1 compatibility
- gRPC offers more advanced features like bi-directional streaming and HTTP/2 support
- Twirp has a smaller ecosystem and community compared to gRPC
- gRPC provides better cross-language support and is more suitable for complex, large-scale systems
Use Cases
- Twirp: Smaller projects, microservices, or when simplicity is preferred
- gRPC: Large-scale distributed systems, performance-critical applications, or when advanced features are required
Both projects aim to simplify RPC communication, but they cater to different needs and complexity levels. The choice between them depends on the specific requirements of your project and the desired balance between simplicity and feature richness.
gRPC Web implementation for Golang and TypeScript
Pros of grpc-web
- Supports browser-based gRPC communication, enabling direct gRPC calls from web applications
- Provides TypeScript support, enhancing developer experience for frontend development
- Offers a more seamless integration with existing gRPC backend services
Cons of grpc-web
- Limited to web-based environments, whereas grpc supports a broader range of platforms
- May require additional setup and configuration for browser compatibility
- Potentially less mature and less widely adopted compared to the more established grpc
Code Comparison
grpc-web:
const { HelloRequest } = require('./hello_pb.js');
const { GreeterClient } = require('./hello_grpc_web_pb.js');
const client = new GreeterClient('http://localhost:8080');
const request = new HelloRequest();
request.setName('World');
client.sayHello(request, {}, (err, response) => {
console.log(response.getMessage());
});
grpc:
defmodule GreeterClient do
use GRPC.Client, service: Helloworld.Greeter.Service
def say_hello(name) do
{:ok, channel} = GRPC.Stub.connect("localhost:50051")
request = Helloworld.HelloRequest.new(name: name)
{:ok, reply} = channel |> Helloworld.Greeter.Stub.say_hello(request)
reply.message
end
end
This comparison highlights the different approaches and language-specific implementations of gRPC clients in web and Elixir environments.
Generate message validators from .proto annotations.
Pros of go-proto-validators
- Specifically designed for Go, offering tight integration with Go's ecosystem
- Provides custom validation rules for protocol buffer messages
- Lightweight and focused on validation, making it easier to integrate into existing projects
Cons of go-proto-validators
- Limited to Go language, whereas grpc supports multiple languages including Elixir
- Narrower scope, focusing only on validation rather than full gRPC implementation
- Less active community and fewer contributors compared to the more widely-used grpc
Code Comparison
go-proto-validators:
message User {
string id = 1 [(validator.field) = {length_gt: 3, length_lt: 64}];
string email = 2 [(validator.field) = {regex: "^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,4}$"}];
int32 age = 3 [(validator.field) = {int_gt: 0, int_lt: 150}];
}
grpc (Elixir):
defmodule MyApp.User do
use Protobuf, syntax: :proto3
field :id, 1, type: :string
field :email, 2, type: :string
field :age, 3, type: :int32
end
Note that grpc doesn't provide built-in validation, but it offers a more comprehensive gRPC implementation across multiple languages. The go-proto-validators example shows how it adds validation rules directly to the protocol buffer definition, while the grpc example demonstrates a basic message definition in Elixir.
Like cURL, but for gRPC: Command-line tool for interacting with gRPC servers
Pros of grpcurl
- Language-agnostic CLI tool for interacting with gRPC servers
- Supports reflection for discovering services and methods
- Can be used for testing and debugging gRPC services without writing client code
Cons of grpcurl
- Limited to command-line usage, not suitable for integration into Elixir applications
- Lacks native Elixir ecosystem integration and OTP compatibility
Code Comparison
grpc (Elixir):
defmodule MyService do
use GRPC.Server, service: Helloworld.Greeter.Service
def say_hello(request, _stream) do
Helloworld.HelloReply.new(message: "Hello, #{request.name}!")
end
end
grpcurl (Command-line):
grpcurl -plaintext -d '{"name": "World"}' \
localhost:50051 helloworld.Greeter/SayHello
Key Differences
- grpc is an Elixir-specific implementation of gRPC, while grpcurl is a language-agnostic CLI tool
- grpc allows for seamless integration with Elixir applications and OTP, whereas grpcurl is primarily for testing and debugging
- grpc requires writing Elixir code to define services and methods, while grpcurl can interact with existing gRPC services without additional coding
Use Cases
- Use grpc for building gRPC services and clients within Elixir applications
- Use grpcurl for quick testing, debugging, and exploring gRPC services without writing client code
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
gRPC Elixir
An Elixir implementation of gRPC.
Table of contents
Installation
The package can be installed as:
def deps do
[
{:grpc, "~> 0.9"}
]
end
Usage
- Write your protobuf file:
syntax = "proto3";
package helloworld;
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greeting
message HelloReply {
string message = 1;
}
// The greeting service definition.
service Greeter {
// Greeting function
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
- Then generate Elixir code from proto file as protobuf-elixir shows (especially the
gRPC Support
section) or using protobuf_generate hex package. Example usingprotobuf_generate
lib:
mix protobuf.generate --output-path=./lib --include-path=./priv/protos helloworld.proto
In the following sections you will see how to implement gRPC server logic.
Simple RPC
- Implement the server side code like below and remember to return the expected message types.
defmodule Helloworld.Greeter.Server do
use GRPC.Server, service: Helloworld.Greeter.Service
@spec say_hello(Helloworld.HelloRequest.t, GRPC.Server.Stream.t) :: Helloworld.HelloReply.t
def say_hello(request, _stream) do
Helloworld.HelloReply.new(message: "Hello #{request.name}")
end
end
- Define gRPC endpoints
# Define your endpoint
defmodule Helloworld.Endpoint do
use GRPC.Endpoint
intercept GRPC.Server.Interceptors.Logger
run Helloworld.Greeter.Server
end
We will use this module in the gRPC server startup section.
Note: For other types of RPC call like streams see here.
HTTP Transcoding
- Adding grpc-gateway annotations to your protobuf file definition:
import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
get: "/v1/greeter/{name}"
};
}
rpc SayHelloFrom (HelloRequestFrom) returns (HelloReply) {
option (google.api.http) = {
post: "/v1/greeter"
body: "*"
};
}
}
- Add protoc plugin dependency and compile your protos using protobuf_generate hex package:
In mix.exs:
def deps do
[
{:grpc, "~> 0.7"},
{:protobuf_generate, "~> 0.1.1"}
]
end
And in your terminal:
mix protobuf.generate \
--include-path=priv/proto \
--include-path=deps/googleapis \
--generate-descriptors=true \
--output-path=./lib \
--plugins=ProtobufGenerate.Plugins.GRPCWithOptions \
google/api/annotations.proto google/api/http.proto helloworld.proto
- Enable http_transcode option in your Server module
defmodule Helloworld.Greeter.Server do
use GRPC.Server,
service: Helloworld.Greeter.Service,
http_transcode: true
@spec say_hello(Helloworld.HelloRequest.t, GRPC.Server.Stream.t) :: Helloworld.HelloReply.t
def say_hello(request, _stream) do
%Helloworld.HelloReply{message: "Hello #{request.name}"}
end
end
See full application code in helloworld_transcoding example.
Start Application
- Start gRPC Server in your supervisor tree or Application module:
# In the start function of your Application
defmodule HelloworldApp do
use Application
def start(_type, _args) do
children = [
# ...
{GRPC.Server.Supervisor, endpoint: Helloworld.Endpoint, port: 50051, start_server: true}
]
opts = [strategy: :one_for_one, name: YourApp]
Supervisor.start_link(children, opts)
end
end
- Call rpc:
iex> {:ok, channel} = GRPC.Stub.connect("localhost:50051")
iex> request = Helloworld.HelloRequest.new(name: "grpc-elixir")
iex> {:ok, reply} = channel |> Helloworld.Greeter.Stub.say_hello(request)
# With interceptors
iex> {:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [GRPC.Client.Interceptors.Logger])
...
Check the examples and interop directories in the project's source code for some examples.
Client Adapter and Configuration
The default adapter used by GRPC.Stub.connect/2
is GRPC.Client.Adapter.Gun
. Another option is to use GRPC.Client.Adapters.Mint
instead, like so:
GRPC.Stub.connect("localhost:50051",
# Use Mint adapter instead of default Gun
adapter: GRPC.Client.Adapters.Mint
)
The GRPC.Client.Adapters.Mint
adapter accepts custom configuration. To do so, you can configure it from your mix application via:
# File: your application's config file.
config :grpc, GRPC.Client.Adapters.Mint, custom_opts
The accepted options for configuration are the ones listed on Mint.HTTP.connect/4
Features
- Various kinds of RPC:
- HTTP Transcoding
- TLS Authentication
- Error handling
- Interceptors (See
GRPC.Endpoint
) - Connection Backoff
- Data compression
- gRPC Reflection
Benchmark
-
Simple benchmark by using ghz
-
Benchmark followed by official spec
Contributing
Your contributions are welcome!
Please open issues if you have questions, problems and ideas. You can create pull requests directly if you want to fix little bugs, add small features and so on. But you'd better use issues first if you want to add a big feature or change a lot of code.
Top Related Projects
The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)
gRPC to JSON proxy generator following the gRPC HTTP spec
A simple RPC framework with protobuf service definitions
gRPC Web implementation for Golang and TypeScript
Generate message validators from .proto annotations.
Like cURL, but for gRPC: Command-line tool for interacting with gRPC servers
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot