Skip to content

Elixir Language Support

Status: ✅ Full Support Examples:

Elixir support provides Protocol Buffer and gRPC integration for building distributed systems, real-time applications, and microservices.

PluginDescriptionGenerated Files
protoc-gen-elixirBase messages & gRPC*.pb.ex
languages.elixir = {
enable = true;
outputPath = "lib/proto";
};
languages.elixir = {
  enable = true;
  outputPath = "lib/proto";
  namespace = "MyApp.Proto";
  options = [];

  # Enable gRPC service generation
  grpc = {
    enable = true;
    options = [];
  };

  # Enable validation support
  validate = {
    enable = true;
    options = [];
  };

  # Compile specific proto files for Elixir
  files = [
    "./proto/services/v1/user_service.proto"
    "./proto/messages/v1/common.proto"
  ];

  # Additional proto files beyond the global list
  additionalFiles = [
    "./proto/internal/v1/admin.proto"
  ];
};

Generates Elixir modules for all protobuf messages with:

  • Full type safety using structs
  • Binary encoding/decoding
  • JSON serialization support
  • Default values and field presence tracking

When enabled, generates:

  • Service modules with server behavior
  • Client stubs for service calls
  • Support for all RPC types (unary, streaming, bidirectional)
  • Error handling with proper gRPC status codes

Provides hooks for integrating with validation libraries:

  • Field validation rules
  • Custom validation functions
  • Integration with Ecto changesets
# Create a message
message = %MyApp.Proto.User{
id: 1,
name: "Alice",
email: "alice@example.com",
roles: ["admin", "user"]
}
# Encode to binary
binary = MyApp.Proto.User.encode(message)
# Decode from binary
{:ok, decoded} = MyApp.Proto.User.decode(binary)
defmodule MyApp.UserService.Server do
use GRPC.Server, service: MyApp.Proto.UserService.Service
def get_user(request, _stream) do
user = MyApp.Users.find(request.id)
%MyApp.Proto.GetUserResponse{user: user}
end
def list_users(request, stream) do
MyApp.Users.list()
|> Stream.map(&%MyApp.Proto.User{&1})
|> Enum.each(&GRPC.Server.send_reply(stream, &1))
end
end
# Connect to server
{:ok, channel} = GRPC.Stub.connect("localhost:50051")
# Make RPC call
request = %MyApp.Proto.GetUserRequest{id: 123}
{:ok, response} = MyApp.Proto.UserService.Stub.get_user(channel, request)

Elixir protobuf integrates well with Phoenix applications:

defmodule MyAppWeb.ProtoController do
use MyAppWeb, :controller
def show(conn, %{"id" => id}) do
user = MyApp.Users.get(id)
proto = %MyApp.Proto.User{user}
conn
|> put_resp_content_type("application/x-protobuf")
|> send_resp(200, MyApp.Proto.User.encode(proto))
end
end

Add these dependencies to your mix.exs:

defp deps do
[
{:protobuf, "~> 0.12.0"},
{:grpc, "~> 0.7.0"}, # If using gRPC
{:jason, "~> 1.4"} # For JSON support
]
end
OptionTypeDefaultDescription
enableboolfalseEnable Elixir code generation
packagepackageprotoc-gen-elixirThe protoc plugin package
outputPathstring | [string]"lib"Output directory for generated code
options[string][]Options to pass to protoc-gen-elixir
namespacestring""Module namespace prefix (e.g., “MyApp.Proto”)
files[string] | nullnullSpecific proto files for this language
additionalFiles[string][]Additional proto files to compile
OptionTypeDefaultDescription
enableboolfalseEnable gRPC code generation
packagepackageprotoc-gen-elixirThe protoc plugin package
options[string][]Options for gRPC generation
OptionTypeDefaultDescription
enableboolfalseEnable validation support
packagepackagenullValidation package (if any)
options[string][]Options for validation generation
  1. Module Organization: Use the namespace option to organize generated modules under your application’s namespace.

  2. OTP Integration: Generated gRPC servers integrate with OTP supervision trees:

    children = [
    {GRPC.Server.Supervisor, endpoint: MyApp.Endpoint, port: 50051}
    ]
  3. Error Handling: Use proper gRPC status codes:

    raise GRPC.RPCError, status: :not_found, message: "User not found"
  4. Testing: Use the generated modules in tests:

    test "encodes and decodes messages" do
    original = %MyApp.Proto.User{id: 1, name: "Test"}
    binary = MyApp.Proto.User.encode(original)
    {:ok, decoded} = MyApp.Proto.User.decode(binary)
    assert decoded == original
    end
  5. Performance: For high-performance scenarios, consider using binary pattern matching directly on encoded messages.

If you get “module not found” errors after generation:

  1. Ensure the outputPath is in your Elixir project’s lib path
  2. Run mix compile to compile the generated modules
  3. Check that the namespace matches your project structure

For gRPC connection problems:

  1. Verify the server is running on the correct port
  2. Check firewall settings
  3. Ensure both client and server use the same proto definitions