Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
226 changes: 226 additions & 0 deletions doc/src/content/docs/reference/languages/elixir.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
---
title: Elixir Language Support
description: Protocol Buffer and gRPC support for Elixir applications, enabling distributed systems and real-time services.
---

import { Tabs, TabItem } from "@astrojs/starlight/components";
import { Code } from "astro:components";
import basicConfig from "./elixir.x-basic-configuration.nix?raw";

# Elixir Language Support

**Status**: ✅ Full Support
**Examples**:
- [`examples/elixir-basic/`](https://github.com/conneroisu/bufr.nix/tree/main/examples/elixir-basic) - Basic protobuf messages
- [`examples/elixir-grpc/`](https://github.com/conneroisu/bufr.nix/tree/main/examples/elixir-grpc) - gRPC services

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

## Available Plugins

| Plugin | Description | Generated Files |
| ----------------------- | --------------------- | ------------------------ |
| **`protoc-gen-elixir`** | Base messages & gRPC | `*.pb.ex` |

## Configuration

### Basic Configuration

```nix
languages.elixir = {
enable = true;
outputPath = "lib/proto";
};
```

### Full Configuration

<Code code={basicConfig} lang="nix" title="flake.nix" />

## Features

### Message Generation

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

### gRPC Support

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

### Validation Support

Provides hooks for integrating with validation libraries:
- Field validation rules
- Custom validation functions
- Integration with Ecto changesets

## Usage Example

### Basic Message Usage

```elixir
# 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)
```

### gRPC Server Implementation

```elixir
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
```

### gRPC Client Usage

```elixir
# 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)
```

## Integration with Phoenix

Elixir protobuf integrates well with Phoenix applications:

```elixir
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
```

## Mix.exs Dependencies

Add these dependencies to your `mix.exs`:

```elixir
defp deps do
[
{:protobuf, "~> 0.12.0"},
{:grpc, "~> 0.7.0"}, # If using gRPC
{:jason, "~> 1.4"} # For JSON support
]
end
```

## Configuration Options

### `languages.elixir`

| Option | Type | Default | Description |
| ---------------- | ------------------- | ------------- | ---------------------------------------------- |
| `enable` | `bool` | `false` | Enable Elixir code generation |
| `package` | `package` | `protoc-gen-elixir` | The protoc plugin package |
| `outputPath` | `string \| [string]`| `"lib"` | Output directory for generated code |
| `options` | `[string]` | `[]` | Options to pass to protoc-gen-elixir |
| `namespace` | `string` | `""` | Module namespace prefix (e.g., "MyApp.Proto") |
| `files` | `[string] \| null` | `null` | Specific proto files for this language |
| `additionalFiles`| `[string]` | `[]` | Additional proto files to compile |

### `languages.elixir.grpc`

| Option | Type | Default | Description |
| --------- | ---------- | -------------------- | -------------------------------- |
| `enable` | `bool` | `false` | Enable gRPC code generation |
| `package` | `package` | `protoc-gen-elixir` | The protoc plugin package |
| `options` | `[string]` | `[]` | Options for gRPC generation |

### `languages.elixir.validate`

| Option | Type | Default | Description |
| --------- | ---------- | ------- | -------------------------------------- |
| `enable` | `bool` | `false` | Enable validation support |
| `package` | `package` | `null` | Validation package (if any) |
| `options` | `[string]` | `[]` | Options for validation generation |

## Tips and Best Practices

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:
```elixir
children = [
{GRPC.Server.Supervisor, endpoint: MyApp.Endpoint, port: 50051}
]
```

3. **Error Handling**: Use proper gRPC status codes:
```elixir
raise GRPC.RPCError, status: :not_found, message: "User not found"
```

4. **Testing**: Use the generated modules in tests:
```elixir
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.

## Common Issues

### Module Not Found

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

### gRPC Connection Issues

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

## See Also

- [Elixir Protobuf Library](https://github.com/elixir-protobuf/protobuf)
- [Elixir gRPC](https://github.com/elixir-grpc/grpc)
- [Phoenix Framework Integration](https://hexdocs.pm/phoenix)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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"
];
};
5 changes: 5 additions & 0 deletions doc/src/content/docs/reference/languages/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ All examples shown are fully functional and can be found in the [`examples/`](ht
Documentation →](./dart)
</Card>

<Card title="Elixir" icon="elixir">
Protocol Buffer and gRPC support for distributed systems and real-time applications. [View Elixir
Documentation →](./elixir)
</Card>

<Card title="Python" icon="python">
Multiple generation options including betterproto, mypy stubs, and gRPC
support. [View Python Documentation →](./python)
Expand Down
91 changes: 91 additions & 0 deletions examples/elixir-basic/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Elixir Basic Example

This example demonstrates basic Protocol Buffer code generation for Elixir using Bufrnix.

## Features

- Basic protobuf message generation
- Nested messages
- Enums
- Maps
- Oneof fields
- Repeated fields

## Prerequisites

- Nix with flakes enabled
- Elixir development environment (provided by the flake)

## Usage

### Generate Protobuf Code

```bash
# Generate the protobuf code
nix run

# Or enter the development shell and generate
nix develop
nix run
```

### Use in Elixir Project

After generation, the protobuf modules will be available in `lib/proto/`. You can use them in your Elixir code:

```elixir
# Create a message
message = %Proto.Example.V1.ExampleMessage{
id: 1,
name: "Alice",
email: "alice@example.com",
tags: ["elixir", "protobuf"],
created_at: %Proto.Example.V1.TimestampMessage{
seconds: System.system_time(:second),
nanos: 0
}
}

# Encode to binary
binary = Proto.Example.V1.ExampleMessage.encode(message)

# Decode from binary
decoded = Proto.Example.V1.ExampleMessage.decode(binary)
```

### Run the Example

```bash
# Enter the development shell
nix develop

# Install dependencies
mix deps.get

# Generate protobuf code
nix run

# Run the example
iex -S mix
iex> ExampleUsage.demo()
```

## Generated Files

After running `nix run`, you'll find the generated Elixir code in:

- `lib/proto/example/v1/example.pb.ex` - Generated protobuf modules

## Project Structure

```
.
├── flake.nix # Nix flake configuration
├── mix.exs # Elixir project file
├── proto/ # Protocol buffer definitions
│ └── example/v1/
│ └── example.proto
└── lib/ # Elixir source code
├── proto/ # Generated protobuf code (after running nix run)
└── example_usage.ex # Example usage code
```
Loading