Skip to content

elixir-grpc/grpc

Folders and files

NameName
Last commit message
Last commit date
Apr 27, 2025
Mar 21, 2025
Aug 1, 2022
Mar 21, 2025
Feb 14, 2024
Mar 21, 2025
Apr 27, 2025
Mar 25, 2024
Mar 25, 2024
Mar 25, 2024
Apr 27, 2025
Jul 18, 2022
Dec 24, 2019
Nov 27, 2023
Apr 9, 2019
Nov 14, 2016
Jul 22, 2022
Apr 27, 2025
Apr 27, 2025
Mar 21, 2025

Repository files navigation

gRPC Elixir

GitHub CI Hex.pm Hex Docs License Total Download Last Updated

An Elixir implementation of gRPC.

Table of contents

Installation

The package can be installed as:

def deps do
  [
    {:grpc, "~> 0.9"}
  ]
end

Usage

  1. 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) {}
}
  1. Then generate Elixir code from proto file as protobuf-elixir:
protoc --elixir_out=plugins=grpc:./lib -I./priv/protos helloworld.proto

In the following sections you will see how to implement gRPC server logic.

Simple RPC

  1. 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
  1. 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

  1. 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: "*"
    };
  }
}
  1. 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
  1. 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.

CORS

When accessing gRPC from a browser via HTTP transcoding or gRPC-Web, CORS headers may be required for the browser to allow access to the gRPC endpoint. Adding CORS headers can be done by using GRPC.Server.Interceptors.CORS as an interceptor in your GRPC.Endpoint module, configuring it as decribed in the module documentation:

Example:

# Define your endpoint
defmodule Helloworld.Endpoint do
  use GRPC.Endpoint

  intercept GRPC.Server.Interceptors.Logger
  intercept GRPC.Server.Interceptors.CORS, allow_origin: "mydomain.io"
  run Helloworld.Greeter.Server
end

Start Application

  1. 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
  1. 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

Benchmark

  1. Simple benchmark by using ghz

  2. 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.