# GenServer: Stateful Servers — Elixir & Phoenix

Source: https://www.geekswithgeeks.com/en/elixir-phoenix/o-genserver

> Implement a GenServer with a client API and server callbacks.

## The standard server behaviour

Writing receive loops by hand is error-prone, so OTP provides **GenServer**, a behaviour for a process that holds state and handles requests. You implement **callbacks**: **`init/1`** sets the initial state; **`handle_call/3`** handles **synchronous** requests from `GenServer.call`, replying with `{:reply, reply, new_state}`; **`handle_cast/2`** handles **asynchronous** `GenServer.cast` messages with `{:noreply, new_state}`; and **`handle_info/2`** handles any other messages, such as timers from `Process.send_after` or monitor notifications. By convention, a module exposes a **client API** (`Cart.add_item(pid, sku)`) that wraps `call` and `cast`, keeping the message format private. `GenServer.call` has a default **timeout of 5 seconds**. A GenServer processes **one message at a time**, which serialises access to its state, but also means a slow callback blocks everyone; move heavy work into Tasks. Use GenServers to model stateful resources: caches, rate limiters, connection managers, game rooms or a shopping cart per user.

## A shopping cart GenServer

Client API on top, callbacks below.

```elixir
defmodule Shop.Cart do
  use GenServer

  # ---- client API ----
  def start_link(customer_id),
    do: GenServer.start_link(__MODULE__, customer_id, name: via(customer_id))

  def add_item(customer_id, sku, qty), do: GenServer.cast(via(customer_id), {:add, sku, qty})
  def items(customer_id), do: GenServer.call(via(customer_id), :items)

  defp via(customer_id), do: {:via, Registry, {Shop.CartRegistry, customer_id}}

  # ---- server callbacks ----
  @impl true
  def init(customer_id) do
    Process.send_after(self(), :expire, :timer.minutes(30))
    {:ok, %{customer_id: customer_id, items: %{}}}
  end

  @impl true
  def handle_cast({:add, sku, qty}, state) do
    items = Map.update(state.items, sku, qty, &(&1 + qty))
    {:noreply, %{state | items: items}}
  end

  @impl true
  def handle_call(:items, _from, state), do: {:reply, state.items, state}

  @impl true
  def handle_info(:expire, state), do: {:stop, :normal, state}   # abandoned cart
end

# usage (with Registry started under the supervisor, see next topic):
# {:ok, _} = Shop.Cart.start_link("c-1")
# Shop.Cart.add_item("c-1", "pen", 2)
# Shop.Cart.items("c-1")       # %{"pen" => 2}
```

## A shop counter with one clerk

A GenServer is a counter with one clerk who keeps the ledger. Customers can wait for an answer (call) or drop a note in the box (cast). The clerk serves one customer at a time, so the ledger never gets mixed up, but a slow customer holds up the queue.

**Quiz:** Which GenServer callback handles synchronous requests made with GenServer.call?

- [ ] handle_cast/2
- [x] handle_call/3
- [ ] handle_info/2
- [ ] init/1

*Answer:* handle_call/3. handle_call replies to the caller, which waits up to a timeout.
