# Banzai
[](https://github.com/threeaccents/banzai/actions/workflows/ci.yml)
[](https://hex.pm/packages/banzai)
[](https://hexdocs.pm/banzai)
A pipeline library for Elixir providing a token-based pattern for sequential function execution with automatic error handling.
> **Why "Banzai"?** The name comes from the [Banzai Pipeline](https://en.wikipedia.org/wiki/Banzai_Pipeline), the legendary surf reef break on the North Shore of Oahu, Hawaii — one of the most famous wave pipelines in the world.
Banzai implements a workflow pattern where a **token** (a map or struct) is passed through a series of **steps** (functions) that transform it. Each step receives the token, performs its operation, and returns an updated token. If any step fails, execution stops immediately and the error is returned.
## Installation
Add `banzai` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:banzai, "~> 0.1.0"}
]
end
```
## Quick Start
```elixir
defmodule CalculateTotal do
use Banzai
embedded_schema do
field :value, :integer
end
def perform(params) do
%__MODULE__{}
|> new(params)
|> step(fn token -> {:ok, Map.put(token, :value, token.value + 10)} end)
|> step(fn token -> {:ok, Map.put(token, :value, token.value * 2)} end)
|> step(fn token -> {:ok, Map.put(token, :value, token.value + 5)} end)
|> run()
end
end
CalculateTotal.perform(%{})
# => {:ok, %{value: 25}}
```
## How It Works
1. Create a token (map or struct) that holds your pipeline state
2. Add steps that transform the token — each returns `{:ok, updated_token}` or `{:error, reason}`
3. Run the pipeline — steps execute in order, stopping at the first error
```elixir
defmodule ProcessOrder do
use Banzai
embedded_schema do
field :order_id, :string
# internal token fields
field :total, :integer
field :charged, :boolean
embeds_one :order, Order
end
def perform(params) do
%__MODULE__{}
|> new(params)
|> step(&fetch_order/1)
|> step(&calculate_total/1)
|> step(&charge_customer/1)
|> run()
end
defp fetch_order(%__MODULE__{} = token) do
case Orders.get(token.order_id) do
nil -> {:error, :order_not_found}
order -> {:ok, %__MODULE__{token | order: order}}
end
end
defp calculate_total(%__MODULE__{} = token) do
{:ok, %__MODULE__{token | total: Order.total(token.order)}
end
defp charge_customer(%__MODULE__{} = token) do
case Billing.charge(token.order.customer_id, token.total) do
:ok -> {:ok, %__MODULE__{token | charged: true}
{:error, reason} -> {:error, reason}
end
end
end
```
## Error Handling
When a step returns `{:error, reason}`:
- Execution stops immediately — subsequent steps are skipped
- The error is logged with context (action ID, step index)
- `{:error, reason}` is returned to the caller
## License
MIT