# Backend Configuration Guide
This guide covers the backend configuration of AshRpc, including router setup, resource exposure, and advanced configuration options.
> ⚠️ **EXPERIMENTAL WARNING**: AshRpc is still in early development and considered highly experimental. Breaking changes may occur frequently without notice. We strongly advise against using this package in production environments until it reaches a stable release (v1.0.0+).
## Router Configuration
### Basic Router Setup
The main entry point for AshRpc is the router module:
```elixir
defmodule MyAppWeb.TrpcRouter do
use AshRpc.Router,
domains: [MyApp.Accounts, MyApp.Billing, MyApp.Notifications]
end
```
Mount it in your Phoenix router:
```elixir
# In your Phoenix router
scope "/trpc" do
pipe_through :ash_rpc
forward "/", MyAppWeb.TrpcRouter
end
```
### Router Options
The router accepts several configuration options:
```elixir
defmodule MyAppWeb.TrpcRouter do
use AshRpc.Router,
# Required: List of Ash domains to expose
domains: [MyApp.Accounts, MyApp.Billing],
# Optional: Custom input/output transformer
transformer: MyApp.TrpcTransformer,
# Optional: Before request hooks
before: [MyApp.TrpcHooks.Logging],
# Optional: After request hooks
after: [MyApp.TrpcHooks.Metrics],
# Optional: Custom context creation function
create_context: &MyApp.TrpcContext.create/1,
# Optional: Custom middlewares
middlewares: [MyApp.TrpcMiddleware.Auth]
end
```
### Phoenix Pipeline Configuration
Configure the tRPC pipeline in your Phoenix router:
```elixir
defmodule MyAppWeb.Router do
use MyAppWeb, :router
# tRPC pipeline - JSON only with authentication
pipeline :ash_rpc do
plug :accepts, ["json"]
plug :retrieve_from_bearer # Extract JWT from Authorization header
plug :set_actor, :user # Set current user as actor
end
# Alternative: tRPC pipeline without authentication
pipeline :ash_rpc_public do
plug :accepts, ["json"]
end
# Mount tRPC endpoints
scope "/trpc" do
pipe_through :ash_rpc
forward "/", MyAppWeb.TrpcRouter
end
# Public tRPC endpoints (no auth required)
scope "/trpc/public" do
pipe_through :ash_rpc_public
forward "/", MyAppWeb.PublicTrpcRouter
end
end
```
## Resource Configuration
### Basic Resource Exposure
Configure your Ash resources to expose actions via tRPC:
```elixir
defmodule MyApp.Accounts.User do
use Ash.Resource,
extensions: [AshRpc],
domain: MyApp.Accounts
ash_rpc do
# Expose specific actions
expose [:read, :create, :update, :destroy]
# Or expose all actions
# expose :all
# Custom resource name (defaults to module name)
resource_name "user"
end
# ... rest of resource definition
end
```
### DSL Reference
#### `trpc` Block Options
- `expose`: Actions to expose (`:all` or list of action names)
- `resource_name`: Override default resource segment name
- `methods`: Override default method mappings (`[read: :query, create: :mutation]`)
#### Query Configuration
```elixir
query :read do
filterable true # Enable filtering (default: true)
sortable true # Enable sorting (default: true)
selectable true # Enable field selection (default: true)
paginatable true # Enable pagination (default: true)
relationships [:posts, :comments] # Loadable relationships
end
# Custom procedure name
query :search, :read do
filterable true
selectable false
relationships []
end
```
#### Mutation Configuration
```elixir
mutation :create, :create do
metadata fn subject, result, ctx ->
%{created_by: subject.id, timestamp: DateTime.utc_now()}
end
end
# Custom procedure name
mutation :register, :register_with_password do
metadata fn _subject, user, _ctx ->
%{token: user.__metadata__.token}
end
end
```
## Advanced Configuration
### Custom Transformers
Create custom input/output transformers:
```elixir
# lib/my_app/trpc_transformer.ex
defmodule MyApp.TrpcTransformer do
@behaviour AshRpc.Output.Transformer
@impl true
def decode_input(input, _ctx) do
# Transform input before processing
input
|> AshRpc.Output.Transformer.decode()
|> transform_keys()
end
@impl true
def encode_output(output, _ctx) do
# Transform output before sending
output
|> transform_response()
|> AshRpc.Output.Transformer.encode()
end
defp transform_keys(map) when is_map(map) do
Map.new(map, fn {k, v} ->
{transform_key(k), transform_value(v)}
end)
end
defp transform_key(key) when is_binary(key) do
# Convert camelCase to snake_case
key
|> String.replace(~r/([A-Z])/, "_\\1")
|> String.downcase()
end
defp transform_value(value) when is_map(value) do
transform_keys(value)
end
defp transform_value(value), do: value
defp transform_response(%{result: result} = response) do
%{response | result: transform_keys(result)}
end
defp transform_response(response), do: response
end
```
## Multiple Routers
Create separate routers for different API versions or access levels:
```elixir
# Public API router
defmodule MyAppWeb.PublicTrpcRouter do
use AshRpc.Router, domains: [MyApp.Public]
end
# Admin API router
defmodule MyAppWeb.AdminTrpcRouter do
use AshRpc.Router, domains: [MyApp.Admin]
end
# Internal API router
defmodule MyAppWeb.InternalTrpcRouter do
use AshRpc.Router,
domains: [MyApp.Internal],
# Different transformer for internal use
transformer: MyApp.InternalTrpcTransformer
end
```
Configure them in your Phoenix router:
```elixir
scope "/trpc" do
# Public endpoints
scope "/public" do
pipe_through :ash_rpc_public
forward "/", MyAppWeb.PublicTrpcRouter
end
# Authenticated endpoints
scope "/api" do
pipe_through :ash_rpc
forward "/", MyAppWeb.TrpcRouter
end
# Admin endpoints
scope "/admin" do
pipe_through [:trpc, :require_admin]
forward "/", MyAppWeb.AdminTrpcRouter
end
# Internal endpoints (not exposed publicly)
scope "/internal" do
pipe_through [:trpc, :require_internal]
forward "/", MyAppWeb.InternalTrpcRouter
end
end
```