InterviewPitch
Elixir interview questions

Elixir Interview Questions with Answers

Most Asked Elixir Interview Questions for Software Engineer Roles

100+ QuestionsDetailed AnswersCode ExamplesUpdated for 2026

Introduction

Elixir is a dynamic, functional programming language built on the Erlang VM (BEAM). Designed for building scalable, maintainable, and fault-tolerant applications, Elixir leverages the battle-tested OTP framework to create resilient systems with lightweight concurrency and message passing. Its Phoenix web framework delivers real-time capabilities through Channels and LiveView, making it a top choice for modern web and distributed systems. This comprehensive guide collects the most frequently asked Elixir interview questions, ranging from foundational concepts like pattern matching, recursion, and the pipe operator to advanced topics such as OTP behaviours, Ecto, Phoenix LiveView, distributed Elixir, and performance optimization. Whether you are starting your Elixir journey or preparing for a senior role, these questions will help you solidify your knowledge and ace your next technical interview.

Why Elixir?

  • Strong static typing – catches errors at compile time, reducing runtime bugs
  • Excellent tooling and IDE support – autocompletion, navigation, and refactoring
  • Superset of JavaScript – works seamlessly with all existing JavaScript libraries
  • Used by major frameworks like React, Angular, and Vue – essential for large-scale apps
  • Enables scalable and maintainable enterprise-grade applications
  • Growing community, continuous improvements, and high demand in the job market

Most Asked Elixir Interview Questions

Beginner
1. What is Elixir and what are its key features?

Elixir is a functional, concurrent programming language built on the Erlang VM (BEAM). It is designed for building scalable and maintainable applications with high fault tolerance.

  • Functional Programming: Immutable data and pure functions
  • Concurrency: Lightweight processes and message passing
  • Fault Tolerance: Supervisor trees and "let it crash" philosophy
  • Hot Code Upgrades: Update code without stopping the system
  • Pattern Matching: Powerful matching and destructuring
elixir
# Hello World in Elixir
IO.puts("Hello, World!")
Beginner
2. What are Data Types in Elixir?

Elixir provides a rich set of immutable data types including integers, floats, booleans, strings, atoms, lists, tuples, and maps.

  • Integers: Arbitrary precision integers
  • Floats: IEEE 754 double-precision
  • Booleans: true and false
  • Strings: UTF-8 encoded binaries
  • Atoms: Constants whose name is their value
  • Lists: Linked lists with head/tail
  • Tuples: Fixed-size collections
  • Maps: Key-value pairs
elixir
# Data Types in Elixir
age = 25
salary = 50000.50
pi = 3.14159265358979
grade = ?A
is_active = true
name = "Alice"
price = 99.99

IO.puts("Age: #{age}")
IO.puts("Salary: #{salary}")
IO.puts("Pi: #{pi}")
IO.puts("Grade: #{grade}")
IO.puts("Active: #{is_active}")
IO.puts("Name: #{name}")
IO.puts("Price: #{price}")
Beginner
3. What are Variables and Constants in Elixir?

Elixir uses immutable variables that cannot be reassigned. Constants can be defined using module attributes with the @ syntax. Variables are bound through pattern matching and cannot be mutated.

  • Variables: Immutable, can be rebound but not mutated
  • Module Attributes: @constant for compile-time constants
  • Pattern Matching: Bind variables through matching
  • Pinning Operator: ^ to use existing value in pattern matching
  • Rebinding: Variables can be rebound to new values
elixir
# Variables and Constants in Elixir
x = 10
pi = 3.14159
val = 3.14
str = "Hello"

IO.puts("x = #{x}")
IO.puts("pi = #{pi}")
IO.puts("val = #{val}")
IO.puts("str = #{str}")
Beginner
4. What is Pattern Matching in Elixir?

Pattern Matching allows matching values against patterns and binding variables. It's a fundamental feature used throughout Elixir for destructuring data structures and controlling program flow.

  • Match Operator: = for pattern matching
  • List Matching: [head | tail] pattern
  • Tuple Matching: {a, b} pattern
  • Map Matching: %{key: value} pattern
  • Pinning: ^ to match existing value
elixir
# Pattern Matching in Elixir
{x, y} = {3, 4}
IO.puts("x = #{x}, y = #{y}")

[head | tail] = [1, 2, 3, 4]
IO.puts("Head: #{head}, Tail: #{inspect(tail)}")

%{name: name, age: age} = %{name: "Alice", age: 25}
IO.puts("Name: #{name}, Age: #{age}")

# Pinning operator
x = 10
^x = 10  # Matches
# ^x = 20 # Will not match, raises error

# Pattern matching in function clauses
defmodule Math do
  def add({a, b}), do: a + b
  def add([a, b]), do: a + b
  def add(%{a: a, b: b}), do: a + b
end

IO.puts(Math.add({3, 4}))
IO.puts(Math.add([3, 4]))
IO.puts(Math.add(%{a: 3, b: 4}))
Beginner
5. What are Modules and Functions in Elixir?

Modules are used to group related functions. Functions are defined using def and can have multiple clauses for pattern matching. Private functions are defined with defp.

  • defmodule — Defines a module
  • def — Defines a public function
  • defp — Defines a private function
  • Multiple Clauses: Different implementations for different patterns
  • Guard Clauses: when for additional conditions
elixir
# Modules and Functions in Elixir
defmodule Math do
  # Public function
  def add(a, b) do
    a + b
  end
  
  # Private function
  defp subtract(a, b) do
    a - b
  end
  
  # One-liner function
  def multiply(a, b), do: a * b
  
  # Multiple clauses
  def divide(a, 0), do: {:error, "Division by zero"}
  def divide(a, b), do: {:ok, a / b}
  
  # Guard clauses
  def factorial(0), do: 1
  def factorial(n) when n > 0 do
    n * factorial(n - 1)
  end
  
  # Default arguments
  def greet(name, greeting \ "Hello") do
    "#{greeting}, #{name}!"
  end
end

IO.puts(Math.add(10, 20))
IO.puts(Math.multiply(5, 4))
IO.puts(Math.factorial(5))
IO.puts(Math.greet("Alice"))
IO.puts(Math.greet("Bob", "Hi"))
Beginner
6. What is Recursion in Elixir?

Recursion is a technique where a function calls itself. Elixir prefers recursion over loops due to immutability and the absence of traditional loops. Tail call optimization ensures efficiency.

  • Base Case: Stopping condition
  • Recursive Case: Self-call with smaller input
  • Tail Call Optimization: Efficient recursion without stack overflow
  • Recursive Functions: Factorial, Fibonacci, list processing
  • Accumulator Pattern: Efficient recursion with accumulators
elixir
# Recursion in Elixir
defmodule Recursion do
  # Factorial
  def factorial(0), do: 1
  def factorial(n) when n > 0 do
    n * factorial(n - 1)
  end
  
  # Fibonacci
  def fibonacci(0), do: 0
  def fibonacci(1), do: 1
  def fibonacci(n) when n > 1 do
    fibonacci(n - 1) + fibonacci(n - 2)
  end
  
  # List processing
  def sum([]), do: 0
  def sum([head | tail]) do
    head + sum(tail)
  end
  
  def length([]), do: 0
  def length([_ | tail]) do
    1 + length(tail)
  end
  
  # Tail recursion
  def reverse(list), do: reverse(list, [])
  defp reverse([], acc), do: acc
  defp reverse([head | tail], acc) do
    reverse(tail, [head | acc])
  end
  
  # Map function with recursion
  def map([], _func), do: []
  def map([head | tail], func) do
    [func.(head) | map(tail, func)]
  end
  
  # Filter function with recursion
  def filter([], _predicate), do: []
  def filter([head | tail], predicate) do
    if predicate.(head) do
      [head | filter(tail, predicate)]
    else
      filter(tail, predicate)
    end
  end
end

IO.puts("Factorial 5: #{Recursion.factorial(5)}")
IO.puts("Fibonacci 8: #{Recursion.fibonacci(8)}")
IO.puts("Sum [1,2,3,4,5]: #{Recursion.sum([1,2,3,4,5])}")
IO.puts("Length [1,2,3,4,5]: #{Recursion.length([1,2,3,4,5])}")
IO.puts("Reverse [1,2,3]: #{inspect(Recursion.reverse([1,2,3]))}")
IO.puts("Map [1,2,3] *2: #{inspect(Recursion.map([1,2,3], fn x -> x * 2 end))}")
IO.puts("Filter [1,2,3,4,5] even: #{inspect(Recursion.filter([1,2,3,4,5], fn x -> rem(x, 2) == 0 end))}")
Beginner
7. What is the Enum Module in Elixir?

The Enum module provides a rich set of functions to work with collections like lists, maps, and streams. It offers functional programming operations for data transformation.

  • Enum.map — Transform elements
  • Enum.filter — Filter elements
  • Enum.reduce — Aggregate values
  • Enum.sort — Sort elements
  • Enum.each — Iterate for side effects
elixir
# Enum Module in Elixir
list = [5, 1, 8, 3, 9, 2, 7]

# Map - transform elements
doubled = Enum.map(list, fn x -> x * 2 end)
IO.puts("Doubled: #{inspect(doubled)}")

# Filter - select elements
evens = Enum.filter(list, fn x -> rem(x, 2) == 0 end)
IO.puts("Evens: #{inspect(evens)}")

# Reduce - accumulate values
sum = Enum.reduce(list, 0, fn x, acc -> x + acc end)
IO.puts("Sum: #{sum}")

# Sort - order elements
sorted = Enum.sort(list)
IO.puts("Sorted: #{inspect(sorted)}")

# Map with capture syntax
squares = Enum.map(list, &(&1 * &1))
IO.puts("Squares: #{inspect(squares)}")

# Filter with capture
greater_than_5 = Enum.filter(list, &(&1 > 5))
IO.puts("> 5: #{inspect(greater_than_5)}")

# Each - side effects
Enum.each(list, fn x -> IO.puts("Item: #{x}") end)

# Find - find first matching
first_even = Enum.find(list, fn x -> rem(x, 2) == 0 end)
IO.puts("First even: #{first_even}")

# Any? - check if any matches
has_even = Enum.any?(list, fn x -> rem(x, 2) == 0 end)
IO.puts("Has even: #{has_even}")

# All? - check if all match
all_even = Enum.all?(list, fn x -> rem(x, 2) == 0 end)
IO.puts("All even: #{all_even}")

# Join - convert to string
joined = Enum.join(list, ", ")
IO.puts("Joined: #{joined}")

# With_index - add index
with_index = Enum.with_index(list)
IO.puts("With index: #{inspect(with_index)}")
Beginner
8. What is the Pipe Operator in Elixir?

The Pipe Operator (|>) passes the result of one function as the first argument to the next function, making code more readable and expressive by creating a data pipeline.

  • Function Chaining: Chain multiple operations
  • Readability: Read left-to-right flow
  • Composition: Easily compose functions
  • Pipeline Pattern: Common in Elixir codebases
  • Multiple Arguments: Works with functions taking multiple arguments
elixir
# Pipe Operator in Elixir
list = [5, 1, 8, 3, 9, 2, 7]

# Without pipe - nested function calls
result = Enum.sum(Enum.filter(Enum.map(list, fn x -> x * 2 end), fn x -> x > 10 end))
IO.puts("Without pipe: #{result}")

# With pipe - readable flow
result = list
|> Enum.map(&(&1 * 2))
|> Enum.filter(&(&1 > 10))
|> Enum.sum()

IO.puts("With pipe: #{result}")

# Chaining multiple operations
list
|> Enum.map(&(&1 * 2))
|> Enum.filter(&(&1 > 10))
|> Enum.sort()
|> IO.inspect(label: "Result")

# Pipe with multiple arguments
defmodule Math do
  def double(x), do: x * 2
  def add(x, y), do: x + y
  def square(x), do: x * x
end

result = 5
|> Math.double()
|> Math.add(10)
|> Math.square()

IO.puts("Result: #{result}")

# Pipe with functions that take multiple arguments
result = 10
|> Kernel.+(5)
|> Kernel.*(2)

IO.puts("Result: #{result}")
Intermediate
9. What are GenServer and OTP in Elixir?

GenServer is a behaviour module for implementing server processes. OTP (Open Telecom Platform) provides a set of libraries and design principles for building fault-tolerant systems with supervision trees.

  • GenServer: Generic server implementation
  • Client/Server API: call and cast
  • OTP Behaviours: GenServer, Supervisor, Application
  • Fault Tolerance: Supervisors and restart strategies
  • State Management: Maintain state between calls
elixir
# GenServer Example in Elixir
defmodule Counter do
  use GenServer
  
  # Client API
  def start_link(initial_value) do
    GenServer.start_link(__MODULE__, initial_value, name: __MODULE__)
  end
  
  def increment do
    GenServer.call(__MODULE__, :increment)
  end
  
  def decrement do
    GenServer.call(__MODULE__, :decrement)
  end
  
  def get_count do
    GenServer.call(__MODULE__, :get_count)
  end
  
  def reset do
    GenServer.call(__MODULE__, :reset)
  end
  
  # Server Callbacks
  def init(initial_value) do
    {:ok, initial_value}
  end
  
  def handle_call(:increment, _from, state) do
    {:reply, state + 1, state + 1}
  end
  
  def handle_call(:decrement, _from, state) do
    {:reply, state - 1, state - 1}
  end
  
  def handle_call(:get_count, _from, state) do
    {:reply, state, state}
  end
  
  def handle_call(:reset, _from, _state) do
    {:reply, 0, 0}
  end
  
  # Handle cast (fire and forget)
  def handle_cast({:set, new_value}, _state) do
    {:noreply, new_value}
  end
  
  # Handle info (from external)
  def handle_info(:tick, state) do
    IO.puts("Tick: #{state}")
    {:noreply, state + 1}
  end
end

# Usage
{:ok, pid} = Counter.start_link(0)
Counter.increment()
Counter.increment()
Counter.decrement()
IO.puts("Count: #{Counter.get_count()}")
Counter.reset()
IO.puts("After reset: #{Counter.get_count()}")

# Cast
GenServer.cast(Counter, {:set, 100})
IO.puts("After cast: #{Counter.get_count()}")

# Send info
send(Counter, :tick)
Intermediate
10. What are Supervisors in Elixir?

Supervisors are processes that monitor other processes and restart them if they crash, providing fault tolerance and self-healing capabilities through supervision trees.

  • Supervision Tree: Hierarchical process structure
  • Restart Strategies: :one_for_one, :one_for_all, :rest_for_one
  • Child Specifications: Define how to start children
  • Dynamic Supervision: DynamicSupervisor for dynamic children
  • Supervisor Flags: :permanent, :transient, :temporary
elixir
# Supervisor Example in Elixir
defmodule MyApp.Application do
  use Application
  
  def start(_type, _args) do
    children = [
      # Start the counter GenServer
      {Counter, 0},
      # Start a task
      {Task, fn -> IO.puts("Task started") end},
      # Start a registry
      {Registry, keys: :unique, name: MyRegistry},
      # Start dynamic supervisor
      {DynamicSupervisor, strategy: :one_for_one, name: MyDynamicSupervisor}
    ]
    
    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

# Start the supervision tree
{:ok, pid} = MyApp.Application.start(:normal, [])
IO.puts("Supervisor started")

# Get supervisor children
children = Supervisor.which_children(MyApp.Supervisor)
IO.puts("Children: #{inspect(children)}")
Intermediate
11. What are Tasks in Elixir?

Tasks are used for asynchronous computations and parallel processing. They provide a simple way to run code concurrently with built-in timeout support and supervision.

  • Async/Await: Task.async and Task.await
  • Parallel Processing: Run multiple tasks concurrently
  • Task Supervision: Task.Supervisor for supervised tasks
  • Timeouts: Handle slow tasks with timeouts
  • Task.async_stream: Process streams concurrently
elixir
# Task Module in Elixir
# Async task
task = Task.async(fn ->
  Process.sleep(1000)
  "Task completed"
end)

IO.puts("Task started...")
result = Task.await(task)
IO.puts("Result: #{result}")

# Task with timeout
task = Task.async(fn ->
  Process.sleep(5000)
  "Slow task"
end)

try do
  Task.await(task, 2000)
rescue
  e in Task.TimeoutError -> IO.puts("Task timed out!")
end

# Async stream with multiple tasks
tasks = 1..5
|> Enum.map(fn i ->
  Task.async(fn ->
    Process.sleep(i * 100)
    i * i
  end)
end)

results = Enum.map(tasks, &Task.await/1)
IO.puts("Results: #{inspect(results)}")

# Task with supervisor
defmodule MyTask do
  def start_link do
    Task.start_link(fn ->
      Process.sleep(1000)
      IO.puts("Task completed")
    end)
  end
end

# Task started
{:ok, _} = MyTask.start_link()

# Task as function
Task.async(fn ->
  IO.puts("Running task")
end)
|> Task.await()
Advanced
12. What is ETS (Erlang Term Storage) in Elixir?

ETS is an in-memory storage system for fast lookups. It provides efficient key-value storage with various table types and is ideal for caching and temporary data storage.

  • Table Types: :set, :ordered_set, :bag
  • Operations: Insert, lookup, delete, update
  • Performance: Very fast read/write operations
  • Use Cases: Caching, lookups, temporary storage
  • Match Specifications: Complex query patterns
elixir
# ETS (Erlang Term Storage) in Elixir
# Create an ETS table
table = :ets.new(:my_table, [:set, :public, :named_table])

# Insert data
:ets.insert(:my_table, {:user1, "Alice", 25})
:ets.insert(:my_table, {:user2, "Bob", 30})
:ets.insert(:my_table, {:user3, "Carol", 22})

# Lookup data
case :ets.lookup(:my_table, :user1) do
  [{:user1, name, age}] ->
    IO.puts("User: #{name}, Age: #{age}")
  [] ->
    IO.puts("User not found")
end

# Update data
:ets.insert(:my_table, {:user1, "Alice Johnson", 26})

# Delete data
:ets.delete(:my_table, :user2)

# Get all data
all_data = :ets.tab2list(:my_table)
IO.puts("All data: #{inspect(all_data)}")

# Match data
matches = :ets.match(:my_table, {:user1, :"$1", :"$2"})
IO.puts("Matches: #{inspect(matches)}")

# Match with conditions
matches = :ets.match(:my_table, {:"$1", :"$2", 25})
IO.puts("Users with age 25: #{inspect(matches)}")

# Delete all
:ets.delete_all_objects(:my_table)

# Clean up
:ets.delete(:my_table)
Advanced
13. What is Ecto and how does it work?

Ecto is a database wrapper and query generator for Elixir applications. It provides a type-safe, composable query API with support for multiple databases and migrations.

  • Repo: Database repository module
  • Schemas: Define data models
  • Changesets: Data validation and transformation
  • Queries: from syntax for composable queries
  • Migrations: Database schema management
elixir
# Ecto Query Examples
defmodule MyApp.Repo do
  use Ecto.Repo,
    otp_app: :my_app,
    adapter: Ecto.Adapters.Postgres
end

defmodule MyApp.User do
  use Ecto.Schema
  import Ecto.Changeset
  
  schema "users" do
    field :name, :string
    field :email, :string
    field :age, :integer
    timestamps()
  end
  
  def changeset(user, attrs) do
    user
    |> cast(attrs, [:name, :email, :age])
    |> validate_required([:name, :email])
    |> validate_format(:email, ~r/@/)
    |> validate_number(:age, greater_than: 0)
  end
end

defmodule MyApp.Post do
  use Ecto.Schema
  import Ecto.Changeset
  
  schema "posts" do
    field :title, :string
    field :content, :string
    belongs_to :user, MyApp.User
    timestamps()
  end
end

# Basic Queries
alias MyApp.{Repo, User, Post}

# Insert
user = User.changeset(%User{}, %{name: "Alice", email: "alice@email.com", age: 25})
case Repo.insert(user) do
  {:ok, user} -> IO.puts("User created: #{user.name}")
  {:error, changeset} -> IO.puts("Error: #{inspect(changeset.errors)}")
end

# Query
query = from u in User, where: u.age > 18, select: u
users = Repo.all(query)
IO.puts("Users over 18: #{inspect(users)}")

# Query with order
query = from u in User, order_by: [desc: u.age], select: u
users = Repo.all(query)

# Update
user = Repo.get(User, 1)
updated = User.changeset(user, %{age: 26})
Repo.update(updated)

# Delete
Repo.delete(user)

# Join query
query = from u in User,
  join: p in assoc(u, :posts),
  where: p.title == "Hello",
  select: {u.name, p.title}
results = Repo.all(query)

# Preload
user = Repo.get(User, 1)
user = Repo.preload(user, :posts)
IO.puts("User posts: #{inspect(user.posts)}")
Advanced
14. What is Phoenix Framework?

Phoenix is a web framework for Elixir built on OTP and Plug. It provides real-time features with channels and LiveView, with high performance and low latency.

  • MVC Pattern: Controllers, views, templates
  • Channels: Real-time communication via WebSockets
  • LiveView: Interactive, server-rendered UIs
  • Performance: High concurrency and low latency
  • Ecto Integration: Built-in database support
elixir
# Phoenix Framework Example
defmodule MyAppWeb.Router do
  use Phoenix.Router
  import Phoenix.LiveView.Router
  
  scope "/", MyAppWeb do
    pipe_through :browser
    
    get "/", PageController, :index
    get "/hello/:name", PageController, :hello
    resources "/users", UserController
    
    # LiveView routes
    live "/counter", CounterLive
    live "/dashboard", DashboardLive
  end
  
  scope "/api", MyAppWeb do
    pipe_through :api
    
    resources "/users", UserController, only: [:index, :create, :show, :update, :delete]
  end
end

defmodule MyAppWeb.PageController do
  use MyAppWeb, :controller
  
  def index(conn, _params) do
    render(conn, "index.html", message: "Hello, World!")
  end
  
  def hello(conn, %{"name" => name}) do
    render(conn, "hello.html", name: name)
  end
end

defmodule MyAppWeb.UserController do
  use MyAppWeb, :controller
  
  def index(conn, _params) do
    users = Repo.all(User)
    render(conn, "index.json", users: users)
  end
  
  def create(conn, %{"user" => user_params}) do
    changeset = User.changeset(%User{}, user_params)
    case Repo.insert(changeset) do
      {:ok, user} -> render(conn, "show.json", user: user)
      {:error, changeset} -> conn |> put_status(400) |> render("error.json", changeset: changeset)
    end
  end
  
  def show(conn, %{"id" => id}) do
    user = Repo.get(User, id)
    render(conn, "show.json", user: user)
  end
  
  def update(conn, %{"id" => id, "user" => user_params}) do
    user = Repo.get(User, id)
    changeset = User.changeset(user, user_params)
    case Repo.update(changeset) do
      {:ok, user} -> render(conn, "show.json", user: user)
      {:error, changeset} -> conn |> put_status(400) |> render("error.json", changeset: changeset)
    end
  end
  
  def delete(conn, %{"id" => id}) do
    user = Repo.get(User, id)
    Repo.delete(user)
    send_resp(conn, 204, "")
  end
end
Advanced
15. What is Phoenix LiveView?

Phoenix LiveView enables building rich, real-time user interfaces with server-rendered HTML. It syncs state between server and client automatically over WebSockets.

  • Server-side Rendering: HTML rendered on server
  • Real-time Updates: State changes pushed to client
  • WebSocket Connection: Persistent connection for updates
  • Event Handling: phx-click and other bindings
  • Performance: Minimal client-side JavaScript
elixir
# Phoenix LiveView Example
defmodule MyAppWeb.CounterLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, count: 0, history: [])}
  end
  
  def render(assigns) do
    ~H"""
    <div class="counter-container">
      <h1>Count: <%= @count %></h1>
      <div class="buttons">
        <button phx-click="increment" phx-value-amount="1">+1</button>
        <button phx-click="increment" phx-value-amount="5">+5</button>
        <button phx-click="decrement">-1</button>
        <button phx-click="reset">Reset</button>
      </div>
      <div class="history">
        <h3>History</h3>
        <ul>
          <%= for entry <- @history do %>
            <li><%= entry %></li>
          <% end %>
        </ul>
      </div>
    </div>
    """
  end
  
  def handle_event("increment", %{"amount" => amount}, socket) do
    count = socket.assigns.count + String.to_integer(amount)
    history = ["Incremented by #{amount}" | socket.assigns.history]
    {:noreply, assign(socket, count: count, history: history)}
  end
  
  def handle_event("decrement", _params, socket) do
    count = socket.assigns.count - 1
    history = ["Decremented by 1" | socket.assigns.history]
    {:noreply, assign(socket, count: count, history: history)}
  end
  
  def handle_event("reset", _params, socket) do
    history = ["Reset" | socket.assigns.history]
    {:noreply, assign(socket, count: 0, history: history)}
  end
end

# PubSub for real-time updates
defmodule MyAppWeb.ChatLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    if connected?(socket) do
      MyAppWeb.Endpoint.subscribe("chat:global")
    end
    {:ok, assign(socket, messages: [])}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Chat</h2>
      <div id="messages">
        <%= for message <- @messages do %>
          <div><%= message %></div>
        <% end %>
      </div>
      <form phx-submit="send_message">
        <input type="text" name="message" placeholder="Type a message..." />
        <button type="submit">Send</button>
      </form>
    </div>
    """
  end
  
  def handle_event("send_message", %{"message" => message}, socket) do
    MyAppWeb.Endpoint.broadcast("chat:global", "new_message", message)
    {:noreply, socket}
  end
  
  def handle_info(%{event: "new_message", payload: message}, socket) do
    {:noreply, update(socket, :messages, fn msgs -> [message | msgs] end)}
  end
end
Advanced
16. What is Plug in Elixir?

Plug is a specification and toolkit for building composable web modules. It is the foundation for Phoenix and other web frameworks, providing a simple HTTP abstraction.

  • Plug Functions: Define request/response handlers
  • Plug Pipeline: Chain multiple plugs together
  • Conn Struct: Represents request/response
  • Router: Route matching and dispatching
  • Adapters: Cowboy, Bandit, and others
elixir
# Plug Example
defmodule MyApp.PlugExample do
  import Plug.Conn
  
  def init(options), do: options
  
  def call(conn, _opts) do
    conn
    |> put_resp_content_type("text/plain")
    |> send_resp(200, "Hello, World!")
  end
end

defmodule MyApp.Router do
  use Plug.Router
  
  plug(:match)
  plug(:dispatch)
  
  get "/" do
    send_resp(conn, 200, "Welcome to Plug!")
  end
  
  get "/hello/:name" do
    send_resp(conn, 200, "Hello, #{name}!")
  end
  
  post "/api/users" do
    {:ok, body, conn} = read_body(conn)
    case Jason.decode(body) do
      {:ok, params} ->
        send_resp(conn, 201, Jason.encode!(%{message: "User created", user: params}))
      {:error, _} ->
        send_resp(conn, 400, Jason.encode!(%{error: "Invalid JSON"}))
    end
  end
  
  get "/api/users" do
    users = [%{id: 1, name: "Alice"}, %{id: 2, name: "Bob"}]
    send_resp(conn, 200, Jason.encode!(users))
  end
  
  match _ do
    send_resp(conn, 404, Jason.encode!(%{error: "Not found"}))
  end
end

# Custom Plug
defmodule MyApp.AuthPlug do
  import Plug.Conn
  
  def init(options), do: options
  
  def call(conn, _opts) do
    case get_req_header(conn, "authorization") do
      ["Bearer " <> token] ->
        case validate_token(token) do
          {:ok, user} -> assign(conn, :current_user, user)
          {:error, _} -> conn |> send_resp(401, "Unauthorized") |> halt()
        end
      _ ->
        conn |> send_resp(401, "Unauthorized") |> halt()
    end
  end
  
  defp validate_token(token) do
    # Validate JWT or other token
    {:ok, %{id: 1, name: "Alice"}}
  end
end

# Run the router
{:ok, _} = Plug.Cowboy.http(MyApp.Router, [])
Advanced
17. What are Protocols in Elixir?

Protocols provide polymorphism for different data types. They allow you to define behavior that can be implemented for various types, similar to interfaces in other languages.

  • Protocol Definition: defprotocol
  • Protocol Implementation: defimpl for specific types
  • Data Types: Any data type can implement protocols
  • Use Cases: Custom serialization, formatting, inspection
  • Fallback: @fallback_to_any for default implementation
elixir
# Protocol Example in Elixir
defprotocol Greeter do
  def greet(person)
end

# Implement protocol for various types
defimpl Greeter, for: String do
  def greet(name) do
    "Hello, #{name}!"
  end
end

defimpl Greeter, for: Map do
  def greet(person) do
    "Hello, #{person[:name]}! You are #{person[:age]} years old."
  end
end

defimpl Greeter, for: List do
  def greet([name, age]) do
    "Hello, #{name}! You are #{age} years old."
  end
end

defimpl Greeter, for: Integer do
  def greet(id) do
    "User with ID: #{id}"
  end
end

defimpl Greeter, for: Tuple do
  def greet({name, age, city}) do
    "Hello, #{name} from #{city}! You are #{age} years old."
  end
end

# Fallback to any
defimpl Greeter, for: Any do
  def greet(_), do: "Greetings!"
end

# Usage
IO.puts(Greeter.greet("Alice"))
IO.puts(Greeter.greet(%{name: "Bob", age: 30}))
IO.puts(Greeter.greet(["Carol", 22]))
IO.puts(Greeter.greet(123))
IO.puts(Greeter.greet({"Dave", 35, "NYC"}))

# Protocol inheritance
defprotocol Serializer do
  def to_json(data)
end

defimpl Serializer, for: Map do
  def to_json(data) do
    Jason.encode!(data)
  end
end

defimpl Serializer, for: List do
  def to_json(data) do
    Jason.encode!(data)
  end
end

defimpl Serializer, for: Atom do
  def to_json(data) do
    Jason.encode!(Atom.to_string(data))
  end
end

data = %{name: "Alice", age: 25}
IO.puts("JSON: #{Serializer.to_json(data)}")
Advanced
18. What are Macros in Elixir?

Macros allow metaprogramming by generating code at compile time. They enable powerful abstractions and DSL creation by manipulating the abstract syntax tree (AST).

  • Quote/Unquote: quote and unquote for code generation
  • Compile-time Execution: Macros run during compilation
  • Custom Constructs: Create custom syntax and constructs
  • DSL Creation: Build domain-specific languages
  • Hygiene: Macros are hygienic by default
elixir
# Macros in Elixir
defmodule MyMacros do
  defmacro unless(condition, do: expression) do
    quote do
      if !unquote(condition) do
        unquote(expression)
      end
    end
  end
  
  defmacro debug(expression) do
    quote do
      IO.puts("Expression: #{unquote(expression)}")
      result = unquote(expression)
      IO.puts("Result: #{result}")
      result
    end
  end
  
  defmacro log(message) do
    quote do
      IO.puts("[LOG] #{unquote(message)} at #{DateTime.utc_now()}")
    end
  end
  
  defmacro assert(expression, message \ "Assertion failed") do
    quote do
      if !unquote(expression) do
        raise unquote(message)
      end
    end
  end
  
  defmacro chain(expressions) do
    quote do
      unquote(expressions)
      |> Enum.reduce(fn expr, acc -> expr + acc end)
    end
  end
  
  defmacro unless(condition, do: do_block, else: else_block) do
    quote do
      if unquote(condition) do
        unquote(else_block)
      else
        unquote(do_block)
      end
    end
  end
end

defmodule MyApp do
  require MyMacros
  
  def test do
    MyMacros.unless true do
      IO.puts("This won't print")
    end
    
    MyMacros.unless false do
      IO.puts("This will print")
    end
    
    x = 10
    MyMacros.debug(x * 2)
    
    MyMacros.log("Application started")
    
    MyMacros.assert(x > 5, "x must be greater than 5")
    
    result = MyMacros.chain([1, 2, 3, 4, 5])
    IO.puts("Chain result: #{result}")
    
    # Custom unless with else
    MyMacros.unless true do
      IO.puts("This won't print")
    else
      IO.puts("This will print")
    end
  end
end

MyApp.test()
Advanced
19. What is Distributed Elixir?

Distributed Elixir allows multiple Elixir nodes to communicate over a network, enabling distributed systems and fault tolerance with built-in Erlang distribution.

  • Node Communication: Connect nodes via Node.connect
  • Remote Spawning: Node.spawn for remote process creation
  • Distributed Erlang: Built on Erlang's distribution capabilities
  • Network Topology: Fully connected mesh network
  • Distributed Transactions: Across multiple nodes
elixir
# Distributed Elixir Example
# Node 1
# Start node with name
# iex --name node1@127.0.0.1 --cookie secret

# Node 2
# iex --name node2@127.0.0.1 --cookie secret

defmodule Distributed do
  def ping(node) do
    Node.ping(node)
  end
  
  def list_nodes do
    Node.list()
  end
  
  def send_message(node, message) do
    Node.spawn(node, fn ->
      IO.puts("Received message: #{message}")
    end)
  end
  
  def remote_call(node, module, function, args) do
    case Node.spawn(node, fn ->
      apply(module, function, args)
    end) do
      pid when is_pid(pid) -> {:ok, pid}
      error -> {:error, error}
    end
  end
  
  def remote_call_with_result(node, module, function, args) do
    result = :rpc.call(node, module, function, args)
    case result do
      {:badrpc, _} -> {:error, "RPC failed"}
      _ -> {:ok, result}
    end
  end
  
  def register_node(name) do
    Node.set_cookie(:secret)
    Node.start(name)
  end
end

# Usage on node1
Distributed.ping(:node2@127.0.0.1)
Distributed.list_nodes()
Distributed.send_message(:node2@127.0.0.1, "Hello from node1")

# Execute function on remote node
Distributed.remote_call(:node2@127.0.0.1, IO, :puts, ["Hello from remote"])

# Remote call with result
Distributed.remote_call_with_result(:node2@127.0.0.1, :erlang, :now, [])
Advanced
20. What are Streams in Elixir?

Streams enable lazy, composable operations on collections. They process data on-demand, making them memory efficient and ideal for handling large datasets.

  • Lazy Evaluation: Process only when needed
  • Memory Efficiency: Handle large datasets
  • Composability: Chain operations together
  • Infinite Streams: Create infinite sequences
  • Chunking: Process data in chunks
elixir
# Stream Module in Elixir
# Create a stream
stream = Stream.cycle([1, 2, 3])
|> Enum.take(10)
IO.puts("Cyclic stream: #{inspect(stream)}")

# Generate stream
stream = Stream.iterate(0, &(&1 + 2))
|> Enum.take(5)
IO.puts("Even numbers: #{inspect(stream)}")

# File stream
File.stream!("data.txt")
|> Stream.map(&String.trim/1)
|> Stream.filter(&(&1 != ""))
|> Enum.each(&IO.puts/1)

# Lazy processing
data = 1..1000
|> Stream.map(&(&1 * 2))
|> Stream.filter(&rem(&1, 3) == 0)
|> Enum.take(10)

IO.puts("First 10 numbers divisible by 3 after doubling: #{inspect(data)}")

# Streaming with chunk
stream = 1..100
|> Stream.chunk_every(10)
|> Stream.map(fn chunk -> Enum.sum(chunk) end)
|> Enum.take(5)

IO.puts("Sums of chunks: #{inspect(stream)}")

# Infinite stream with recursion
defmodule RandomStream do
  def stream do
    Stream.repeatedly(fn -> :rand.uniform(100) end)
  end
  
  def take(n) do
    stream()
    |> Enum.take(n)
  end
end

random_numbers = RandomStream.take(10)
IO.puts("Random numbers: #{inspect(random_numbers)}")

# Stream with chunk_by
stream = 1..20
|> Stream.chunk_by(fn x -> rem(x, 2) == 0 end)
|> Enum.take(5)

IO.puts("Chunked: #{inspect(stream)}")
Advanced
21. What is OTP Application structure?

OTP Applications provide a standardized structure for organizing code, dependencies, and starting the supervision tree. They are the building blocks of Elixir systems.

  • Application Callback: start/2 and stop/1
  • Supervision Tree: Root supervisor for all processes
  • Configuration: config/config.exs for app settings
  • Dependencies: Managed via mix.exs
  • Environment: Mix.env() for runtime environment
elixir
# OTP Application Structure
defmodule MyApp do
  use Application
  
  def start(_type, _args) do
    # Start the supervision tree
    children = [
      # Start the HTTP server
      {Plug.Cowboy, scheme: :http, plug: MyApp.Router, options: [port: 4000]},
      # Start the database
      MyApp.Repo,
      # Start the cache
      {Cache, []},
      # Start the scheduler
      {Scheduler, []},
      # Start the registry
      {Registry, keys: :unique, name: MyApp.Registry}
    ]
    
    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
  
  def stop(_state) do
    IO.puts("Application stopping...")
    :ok
  end
end

# Configuration
defmodule MyApp.Config do
  def get(key) do
    Application.get_env(:my_app, key)
  end
  
  def set(key, value) do
    Application.put_env(:my_app, key, value)
  end
  
  def get_all do
    Application.get_all_env(:my_app)
  end
end

# Usage
MyApp.Config.set(:database_url, "postgres://localhost/myapp")
MyApp.Config.set(:pool_size, 10)
IO.puts("Database URL: #{MyApp.Config.get(:database_url)}")
IO.puts("Pool size: #{MyApp.Config.get(:pool_size)}")
Advanced
22. What are Behaviours in Elixir?

Behaviours define a set of callbacks that modules must implement. They provide a contract for module interaction and are used extensively in OTP.

  • Behaviour Definition: @callback and @macrocallback
  • Implementation: @behaviour module attribute
  • Common Behaviours: GenServer, Supervisor, Application
  • Custom Behaviours: Create your own behaviours
  • Callback Validation: Compile-time validation
elixir
# Custom Behaviour Example
defmodule Worker do
  @callback start_link(any()) :: {:ok, pid()} | {:error, any()}
  @callback perform(any()) :: {:ok, any()} | {:error, any()}
  @callback stop(pid()) :: :ok
  @callback get_status(pid()) :: {:running | :stopped, any()}
  
  defmacro __using__(_opts) do
    quote do
      @behaviour Worker
      def start_link(args) do
        GenServer.start_link(__MODULE__, args, name: __MODULE__)
      end
      
      def stop(pid) do
        GenServer.stop(pid)
      end
      
      def get_status(pid) do
        GenServer.call(pid, :get_status)
      end
      
      defoverridable Worker
    end
  end
end

defmodule TaskWorker do
  use Worker
  
  def init(args) do
    {:ok, %{task: args[:task], status: :running}}
  end
  
  def perform(task) do
    IO.puts("Performing task: #{task}")
    Process.sleep(1000)
    {:ok, "Task completed: #{task}"}
  end
  
  def handle_call(:get_status, _from, state) do
    {:reply, {:running, state}, state}
  end
  
  def handle_call({:perform, task}, _from, state) do
    result = perform(task)
    {:reply, result, state}
  end
  
  def handle_cast({:stop, reason}, state) do
    {:stop, reason, state}
  end
end

defmodule EmailWorker do
  use Worker
  
  def init(args) do
    {:ok, %{email: args[:email], sent: 0}}
  end
  
  def perform(email) do
    IO.puts("Sending email to: #{email}")
    Process.sleep(500)
    {:ok, "Email sent to #{email}"}
  end
  
  def handle_call(:get_status, _from, state) do
    {:reply, {:running, state}, state}
  end
  
  def handle_call({:perform, email}, _from, state) do
    result = perform(email)
    updated_state = %{state | sent: state.sent + 1}
    {:reply, result, updated_state}
  end
end

# Usage
{:ok, pid1} = TaskWorker.start_link(%{task: "Process data"})
{:ok, result1} = GenServer.call(pid1, {:perform, "Analyze log"})
IO.puts("Result 1: #{result1}")

{:ok, pid2} = EmailWorker.start_link(%{email: "user@example.com"})
{:ok, result2} = GenServer.call(pid2, {:perform, "user@example.com"})
IO.puts("Result 2: #{result2}")
Advanced
23. What are Agents in Elixir?

Agents provide a simple way to manage state in a process. They are lightweight and easy to use for basic state management with get and update operations.

  • State Management: Agent.start_link with initial state
  • Get/Update: Agent.get, Agent.update
  • Thread Safety: State changes are sequential
  • Use Cases: Simple counters, caches, configuration
  • Short-lived: For simple state management
elixir
# Agent Example in Elixir
defmodule UserAgent do
  def start_link(initial_state) do
    Agent.start_link(fn -> initial_state end, name: __MODULE__)
  end
  
  def add_user(user) do
    Agent.update(__MODULE__, fn state -> [user | state] end)
  end
  
  def get_users do
    Agent.get(__MODULE__, fn state -> state end)
  end
  
  def get_user_by_id(id) do
    Agent.get(__MODULE__, fn state ->
      Enum.find(state, fn user -> user.id == id end)
    end)
  end
  
  def update_user(id, new_user_data) do
    Agent.update(__MODULE__, fn state ->
      Enum.map(state, fn user ->
        if user.id == id do
          Map.merge(user, new_user_data)
        else
          user
        end
      end)
    end)
  end
  
  def delete_user(id) do
    Agent.update(__MODULE__, fn state ->
      Enum.filter(state, fn user -> user.id != id end)
    end)
  end
  
  def clear_all do
    Agent.update(__MODULE__, fn _ -> [] end)
  end
  
  def count_users do
    Agent.get(__MODULE__, fn state -> length(state) end)
  end
end

# Usage
{:ok, _pid} = UserAgent.start_link([])

# Add users
UserAgent.add_user(%{id: 1, name: "Alice", age: 25})
UserAgent.add_user(%{id: 2, name: "Bob", age: 30})
UserAgent.add_user(%{id: 3, name: "Carol", age: 22})

# Get all users
all_users = UserAgent.get_users()
IO.puts("All users: #{inspect(all_users)}")

# Find user by ID
user = UserAgent.get_user_by_id(2)
IO.puts("User 2: #{inspect(user)}")

# Update user
UserAgent.update_user(1, %{age: 26, name: "Alice Johnson"})

# Delete user
UserAgent.delete_user(3)

# Get updated list
IO.puts("Updated users: #{inspect(UserAgent.get_users())}")
IO.puts("User count: #{UserAgent.count_users()}")
Advanced
24. What is Registry in Elixir?

Registry provides a way to register processes by name for easy lookup. It supports unique and duplicate keys with built-in conflict resolution.

  • Registration: Registry.register for process registration
  • Lookup: Registry.lookup to find processes
  • Unique Keys: Each key maps to one process
  • Duplicate Keys: Multiple processes per key
  • Registry Monitoring: Track process lifecycle
elixir
# Registry Example in Elixir
defmodule MyRegistry do
  use Registry
  
  def start_link do
    Registry.start_link(keys: :unique, name: __MODULE__)
  end
  
  def register(name, pid) do
    Registry.register(__MODULE__, name, pid)
  end
  
  def unregister(name) do
    Registry.unregister(__MODULE__, name)
  end
  
  def lookup(name) do
    Registry.lookup(__MODULE__, name)
  end
  
  def whereis(name) do
    case Registry.lookup(__MODULE__, name) do
      [{pid, _}] -> pid
      [] -> nil
    end
  end
  
  def dispatch(name, message) do
    case whereis(name) do
      nil -> {:error, "Process not found"}
      pid -> send(pid, message)
    end
  end
  
  def register_with_value(name, pid, value) do
    Registry.register(__MODULE__, name, value)
  end
  
  def get_value(name) do
    case Registry.lookup(__MODULE__, name) do
      [{_, value}] -> {:ok, value}
      [] -> {:error, "Not found"}
    end
  end
end

# Sample process
defmodule SampleProcess do
  def start_link(name) do
    pid = spawn(fn -> loop() end)
    MyRegistry.register(name, pid)
    pid
  end
  
  def start_link_with_value(name, value) do
    pid = spawn(fn -> loop() end)
    MyRegistry.register_with_value(name, pid, value)
    pid
  end
  
  def loop do
    receive do
      message -> 
        IO.puts("Received: #{message}")
        loop()
    end
  end
end

# Usage
{:ok, _} = MyRegistry.start_link()

# Start processes
SampleProcess.start_link(:worker1)
SampleProcess.start_link_with_value(:worker2, "Important Worker")

# Lookup
pid = MyRegistry.whereis(:worker1)
IO.puts("Worker1 PID: #{inspect(pid)}")

# Get value
{:ok, value} = MyRegistry.get_value(:worker2)
IO.puts("Worker2 value: #{value}")

# Send message
MyRegistry.dispatch(:worker1, "Hello Worker1!")
MyRegistry.dispatch(:worker2, "Hello Worker2!")

# Unregister
MyRegistry.unregister(:worker2)
IO.puts("After unregister: #{MyRegistry.whereis(:worker2)}")
Advanced
25. What is DynamicSupervisor?

DynamicSupervisor allows starting and stopping child processes dynamically at runtime, unlike static supervisors. It's ideal for worker pools and temporary processes.

  • Dynamic Children: Start children on demand
  • Strategies: :one_for_one only
  • Use Cases: Worker pools, temporary processes
  • Management: start_child, terminate_child
  • Count Children: count_children for monitoring
elixir
# DynamicSupervisor Example
defmodule MySupervisor do
  use DynamicSupervisor
  
  def start_link(init_arg) do
    DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
  end
  
  def start_child(module, args) do
    DynamicSupervisor.start_child(__MODULE__, {module, args})
  end
  
  def terminate_child(pid) do
    DynamicSupervisor.terminate_child(__MODULE__, pid)
  end
  
  def count_children do
    DynamicSupervisor.count_children(__MODULE__)
  end
  
  def which_children do
    DynamicSupervisor.which_children(__MODULE__)
  end
  
  def init(_init_arg) do
    DynamicSupervisor.init(strategy: :one_for_one)
  end
end

# Worker process
defmodule DynamicWorker do
  use GenServer
  
  def start_link(name) do
    GenServer.start_link(__MODULE__, name, name: via_tuple(name))
  end
  
  def via_tuple(name) do
    {:via, Registry, {MyRegistry, name}}
  end
  
  def init(name) do
    IO.puts("Worker #{name} started")
    {:ok, %{name: name, tasks: 0}}
  end
  
  def work(pid, task) do
    GenServer.call(pid, {:work, task})
  end
  
  def handle_call({:work, task}, _from, state) do
    IO.puts("#{state.name}: Working on #{task}")
    Process.sleep(1000)
    new_state = %{state | tasks: state.tasks + 1}
    {:reply, {:ok, "Completed #{task}"}, new_state}
  end
  
  def handle_info(:stop, state) do
    IO.puts("#{state.name}: Stopping")
    {:stop, :normal, state}
  end
end

# Usage
{:ok, _} = MySupervisor.start_link([])

# Start dynamic children
{:ok, pid1} = MySupervisor.start_child(DynamicWorker, ["Worker1"])
{:ok, pid2} = MySupervisor.start_child(DynamicWorker, ["Worker2"])

DynamicWorker.work(pid1, "Task A")
DynamicWorker.work(pid2, "Task B")

IO.puts("Children count: #{MySupervisor.count_children()}")

# Terminate a child
MySupervisor.terminate_child(pid1)

# Add new child
{:ok, pid3} = MySupervisor.start_child(DynamicWorker, ["Worker3"])
DynamicWorker.work(pid3, "Task C")
Advanced
26. What is Task Supervisor?

Task Supervisor provides a supervisor for managing tasks, making it easy to run supervised asynchronous operations with automatic error handling.

  • Supervised Tasks: Tasks are automatically supervised
  • Async/Await: Same API as regular tasks
  • Fault Tolerance: Restart failed tasks
  • Use Cases: Background jobs, parallel processing
  • Task.Supervisor: Built-in task supervisor
elixir
# Task with Supervisor Example
defmodule TaskSupervisor do
  use Supervisor
  
  def start_link do
    Supervisor.start_link(__MODULE__, [], name: __MODULE__)
  end
  
  def start_task(function) do
    Supervisor.start_child(__MODULE__, {Task, function})
  end
  
  def start_task_with_timeout(function, timeout \ 5000) do
    Supervisor.start_child(__MODULE__, {Task, fn ->
      try do
        Task.await(Task.async(function), timeout)
      rescue
        e in Task.TimeoutError -> {:error, "Task timed out"}
      end
    end})
  end
  
  def init(_) do
    children = [
      {Task.Supervisor, name: MyTaskSupervisor}
    ]
    
    Supervisor.init(children, strategy: :one_for_one)
  end
end

defmodule Worker do
  def perform_heavy_task(data) do
    Task.Supervisor.start_child(MyTaskSupervisor, fn ->
      Process.sleep(2000)
      IO.puts("Completed: #{data}")
      {:ok, data}
    end)
  end
  
  def perform_with_result(data) do
    Task.Supervisor.start_child(MyTaskSupervisor, fn ->
      result = do_work(data)
      {:ok, result}
    end)
  end
  
  defp do_work(data) do
    Process.sleep(1000)
    "Processed: #{data}"
  end
end

# Usage
TaskSupervisor.start_link()

# Start multiple tasks
1..5
|> Enum.each(fn i ->
  Worker.perform_heavy_task("Task #{i}")
end)

# Tasks with results
tasks = 1..3
|> Enum.map(fn i ->
  Worker.perform_with_result("Data #{i}")
end)

Enum.each(tasks, fn task ->
  case Task.await(task) do
    {:ok, result} -> IO.puts("Result: #{result}")
    _ -> IO.puts("Task failed")
  end
end)

IO.puts("All tasks started")
Advanced
27. What is Phoenix PubSub?

Phoenix PubSub provides a publish-subscribe mechanism for real-time messaging between processes and nodes. It's built on PG2 for distributed messaging.

  • Topics: Subscribe to specific topics
  • Broadcasting: Send messages to all subscribers
  • Distributed: Works across nodes
  • Use Cases: Real-time notifications, chat, updates
  • PG2 Integration: Distributed process groups
elixir
# Phoenix PubSub Example
defmodule MyApp.PubSub do
  use Phoenix.PubSub
  
  def start_link do
    Phoenix.PubSub.start_link(__MODULE__, [name: __MODULE__])
  end
  
  def broadcast(topic, message) do
    Phoenix.PubSub.broadcast(__MODULE__, topic, {:message, message})
  end
  
  def broadcast_from(sender, topic, message) do
    Phoenix.PubSub.broadcast_from(__MODULE__, sender, topic, {:message, message})
  end
  
  def subscribe(topic) do
    Phoenix.PubSub.subscribe(__MODULE__, topic)
  end
  
  def unsubscribe(topic) do
    Phoenix.PubSub.unsubscribe(__MODULE__, topic)
  end
  
  def subscribe_to_many(topics) do
    Enum.each(topics, fn topic ->
      Phoenix.PubSub.subscribe(__MODULE__, topic)
    end)
  end
  
  def count_subscribers(topic) do
    Phoenix.PubSub.count_subscribers(__MODULE__, topic)
  end
end

# Listener process
defmodule Listener do
  def start_link(topic) do
    pid = spawn(fn -> loop(topic) end)
    {:ok, pid}
  end
  
  def loop(topic) do
    MyApp.PubSub.subscribe(topic)
    receive do
      {:message, message} ->
        IO.puts("[#{topic}] Received: #{message}")
        loop(topic)
    end
  end
end

# Message processor
defmodule MessageProcessor do
  def start_link do
    pid = spawn(fn -> loop() end)
    {:ok, pid}
  end
  
  def loop do
    receive do
      {:broadcast, topic, message} ->
        MyApp.PubSub.broadcast(topic, message)
        loop()
      {:broadcast_from, sender, topic, message} ->
        MyApp.PubSub.broadcast_from(sender, topic, message)
        loop()
    end
  end
end

# Usage
MyApp.PubSub.start_link()

# Create listeners
{:ok, _} = Listener.start_link("news")
{:ok, _} = Listener.start_link("updates")
{:ok, _} = Listener.start_link("alerts")

# Subscribe to multiple topics
Listener.start_link("user_channel")

# Publish messages
MyApp.PubSub.broadcast("news", "New article published!")
MyApp.PubSub.broadcast("updates", "System update available!")
MyApp.PubSub.broadcast("news", "Breaking news!")
MyApp.PubSub.broadcast("alerts", "Alert: System maintenance!")

# Check subscribers
IO.puts("News subscribers: #{MyApp.PubSub.count_subscribers("news")}")
Advanced
28. What are Streams for Data Processing?

Streams enable efficient data processing with lazy evaluation, making them suitable for large datasets and real-time processing with memory efficiency.

  • Lazy Evaluation: Process on demand
  • Memory Efficiency: No need to load entire dataset
  • Composition: Chain operations together
  • Use Cases: File processing, data pipelines
  • Infinite Streams: Process infinite sequences
elixir
# Streaming Example in Elixir
defmodule DataStream do
  def generate_random_numbers(count) do
    Stream.repeatedly(fn -> :rand.uniform(100) end)
    |> Enum.take(count)
  end
  
  def process_data(stream) do
    stream
    |> Stream.map(&(&1 * 2))
    |> Stream.filter(&(&1 > 50))
    |> Stream.map(&IO.inspect/1)
    |> Enum.to_list()
  end
  
  def read_file_chunks(file_path, chunk_size \ 10) do
    File.stream!(file_path)
    |> Stream.map(&String.trim/1)
    |> Stream.filter(&(&1 != ""))
    |> Stream.chunk_every(chunk_size)
    |> Stream.map(fn chunk -> Enum.join(chunk, ",") end)
  end
  
  def web_request_stream(url) do
    # Simulate web request streaming
    Stream.iterate(1, &(&1 + 1))
    |> Stream.map(fn page ->
      # Simulate paginated API
      %{page: page, data: Enum.to_list(1..10)}
    end)
    |> Stream.filter(&(&1.page <= 5))
  end
  
  def process_large_file(file_path) do
    File.stream!(file_path)
    |> Stream.map(&String.trim/1)
    |> Stream.map(&String.split/1)
    |> Stream.flat_map(& &1)
    |> Stream.map(&String.downcase/1)
    |> Stream.filter(&(&1 != ""))
    |> Enum.reduce(%{}, fn word, acc ->
      Map.update(acc, word, 1, &(&1 + 1))
    end)
  end
  
  def stream_with_state(data) do
    Stream.iterate(data, fn state ->
      new_state = state + 1
      IO.puts("State: #{new_state}")
      new_state
    end)
    |> Enum.take(5)
  end
  
  def slow_stream do
    Stream.iterate(0, &(&1 + 1))
    |> Stream.take(10)
    |> Stream.map(fn x ->
      Process.sleep(100)
      x * 2
    end)
  end
end

# Usage
# Generate random numbers
random_numbers = DataStream.generate_random_numbers(20)
IO.puts("Random numbers: #{inspect(random_numbers)}")

# Process data
processed = DataStream.process_data(1..100)
IO.puts("Processed: #{inspect(processed)}")

# Web request stream
DataStream.web_request_stream()
|> Enum.each(fn response ->
  IO.puts("Page #{response.page}: #{inspect(response.data)}")
end)

# Process large file
# result = DataStream.process_large_file("data.txt")
# IO.puts("Word count: #{inspect(result)}")
Advanced
29. What is Mnesia Database?

Mnesia is a distributed database management system built into Erlang/Elixir, providing ACID transactions and fault tolerance with built-in replication.

  • ACID Transactions: Atomic, consistent, isolated, durable
  • Distributed: Works across multiple nodes
  • Table Types: Set, ordered_set, bag
  • Use Cases: Caching, configuration, session storage
  • Replication: Built-in data replication
elixir
# Mnesia Database Example
defmodule MyMnesia do
  def start do
    :mnesia.start()
    :mnesia.create_table(:users, [
      attributes: [:id, :name, :email, :age],
      record_name: :user,
      type: :set
    ])
  end
  
  def add_user(id, name, email, age) do
    user = {:user, id, name, email, age}
    :mnesia.transaction(fn ->
      :mnesia.write(user)
    end)
  end
  
  def get_user(id) do
    :mnesia.transaction(fn ->
      case :mnesia.read({:user, id}) do
        [user] -> {:ok, user}
        [] -> {:error, :not_found}
      end
    end)
  end
  
  def get_all_users do
    :mnesia.transaction(fn ->
      :mnesia.match_object({:user, :_, :_, :_, :_})
    end)
  end
  
  def update_user(id, field, value) do
    :mnesia.transaction(fn ->
      case :mnesia.read({:user, id}) do
        [{:user, id, name, email, age}] ->
          updated_user = case field do
            :name -> {:user, id, value, email, age}
            :email -> {:user, id, name, value, age}
            :age -> {:user, id, name, email, value}
          end
          :mnesia.write(updated_user)
          {:ok, updated_user}
        [] ->
          {:error, :not_found}
      end
    end)
  end
  
  def delete_user(id) do
    :mnesia.transaction(fn ->
      :mnesia.delete({:user, id})
    end)
  end
  
  def find_by_name(name) do
    :mnesia.transaction(fn ->
      :mnesia.match_object({:user, :_, name, :_, :_})
    end)
  end
  
  def find_by_age_range(min_age, max_age) do
    :mnesia.transaction(fn ->
      :mnesia.match_object({:user, :_, :_, :_, :_})
      |> Enum.filter(fn {:user, _, _, _, age} -> age >= min_age and age <= max_age end)
    end)
  end
end

# Usage
MyMnesia.start()

# Add users
MyMnesia.add_user(1, "Alice", "alice@email.com", 25)
MyMnesia.add_user(2, "Bob", "bob@email.com", 30)
MyMnesia.add_user(3, "Carol", "carol@email.com", 22)

# Get user
case MyMnesia.get_user(1) do
  {:ok, user} -> IO.puts("User found: #{inspect(user)}")
  {:error, _} -> IO.puts("User not found")
end

# Get all users
all_users = MyMnesia.get_all_users()
IO.puts("All users: #{inspect(all_users)}")

# Update user
MyMnesia.update_user(1, :age, 26)

# Delete user
MyMnesia.delete_user(3)

# Find by name
users = MyMnesia.find_by_name("Alice")
IO.puts("Found by name: #{inspect(users)}")

# Find by age range
users = MyMnesia.find_by_age_range(20, 28)
IO.puts("Found by age range: #{inspect(users)}")
Advanced
30. What is GenStage?

GenStage is a behaviour for building event processing pipelines with back-pressure and flow control. It enables scalable data processing systems.

  • Producers: Generate events
  • Consumers: Process events
  • Producer-Consumers: Both produce and consume
  • Back-pressure: Flow control via demand
  • Use Cases: Data pipelines, ETL, streaming
elixir
# GenStage Example
defmodule Producer do
  use GenStage
  
  def start_link(initial) do
    GenStage.start_link(__MODULE__, initial, name: __MODULE__)
  end
  
  def init(initial) do
    {:producer, initial}
  end
  
  def handle_demand(demand, state) when demand > 0 do
    events = Enum.to_list(state..state + demand - 1)
    {:noreply, events, state + demand}
  end
end

defmodule Consumer do
  use GenStage
  
  def start_link do
    GenStage.start_link(__MODULE__, :ok, name: __MODULE__)
  end
  
  def init(:ok) do
    {:consumer, :ok}
  end
  
  def handle_events(events, _from, state) do
    Enum.each(events, fn event ->
      IO.puts("Consumed: #{event}")
    end)
    {:noreply, [], state}
  end
end

defmodule ProducerConsumer do
  use GenStage
  
  def start_link do
    GenStage.start_link(__MODULE__, :ok, name: __MODULE__)
  end
  
  def init(:ok) do
    {:producer_consumer, :ok}
  end
  
  def handle_events(events, _from, state) do
    events = Enum.map(events, &(&1 * 2))
    {:noreply, events, state}
  end
end

defmodule Pipeline do
  def start do
    {:ok, producer} = Producer.start_link(1)
    {:ok, producer_consumer} = ProducerConsumer.start_link()
    {:ok, consumer} = Consumer.start_link()
    
    # Create pipeline
    GenStage.sync_subscribe(producer_consumer, to: producer)
    GenStage.sync_subscribe(consumer, to: producer_consumer)
    
    {:ok, producer}
  end
  
  def request_data(count) do
    GenStage.demand(__MODULE__, count)
  end
end

# Usage
{:ok, producer} = Pipeline.start()

# Request data
GenStage.demand(producer, 5)
Advanced
31. What is Flow?

Flow is a library built on GenStage for parallel data processing with built-in back-pressure and composition. It provides high-performance data processing.

  • Parallel Processing: Process data in parallel
  • Back-pressure: Automatic flow control
  • Composition: Chain operations together
  • Use Cases: ETL, data pipelines, batch processing
  • MapReduce: Built-in MapReduce support
elixir
# Flow Example (Parallel Processing)
defmodule FlowExample do
  def process_data(data) do
    data
    |> Flow.from_enumerable()
    |> Flow.map(&(&1 * 2))
    |> Flow.filter(&(&1 > 10))
    |> Flow.map(&IO.inspect/1)
    |> Enum.to_list()
  end
  
  def process_file(file_path) do
    File.stream!(file_path)
    |> Flow.from_enumerable()
    |> Flow.map(&String.trim/1)
    |> Flow.filter(&(&1 != ""))
    |> Flow.chunk_every(100)
    |> Flow.map(fn chunk ->
      Enum.join(chunk, ",")
    end)
    |> Enum.to_list()
  end
  
  def parallel_map(data, function) do
    data
    |> Flow.from_enumerable(max_demand: 10)
    |> Flow.map(function)
    |> Enum.to_list()
  end
  
  def parallel_aggregate(data) do
    data
    |> Flow.from_enumerable(max_demand: 10)
    |> Flow.reduce(fn -> 0 end, fn x, acc -> x + acc end)
    |> Enum.to_list()
  end
  
  def parallel_group_by(data, key_function) do
    data
    |> Flow.from_enumerable()
    |> Flow.group_by(key_function)
    |> Enum.to_list()
  end
  
  def parallel_join(left, right, join_key) do
    Flow.from_enumerables([left, right])
    |> Flow.map(fn item -> {join_key.(item), item} end)
    |> Flow.group_by(fn {key, _} -> key end)
    |> Flow.map(fn {key, items} ->
      {key, Enum.map(items, fn {_, item} -> item end)}
    end)
    |> Enum.to_list()
  end
end

# Usage
data = 1..100

# Process data in parallel
result = FlowExample.process_data(data)
IO.puts("Processed: #{inspect(result)}")

# Parallel map
squares = FlowExample.parallel_map(data, fn x -> x * x end)
IO.puts("Squares: #{inspect(Enum.take(squares, 10))}")

# Parallel aggregate
sum = FlowExample.parallel_aggregate(data)
IO.puts("Sum: #{inspect(sum)}")
Advanced
32. What is Broadway?

Broadway is a data processing pipeline library for Elixir, built on GenStage for high-throughput and fault-tolerant processing with built-in error handling.

  • Pipeline Processing: Process data in stages
  • Batch Processing: Process data in batches
  • Fault Tolerance: Automatic retry and failure handling
  • Use Cases: Data ingestion, ETL, stream processing
  • Back-pressure: Built-in flow control
elixir
# Broadway Example (Data Processing Pipeline)
defmodule MyBroadway do
  use Broadway
  
  def start_link(_opts) do
    Broadway.start_link(__MODULE__,
      name: __MODULE__,
      producer: [
        module: {Broadway.DummyProducer, []},
        concurrency: 5
      ],
      processors: [
        default: [concurrency: 10]
      ],
      batchers: [
        default: [concurrency: 5, batch_size: 100, batch_timeout: 1000]
      ]
    )
  end
  
  def handle_message(_, message, _) do
    # Process individual message
    data = message.data
    processed = String.upcase(data)
    message
    |> Broadway.Message.update_data(fn _ -> processed end)
  end
  
  def handle_batch(_, messages, _, _) do
    # Process batch of messages
    Enum.each(messages, fn message ->
      IO.puts("Batch processed: #{message.data}")
    end)
    
    messages
  end
  
  def handle_failure(_, failure, _, _) do
    # Handle failures
    IO.puts("Failed to process: #{inspect(failure)}")
    failure
  end
  
  def handle_message_with_timeout(_, message, _) do
    try do
      # Simulate work with timeout
      Process.sleep(500)
      message
    rescue
      e -> 
        IO.puts("Error processing: #{inspect(e)}")
        {:error, message}
    end
  end
end

# Usage
{:ok, _} = MyBroadway.start_link([])

# Send test messages
1..10
|> Enum.each(fn i ->
  Broadway.Message.new("Message #{i}")
  |> MyBroadway.process()
end)
Advanced
33. What is Telemetry?

Telemetry provides a standard way to emit and handle events for metrics, logging, and monitoring in Elixir applications with built-in integration support.

  • Events: Emit events with measurements
  • Handlers: Attach handlers to events
  • Metrics: Collect performance and business metrics
  • Integration: Works with Prometheus, StatsD, etc.
  • Instrumentation: Built-in library instrumentation
elixir
# Telemetry Example
defmodule MyApp.Telemetry do
  use Supervisor
  
  def start_link do
    Supervisor.start_link(__MODULE__, [], name: __MODULE__)
  end
  
  def init(_) do
    children = [
      {Telemetry, []}
    ]
    
    Supervisor.init(children, strategy: :one_for_one)
  end
  
  def setup do
    :telemetry.attach(
      "my-handler",
      [:my_app, :request, :stop],
      &handle_event/4,
      nil
    )
    
    :telemetry.attach(
      "error-handler",
      [:my_app, :request, :error],
      &handle_error/4,
      nil
    )
  end
  
  def handle_event([:my_app, :request, :stop], measurements, metadata, _config) do
    duration = measurements.duration
    path = metadata.path
    status = metadata.status
    
    IO.puts("Request to #{path} completed in #{duration}ms with status #{status}")
    
    # Log slow requests
    if duration > 1000 do
      IO.puts("Slow request: #{path} took #{duration}ms")
    end
  end
  
  def handle_error([:my_app, :request, :error], measurements, metadata, _config) do
    error = metadata.error
    path = metadata.path
    
    IO.puts("Error on #{path}: #{inspect(error)}")
  end
  
  def measure_request(path, status, function) do
    start = System.monotonic_time()
    
    try do
      result = function.()
      stop = System.monotonic_time()
      duration = System.convert_time_unit(stop - start, :native, :millisecond)
      
      :telemetry.execute([:my_app, :request, :stop], %{duration: duration}, %{
        path: path,
        status: status
      })
      
      result
    rescue
      e ->
        stop = System.monotonic_time()
        duration = System.convert_time_unit(stop - start, :native, :millisecond)
        
        :telemetry.execute([:my_app, :request, :error], %{duration: duration}, %{
          path: path,
          error: e
        })
        
        {:error, e}
    end
  end
end

# Usage
MyApp.Telemetry.start_link()
MyApp.Telemetry.setup()

# Measure a successful request
MyApp.Telemetry.measure_request("/api/users", 200, fn ->
  Process.sleep(500)
  {:ok, "Response data"}
end)

# Measure a slow request
MyApp.Telemetry.measure_request("/api/reports", 200, fn ->
  Process.sleep(1500)
  {:ok, "Report data"}
end)

# Measure a failed request
MyApp.Telemetry.measure_request("/api/error", 500, fn ->
  Process.sleep(100)
  raise "Something went wrong"
end)
Advanced
34. What is Logger in Elixir?

Logger is the built-in logging system in Elixir, providing structured logging with configurable levels and backends for comprehensive logging capabilities.

  • Log Levels: debug, info, warning, error
  • Metadata: Attach metadata to log messages
  • Backends: console, file, custom backends
  • Configuration: Configure via config.exs
  • Formatter: Custom log formatting
elixir
# Logger Example
defmodule MyApp.Logger do
  require Logger
  
  def configure do
    Logger.configure(fn config ->
      %{config | level: :debug, format: "$time $level $message"}
    end)
    
    Logger.add_backend(:console)
    Logger.add_backend(MyApp.CustomLogger)
  end
  
  def log_info(message) do
    Logger.info(message)
  end
  
  def log_debug(message) do
    Logger.debug(message)
  end
  
  def log_warning(message) do
    Logger.warning(message)
  end
  
  def log_error(message) do
    Logger.error(message)
  end
  
  def log_with_metadata(message, metadata) do
    Logger.metadata(metadata)
    Logger.info(message)
    Logger.reset_metadata()
  end
  
  def log_with_context(message, context) do
    Logger.metadata(context)
    Logger.info(message)
    Logger.reset_metadata()
  end
end

defmodule MyApp.CustomLogger do
  use GenServer
  
  def start_link do
    GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
  end
  
  def init(:ok) do
    {:ok, %{logs: [], errors: 0}}
  end
  
  def handle_call(:get_logs, _from, state) do
    {:reply, state.logs, state}
  end
  
  def handle_call(:get_stats, _from, state) do
    {:reply, %{total: length(state.logs), errors: state.errors}, state}
  end
  
  def handle_info({:log, level, message, timestamp}, state) do
    logs = [{timestamp, level, message} | state.logs]
    errors = if level == :error, do: state.errors + 1, else: state.errors
    {:noreply, %{state | logs: logs, errors: errors}}
  end
end

# Usage
require Logger
MyApp.Logger.configure()

# Log messages
Logger.info("Application started")
Logger.debug("Debug information")
Logger.warning("Low memory warning")
Logger.error("Error occurred")

# Log with metadata
Logger.metadata(user_id: 123, request_id: "abc-123")
Logger.info("User action logged")
Logger.reset_metadata()

# Log with context
MyApp.Logger.log_with_context("User login", %{user_id: 456, ip: "192.168.1.1"})
Advanced
35. How to manage Application Configuration?

Elixir provides a comprehensive configuration system through Application module, environment variables, and config files for flexible application setup.

  • Application.get_env: Access configuration
  • config/config.exs: Configuration files
  • Environment Variables: System environment variables
  • Runtime Configuration: Config module
  • Release Configuration: Runtime config for releases
elixir
# Application Configuration
defmodule MyApp.Config do
  @moduledoc """
  Application configuration module
  """
  
  def get(key, default \ nil) do
    Application.get_env(:my_app, key, default)
  end
  
  def set(key, value) do
    Application.put_env(:my_app, key, value)
  end
  
  def load_config do
    # Load from environment variables
    database_url = System.get_env("DATABASE_URL") || "postgres://localhost/myapp"
    port = System.get_env("PORT") || "4000"
    secret_key = System.get_env("SECRET_KEY_BASE")
    
    set(:database_url, database_url)
    set(:port, String.to_integer(port))
    set(:environment, System.get_env("MIX_ENV") || "development")
    
    if secret_key do
      set(:secret_key_base, secret_key)
    end
    
    # Load from config file
    config_file = Path.join(File.cwd!(), "config/config.exs")
    if File.exists?(config_file) do
      Code.require_file(config_file)
    end
    
    # Load from config file
    config_file = Path.join(File.cwd!(), "config/config.exs")
    if File.exists?(config_file) do
      Code.require_file(config_file)
    end
  end
  
  def get_database_config do
    %{
      url: get(:database_url),
      pool_size: get(:pool_size, 10),
      timeout: get(:timeout, 5000),
      loggers: get(:db_loggers, [])
    }
  end
  
  def get_server_config do
    %{
      port: get(:port, 4000),
      host: get(:host, "localhost"),
      environment: get(:environment, "development"),
      secret_key: get(:secret_key_base)
    }
  end
  
  def get_cache_config do
    %{
      ttl: get(:cache_ttl, 3600),
      max_size: get(:cache_max_size, 1000),
      strategy: get(:cache_strategy, :lru)
    }
  end
end

# Usage
MyApp.Config.load_config()

# Access config
db_config = MyApp.Config.get_database_config()
IO.puts("Database URL: #{db_config.url}")

server_config = MyApp.Config.get_server_config()
IO.puts("Server port: #{server_config.port}")

# Update config
MyApp.Config.set(:pool_size, 20)
IO.puts("Pool size: #{MyApp.Config.get(:pool_size)}")
Advanced
36. What is Phoenix Channels?

Phoenix Channels provide real-time communication over WebSockets, enabling bidirectional messaging between clients and servers.

  • WebSocket Layer: Persistent connection for real-time
  • PubSub: Built-in publish/subscribe
  • Presence: Track online users and state
  • Authentication: Built-in socket authentication
elixir
# Phoenix Channels Example
defmodule MyAppWeb.UserSocket do
  use Phoenix.Socket
  
  channel "room:*", MyAppWeb.RoomChannel
  channel "user:*", MyAppWeb.UserChannel
  
  def connect(params, socket, _connect_info) do
    {:ok, assign(socket, :user_id, params["user_id"])}
  end
  
  def id(_socket), do: nil
end

defmodule MyAppWeb.RoomChannel do
  use Phoenix.Channel
  
  def join("room:" <> room_id, _message, socket) do
    {:ok, assign(socket, :room_id, room_id)}
  end
  
  def handle_in("new_msg", %{"body" => body}, socket) do
    broadcast!(socket, "new_msg", %{body: body, user: socket.assigns.user_id})
    {:noreply, socket}
  end
end
Advanced
37. What is Phoenix Presence?

Phoenix Presence is a feature for tracking user presence across nodes, enabling real-time visibility of online users.

  • User Tracking: Track online/offline status
  • CRDT: Conflict-free replicated data types
  • Presence State: Sync state across nodes
  • Join/Leave Events: React to user presence changes
elixir
# Phoenix Presence Example
defmodule MyAppWeb.Presence do
  use Phoenix.Presence,
    otp_app: :my_app,
    pubsub_server: MyApp.PubSub
end

defmodule MyAppWeb.RoomChannel do
  use Phoenix.Channel
  alias MyAppWeb.Presence
  
  def join("room:" <> room_id, _params, socket) do
    send(self(), :after_join)
    {:ok, assign(socket, :room_id, room_id)}
  end
  
  def handle_info(:after_join, socket) do
    {:ok, _} = Presence.track(socket, socket.assigns.user_id, %{
      online_at: inspect(System.system_time(:second))
    })
    
    push(socket, "presence_state", Presence.list(socket))
    {:noreply, socket}
  end
end
Advanced
38. What is ExUnit?

ExUnit is Elixir's built-in testing framework, providing a simple and powerful way to write and run tests.

  • Test Cases: test/2 macro for defining tests
  • Assertions: assert, refute, and custom assertions
  • Setup/Teardown: setup and setup_all callbacks
  • Async Tests: Run tests in parallel
elixir
# ExUnit Testing Example
defmodule MyApp.MathTest do
  use ExUnit.Case
  doctest MyApp.Math
  
  test "add/2 works correctly" do
    assert MyApp.Math.add(2, 3) == 5
    assert MyApp.Math.add(-1, 1) == 0
  end
  
  test "divide/2 handles division by zero" do
    assert_raise ArithmeticError, fn ->
      MyApp.Math.divide(10, 0)
    end
  end
  
  describe "factorial/1" do
    test "returns 1 for 0" do
      assert MyApp.Math.factorial(0) == 1
    end
    
    test "returns correct value for positive numbers" do
      assert MyApp.Math.factorial(5) == 120
    end
  end
  
  setup do
    %{user: %{name: "Alice", age: 25}}
  end
  
  test "uses setup data", %{user: user} do
    assert user.name == "Alice"
  end
end
Advanced
39. What is Mix in Elixir?

Mix is Elixir's build tool for creating, compiling, testing, and managing dependencies in Elixir projects.

  • Project Management: mix new to create projects
  • Dependencies: Manage dependencies with Hex
  • Tasks: mix test, mix compile, mix run
  • Custom Tasks: Create your own Mix tasks
elixir
# Mix Tasks Example
defmodule Mix.Tasks.Hello do
  use Mix.Task
  
  @shortdoc "Prints Hello, World!"
  
  def run(_args) do
    IO.puts("Hello, World!")
  end
end

defmodule Mix.Tasks.Greet do
  use Mix.Task
  
  @shortdoc "Greets a person by name"
  
  def run(args) do
    name = case args do
      [name] -> name
      _ -> "World"
    end
    
    IO.puts("Hello, #{name}!")
  end
end

defmodule Mix.Tasks.Setup do
  use Mix.Task
  
  def run(_args) do
    Mix.shell().info("Setting up application...")
    
    # Run database migrations
    Mix.Task.run("ecto.create")
    Mix.Task.run("ecto.migrate")
    
    # Seed data
    Mix.Task.run("run", ["priv/repo/seeds.exs"])
    
    Mix.shell().info("Setup complete!")
  end
end
Advanced
40. What is Hex?

Hex is the package manager for Elixir, providing a central repository for libraries and dependencies.

  • Package Management: Install and manage packages
  • Versioning: Semantic versioning support
  • Dependencies: Resolve and fetch dependencies
  • Publishing: Publish your own packages
elixir
# Hex Package Example
defmodule MyApp.MixProject do
  use Mix.Project
  
  def project do
    [
      app: :my_app,
      version: "0.1.0",
      elixir: "~> 1.12",
      start_permanent: Mix.env() == :prod,
      deps: deps(),
      package: package(),
      description: description()
    ]
  end
  
  def application do
    [
      extra_applications: [:logger],
      mod: {MyApp.Application, []}
    ]
  end
  
  defp deps do
    [
      {:phoenix, "~> 1.6.0"},
      {:phoenix_live_view, "~> 0.17.0"},
      {:ecto_sql, "~> 3.0"},
      {:postgrex, ">= 0.0.0"},
      {:jason, "~> 1.2"},
      {:plug_cowboy, "~> 2.5"}
    ]
  end
  
  defp package do
    [
      files: ["lib", "priv", "mix.exs", "README.md"],
      licenses: ["MIT"],
      links: %{"GitHub" => "https://github.com/username/my_app"}
    ]
  end
  
  defp description do
    "A sample Elixir application with Hex packaging"
  end
end
Advanced
41. What is Supervisor with Dynamic Children?

DynamicSupervisor allows starting and stopping child processes dynamically at runtime, ideal for worker pools and temporary processes with one-for-one strategy.

  • Dynamic Children: Start children on demand
  • Strategies: :one_for_one only
  • Use Cases: Worker pools, temporary processes
  • Management: start_child, terminate_child
  • Count Children: count_children for monitoring
elixir
# Supervisor with Dynamic Children
defmodule MyApp.DynamicSupervisor do
  use DynamicSupervisor
  
  def start_link(init_arg) do
    DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
  end
  
  def start_child(worker_module, args) do
    DynamicSupervisor.start_child(__MODULE__, {worker_module, args})
  end
  
  def terminate_child(pid) do
    DynamicSupervisor.terminate_child(__MODULE__, pid)
  end
  
  def count_children do
    DynamicSupervisor.count_children(__MODULE__)
  end
  
  def init(_init_arg) do
    DynamicSupervisor.init(strategy: :one_for_one)
  end
end

defmodule MyApp.Worker do
  use GenServer
  
  def start_link(args) do
    GenServer.start_link(__MODULE__, args, name: via_tuple(args[:name]))
  end
  
  def via_tuple(name) do
    {:via, Registry, {MyApp.Registry, name}}
  end
  
  def init(args) do
    {:ok, args}
  end
  
  def handle_call(:get_state, _from, state) do
    {:reply, state, state}
  end
end

# Usage
{:ok, _} = MyApp.DynamicSupervisor.start_link([])

# Start dynamic children
{:ok, pid1} = MyApp.DynamicSupervisor.start_child(MyApp.Worker, [name: "worker1"])
{:ok, pid2} = MyApp.DynamicSupervisor.start_child(MyApp.Worker, [name: "worker2"])

# Check children count
IO.puts("Children count: #{MyApp.DynamicSupervisor.count_children()}")
Advanced
42. What is GenStage with Multiple Producers?

GenStage supports multiple producers with broadcast dispatcher, enabling parallel event processing with back-pressure and flow control.

  • Multiple Producers: Multiple event sources
  • Broadcast Dispatcher: Send events to all consumers
  • Back-pressure: Flow control via demand
  • Use Cases: Event processing pipelines
  • Parallel Processing: Process events concurrently
elixir
# GenStage with Multiple Producers
defmodule MultiProducer do
  use GenStage
  
  def start_link(producers) do
    GenStage.start_link(__MODULE__, producers, name: __MODULE__)
  end
  
  def init(producers) do
    {:producer, producers, dispatcher: GenStage.BroadcastDispatcher}
  end
  
  def handle_demand(demand, state) when demand > 0 do
    events = Enum.map(1..demand, fn i -> "Event #{i}" end)
    {:noreply, events, state}
  end
end

defmodule MultiConsumer do
  use GenStage
  
  def start_link do
    GenStage.start_link(__MODULE__, :ok, name: __MODULE__)
  end
  
  def init(:ok) do
    {:consumer, :ok}
  end
  
  def handle_events(events, _from, state) do
    Enum.each(events, fn event ->
      IO.puts("Consumer received: #{event}")
    end)
    {:noreply, [], state}
  end
end

# Usage
{:ok, producer} = MultiProducer.start_link([])
{:ok, consumer1} = MultiConsumer.start_link()
{:ok, consumer2} = MultiConsumer.start_link()

GenStage.sync_subscribe(consumer1, to: producer)
GenStage.sync_subscribe(consumer2, to: producer)

GenStage.demand(producer, 3)
Advanced
43. What is Phoenix LiveView with Ecto?

Phoenix LiveView with Ecto enables building interactive UIs with database integration, providing real-time updates and form handling with Ecto changesets.

  • Database Integration: Ecto for data persistence
  • Real-time Updates: LiveView for interactive UIs
  • Form Handling: Changesets for validation
  • Data Binding: Bind database data to UI
  • CRUD Operations: Create, read, update, delete
elixir
# Phoenix LiveView with Ecto
defmodule MyAppWeb.UserLive do
  use Phoenix.LiveView
  alias MyApp.{Repo, User}
  
  def mount(_params, _session, socket) do
    users = Repo.all(User)
    {:ok, assign(socket, users: users, form: nil)}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Users</h2>
      <table>
        <thead>
          <tr><th>Name</th><th>Email</th><th>Age</th></tr>
        </thead>
        <tbody>
          <%= for user <- @users do %>
            <tr>
              <td><%= user.name %></td>
              <td><%= user.email %></td>
              <td><%= user.age %></td>
            </tr>
          <% end %>
        </tbody>
      </table>
      
      <h3>Add User</h3>
      <.form let={f} for={@form} phx-submit="save">
        <%= text_input(f, :name, placeholder: "Name") %>
        <%= text_input(f, :email, placeholder: "Email") %>
        <%= number_input(f, :age, placeholder: "Age") %>
        <%= submit("Save") %>
      </.form>
    </div>
    """
  end
  
  def handle_event("save", %{"user" => user_params}, socket) do
    changeset = User.changeset(%User{}, user_params)
    case Repo.insert(changeset) do
      {:ok, user} ->
        users = [user | socket.assigns.users]
        {:noreply, assign(socket, users: users, form: nil)}
      {:error, changeset} ->
        {:noreply, assign(socket, form: changeset)}
    end
  end
end
Advanced
44. What is Phoenix LiveView with PubSub?

Phoenix LiveView with PubSub enables real-time communication between LiveViews, allowing broadcasting of messages and updates across connected clients.

  • PubSub Integration: Phoenix PubSub for messaging
  • Real-time Updates: Broadcast to multiple LiveViews
  • Event Handling: Handle incoming messages
  • Use Cases: Chat, notifications, real-time feeds
  • Cross-View Communication: Communicate between views
elixir
# Phoenix LiveView with PubSub
defmodule MyAppWeb.ChatLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    if connected?(socket) do
      Phoenix.PubSub.subscribe(MyApp.PubSub, "chat")
    end
    {:ok, assign(socket, messages: [])}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Chat</h2>
      <div id="messages">
        <%= for msg <- @messages do %>
          <div><%= msg %></div>
        <% end %>
      </div>
      <form phx-submit="send">
        <input type="text" name="message" placeholder="Type a message..." />
        <button type="submit">Send</button>
      </form>
    </div>
    """
  end
  
  def handle_event("send", %{"message" => message}, socket) do
    Phoenix.PubSub.broadcast(MyApp.PubSub, "chat", {:new_message, message})
    {:noreply, socket}
  end
  
  def handle_info({:new_message, message}, socket) do
    {:noreply, update(socket, :messages, fn msgs -> [message | msgs] end)}
  end
end
Advanced
45. What is Phoenix Controller with Plug?

Phoenix Controllers handle HTTP requests with Plug for authentication, authorization, and request/response processing in a pipeline pattern.

  • Controllers: Handle HTTP requests
  • Plug Pipeline: Process requests through plugs
  • Authentication: JWT and session-based auth
  • JSON APIs: Render JSON responses
  • Error Handling: Handle errors gracefully
elixir
# Phoenix Controller with Plug
defmodule MyAppWeb.AuthController do
  use MyAppWeb, :controller
  
  def login(conn, %{"email" => email, "password" => password}) do
    case MyApp.Auth.authenticate(email, password) do
      {:ok, user} ->
        conn
        |> put_session(:user_id, user.id)
        |> put_status(:ok)
        |> json(%{token: MyApp.Auth.generate_token(user)})
        
      {:error, reason} ->
        conn
        |> put_status(:unauthorized)
        |> json(%{error: reason})
    end
  end
  
  def logout(conn, _params) do
    conn
    |> delete_session(:user_id)
    |> put_status(:ok)
    |> json(%{message: "Logged out"})
  end
  
  def me(conn, _params) do
    user = conn.assigns.current_user
    json(conn, %{user: user})
  end
end

defmodule MyAppWeb.AuthPlug do
  import Plug.Conn
  
  def init(options), do: options
  
  def call(conn, _opts) do
    case get_req_header(conn, "authorization") do
      ["Bearer " <> token] ->
        case MyApp.Auth.verify_token(token) do
          {:ok, user} ->
            assign(conn, :current_user, user)
          {:error, _} ->
            conn |> send_resp(401, "Unauthorized") |> halt()
        end
      _ ->
        conn |> send_resp(401, "Unauthorized") |> halt()
    end
  end
end
Advanced
46. What is Phoenix Router with LiveView?

Phoenix Router defines routes for both traditional controllers and LiveViews, enabling seamless integration of server-rendered and real-time pages.

  • Routing: Define URL routes
  • LiveView Routes: live macro for LiveViews
  • Scopes: Group routes by scope
  • Pipelines: Apply plugs to routes
  • Nested Routes: Resource nesting and routing
elixir
# Phoenix Router with LiveView
defmodule MyAppWeb.Router do
  use MyAppWeb, :router
  import Phoenix.LiveView.Router
  
  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end
  
  pipeline :api do
    plug :accepts, ["json"]
  end
  
  scope "/", MyAppWeb do
    pipe_through :browser
    
    get "/", PageController, :index
    live "/counter", CounterLive
    live "/chat", ChatLive
    live "/users", UserLive
    live "/dashboard", DashboardLive
    live "/profile/:id", ProfileLive
  end
  
  scope "/api", MyAppWeb do
    pipe_through :api
    
    resources "/users", UserController, except: [:new, :edit]
    resources "/posts", PostController, except: [:new, :edit]
  end
  
  # Admin routes
  scope "/admin", MyAppWeb.Admin, as: :admin do
    pipe_through [:browser, :admin_auth]
    
    resources "/users", UserController
    resources "/posts", PostController
    live "/dashboard", DashboardLive
  end
end
Advanced
47. What is Phoenix with Ecto Associations?

Phoenix with Ecto Associations enables working with related data through associations like belongs_to, has_many, and has_one.

  • Associations: belongs_to, has_many, has_one
  • Preloading: preload for eager loading
  • Build Associations: build_assoc for nested creation
  • Query Joins: Join tables in queries
  • Nested Data: Work with nested data structures
elixir
# Phoenix with Ecto Associations
defmodule MyApp.Blog do
  alias MyApp.{Repo, User, Post, Comment}
  
  def create_post(user_id, attrs) do
    user = Repo.get(User, user_id)
    user
    |> Ecto.build_assoc(:posts)
    |> Post.changeset(attrs)
    |> Repo.insert()
  end
  
  def get_posts_with_comments do
    query = from p in Post,
      join: c in assoc(p, :comments),
      preload: [comments: c],
      order_by: [desc: p.inserted_at]
    Repo.all(query)
  end
  
  def add_comment(post_id, user_id, content) do
    post = Repo.get(Post, post_id)
    user = Repo.get(User, user_id)
    
    Ecto.build_assoc(post, :comments)
    |> Comment.changeset(%{content: content, user_id: user_id})
    |> Repo.insert()
  end
  
  def get_user_posts(user_id) do
    query = from u in User,
      where: u.id == ^user_id,
      preload: [posts: from(p in Post, order_by: [desc: p.inserted_at])]
    Repo.one(query)
  end
end
Advanced
48. What is Phoenix with Ecto Multi?

Phoenix with Ecto Multi enables performing multiple database operations in a single transaction with built-in error handling and rollback support.

  • Ecto.Multi: Bundle multiple operations
  • Transactions: ACID transactions with rollback
  • Error Handling: Handle errors gracefully
  • Run/Insert/Update: Multiple operation types
  • Complex Operations: Combine inserts, updates, and custom logic
elixir
# Phoenix with Ecto Multi
defmodule MyApp.Accounts do
  alias MyApp.{Repo, User, Profile}
  import Ecto.Multi
  
  def create_user_with_profile(attrs, profile_attrs) do
    Ecto.Multi.new()
    |> Ecto.Multi.insert(:user, User.changeset(%User{}, attrs))
    |> Ecto.Multi.run(:profile, fn repo, %{user: user} ->
      changeset = Profile.changeset(%Profile{}, profile_attrs)
      changeset = Ecto.Changeset.put_change(changeset, :user_id, user.id)
      repo.insert(changeset)
    end)
    |> Ecto.Multi.run(:notify, fn repo, %{user: user} ->
      MyApp.Notifications.user_created(user)
      {:ok, user}
    end)
    |> Repo.transaction()
  end
  
  def update_user_with_email(user_id, attrs, email_attrs) do
    Ecto.Multi.new()
    |> Ecto.Multi.update(:user, fn changes ->
      user = Repo.get(User, user_id)
      User.changeset(user, attrs)
    end)
    |> Ecto.Multi.run(:email, fn repo, %{user: user} ->
      MyApp.Email.send_welcome(user.email)
      {:ok, user}
    end)
    |> Repo.transaction()
  end
end
Advanced
49. What is Phoenix with Absinthe GraphQL?

Phoenix with Absinthe GraphQL provides GraphQL API support with schema definition, resolvers, and subscriptions for real-time GraphQL queries.

  • GraphQL Schema: Define types, queries, mutations
  • Resolvers: Handle GraphQL queries
  • Subscriptions: Real-time GraphQL updates
  • Middleware: Authentication and authorization
  • Complexity Analysis: Query complexity limits
elixir
# Phoenix with Absinthe GraphQL
defmodule MyAppWeb.Schema do
  use Absinthe.Schema
  
  import_types MyAppWeb.Types
  
  query do
    field :users, list_of(:user) do
      resolve fn _parent, _args, _resolution ->
        {:ok, MyApp.Repo.all(MyApp.User)}
      end
    end
    
    field :user, :user do
      arg :id, non_null(:id)
      resolve fn %{id: id}, _resolution ->
        {:ok, MyApp.Repo.get(MyApp.User, id)}
      end
    end
  end
  
  mutation do
    field :create_user, :user do
      arg :name, non_null(:string)
      arg :email, non_null(:string)
      arg :age, :integer
      
      resolve fn %{name: name, email: email, age: age}, _resolution ->
        case MyApp.Accounts.create_user(%{name: name, email: email, age: age}) do
          {:ok, user} -> {:ok, user}
          {:error, changeset} -> {:error, changeset}
        end
      end
    end
  end
  
  subscription do
    field :user_created, :user do
      config fn _args, _resolution ->
        {:ok, topic: "user_created"}
      end
    end
  end
end
Advanced
50. What is Phoenix with Absinthe Subscriptions?

Phoenix with Absinthe Subscriptions enables real-time GraphQL subscriptions with Phoenix PubSub, allowing clients to subscribe to data changes.

  • GraphQL Subscriptions: Real-time data streaming
  • PubSub Integration: Phoenix PubSub for subscriptions
  • Topic Management: Manage subscription topics
  • Broadcasting: Broadcast updates to subscribers
  • Use Cases: Live data, notifications, real-time feeds
elixir
# Phoenix with Absinthe Subscriptions
defmodule MyAppWeb.Schema do
  use Absinthe.Schema
  
  subscription do
    field :user_created, :user do
      config fn _args, _resolution ->
        {:ok, topic: "user_created"}
      end
    end
    
    field :post_created, :post do
      config fn _args, _resolution ->
        {:ok, topic: "post_created"}
      end
    end
  end
end

defmodule MyAppWeb.Resolvers do
  def create_user(_parent, args, _resolution) do
    case MyApp.Accounts.create_user(args) do
      {:ok, user} ->
        Absinthe.Subscription.publish(MyAppWeb.Endpoint, user, user_created: "user_created")
        {:ok, user}
      {:error, changeset} -> {:error, changeset}
    end
  end
end

defmodule MyAppWeb.UserSubscription do
  use Absinthe.Subscription
  
  def publish(doc_result, _args, _resolution) do
    {:ok, doc_result}
  end
end
Advanced
51. What is Phoenix Context?

Phoenix Context is a module that encapsulates related functionality and data, providing a clear boundary between different parts of the application.

  • Context Definition: Bounded context for domain logic
  • Data Management: Handle data operations
  • Business Logic: Encapsulate business rules
  • Separation of Concerns: Clear module boundaries
  • Testing: Easily test contexts
elixir
# Phoenix Context Example
defmodule MyApp.Accounts do
  @moduledoc """
  The Accounts context for user management
  """
  
  alias MyApp.{Repo, User, Profile}
  
  def get_user(id) do
    Repo.get(User, id)
  end
  
  def get_user_by_email(email) do
    Repo.get_by(User, email: email)
  end
  
  def list_users do
    Repo.all(User)
  end
  
  def create_user(attrs \ %{}) do
    %User{}
    |> User.changeset(attrs)
    |> Repo.insert()
  end
  
  def update_user(%User{} = user, attrs) do
    user
    |> User.changeset(attrs)
    |> Repo.update()
  end
  
  def delete_user(%User{} = user) do
    Repo.delete(user)
  end
end

defmodule MyApp.Blog do
  @moduledoc """
  The Blog context for post management
  """
  
  alias MyApp.{Repo, Post, Comment}
  
  def list_posts do
    Repo.all(Post)
  end
  
  def get_post(id) do
    Repo.get(Post, id)
  end
  
  def create_post(attrs \ %{}) do
    %Post{}
    |> Post.changeset(attrs)
    |> Repo.insert()
  end
end
Advanced
52. What is Phoenix Context with Ecto?

Phoenix Context with Ecto combines domain logic with database operations, providing a clean separation between business rules and data access.

  • Data Access: Ecto for database operations
  • Business Logic: Encapsulated in contexts
  • Preloading: Eager loading of associations
  • Pagination: Page through large datasets
  • Search: Full-text and partial search
elixir
# Phoenix Context with Ecto
defmodule MyApp.Accounts do
  alias MyApp.{Repo, User, Profile}
  
  def get_user_with_profile(id) do
    query = from u in User,
      where: u.id == ^id,
      preload: [:profile]
    Repo.one(query)
  end
  
  def get_user_with_posts(id) do
    query = from u in User,
      where: u.id == ^id,
      preload: [posts: from(p in Post, order_by: [desc: p.inserted_at])]
    Repo.one(query)
  end
  
  def get_user_with_comments(id) do
    query = from u in User,
      where: u.id == ^id,
      preload: [comments: from(c in Comment, order_by: [desc: c.inserted_at])]
    Repo.one(query)
  end
  
  def search_users(search_term) do
    query = from u in User,
      where: ilike(u.name, ^"%#{search_term}%") or ilike(u.email, ^"%#{search_term}%"),
      order_by: u.name
    Repo.all(query)
  end
  
  def paginate_users(page \ 1, page_size \ 10) do
    query = from u in User, order_by: u.name
    Repo.paginate(query, page: page, page_size: page_size)
  end
end
Advanced
53. What is Phoenix with Cachex?

Phoenix with Cachex provides caching capabilities with TTL support, enabling efficient data caching and performance optimization.

  • Cachex: Caching library for Elixir
  • TTL: Time-to-live for cache entries
  • Cache Operations: Get, put, delete, clear
  • Cache Miss Handling: Compute on cache miss
  • Use Cases: Query caching, API response caching
elixir
# Phoenix with Cachex
defmodule MyApp.Cache do
  use Cachex
  
  def start_link do
    Cachex.start_link(:my_cache)
  end
  
  def get(key) do
    Cachex.get(:my_cache, key)
  end
  
  def put(key, value, ttl \ 3600) do
    Cachex.put(:my_cache, key, value, ttl: ttl)
  end
  
  def delete(key) do
    Cachex.del(:my_cache, key)
  end
  
  def clear do
    Cachex.clear(:my_cache)
  end
  
  def exists?(key) do
    Cachex.exists?(:my_cache, key)
  end
  
  def get_or_compute(key, function, ttl \ 3600) do
    case Cachex.get(:my_cache, key) do
      {:ok, value} when not is_nil(value) -> value
      _ ->
        value = function.()
        put(key, value, ttl)
        value
    end
  end
end

# Usage
MyApp.Cache.put("user:1", %{name: "Alice", age: 25})
user = MyApp.Cache.get("user:1")
IO.puts("User: #{inspect(user)}")
Advanced
54. What is Phoenix with Rate Limiting?

Phoenix with Rate Limiting provides request throttling to prevent abuse and ensure fair usage, with configurable limits and periods.

  • Hammer: Rate limiting library
  • Limit Configuration: Requests per time period
  • Rate Limiting Plug: Plug for rate limiting
  • Headers: Rate limit response headers
  • Use Cases: API protection, DDoS prevention
elixir
# Phoenix with Rate Limiting
defmodule MyApp.RateLimiter do
  use Hammer
  
  def start_link do
    Hammer.start_link(backend: :ets)
  end
  
  def check_rate_limit(key, limit \ 60, period \ 3600) do
    case Hammer.check_rate(key, limit, period) do
      {:allow, count} ->
        {:ok, count}
      {:deny, count} ->
        {:error, "Rate limit exceeded. Limit: #{limit}, Count: #{count}"}
    end
  end
  
  def get_current_count(key) do
    Hammer.get_rate(key)
  end
  
  def reset_rate_limit(key) do
    Hammer.delete_rate(key)
  end
end

defmodule MyAppWeb.RateLimiterPlug do
  import Plug.Conn
  
  def init(options), do: options
  
  def call(conn, opts) do
    key = "api:#{conn.remote_ip}"
    limit = Keyword.get(opts, :limit, 60)
    period = Keyword.get(opts, :period, 3600)
    
    case MyApp.RateLimiter.check_rate_limit(key, limit, period) do
      {:ok, count} ->
        conn
        |> put_resp_header("x-ratelimit-limit", to_string(limit))
        |> put_resp_header("x-ratelimit-remaining", to_string(limit - count))
      {:error, message} ->
        conn
        |> send_resp(429, Jason.encode!(%{error: message}))
        |> halt()
    end
  end
end
Advanced
55. What is Phoenix with File Upload?

Phoenix with File Upload enables handling file uploads with validation, storage, and streaming support for large files.

  • File Upload: Handle multipart file uploads
  • Storage: Store files on disk or cloud
  • Validation: File size, type, and content validation
  • Streaming: Stream large files efficiently
  • Security: Validate file content securely
elixir
# Phoenix with File Upload
defmodule MyAppWeb.UploadController do
  use MyAppWeb, :controller
  
  def upload(conn, %{"upload" => upload}) do
    case MyApp.Uploader.store(upload) do
      {:ok, path} ->
        json(conn, %{success: true, path: path})
      {:error, reason} ->
        conn |> put_status(400) |> json(%{error: reason})
    end
  end
  
  def upload_multiple(conn, %{"uploads" => uploads}) do
    results = Enum.map(uploads, fn upload ->
      MyApp.Uploader.store(upload)
    end)
    
    json(conn, %{results: results})
  end
end

defmodule MyApp.Uploader do
  def store(upload) do
    path = Path.join(["uploads", upload.filename])
    
    case File.write(path, upload.content) do
      :ok -> {:ok, path}
      {:error, reason} -> {:error, reason}
    end
  end
  
  def stream_upload(upload, chunk_size \ 1024) do
    path = Path.join(["uploads", upload.filename])
    
    File.open(path, [:write], fn file ->
      Enum.each(upload.stream, fn chunk ->
        IO.binwrite(file, chunk)
      end)
    end)
  end
end
Advanced
56. What is Phoenix with Email Sending?

Phoenix with Email Sending provides email capabilities with templates, attachments, and delivery through various email providers.

  • Bamboo: Email sending library
  • Email Templates: HTML and text templates
  • Delivery: SMTP, SendGrid, Mailgun, etc.
  • Attachments: Include file attachments
  • Background Sending: Send emails asynchronously
elixir
# Phoenix with Email Sending
defmodule MyApp.Mailer do
  use Bamboo.Mailer, otp_app: :my_app
end

defmodule MyApp.Email do
  import Bamboo.Email
  
  def welcome_email(user) do
    new_email()
    |> to(user.email)
    |> from("no-reply@myapp.com")
    |> subject("Welcome to MyApp!")
    |> text_body("Hello #{user.name}, welcome to MyApp!")
    |> html_body("<h1>Hello #{user.name}</h1><p>Welcome to MyApp!</p>")
  end
  
  def reset_password_email(user, token) do
    new_email()
    |> to(user.email)
    |> from("no-reply@myapp.com")
    |> subject("Reset your password")
    |> text_body("Click the link to reset your password: /reset?token=#{token}")
    |> html_body("<a href='/reset?token=#{token}'>Reset Password</a>")
  end
  
  def notification_email(user, message) do
    new_email()
    |> to(user.email)
    |> from("notifications@myapp.com")
    |> subject("New Notification")
    |> text_body(message)
    |> html_body("<p>#{message}</p>")
  end
end

# Usage
user = %{name: "Alice", email: "alice@email.com"}
email = MyApp.Email.welcome_email(user)
MyApp.Mailer.deliver_now(email)
Advanced
57. What is Phoenix with Background Jobs?

Phoenix with Background Jobs enables processing long-running tasks asynchronously with job scheduling, retries, and error handling.

  • Oban: Background job processing library
  • Job Scheduling: Schedule jobs with cron
  • Retries: Automatic retry on failure
  • Queues: Prioritize jobs with multiple queues
  • Monitoring: Track job status and performance
elixir
# Phoenix with Background Jobs
defmodule MyApp.Worker do
  use Oban.Worker
  
  def perform(%Oban.Job{args: args}) do
    IO.puts("Processing job: #{inspect(args)}")
    # Do work
    :ok
  end
end

defmodule MyApp.Scheduler do
  use Oban.Worker
  
  @impl Oban.Worker
  def perform(%Oban.Job{args: %{"schedule" => schedule}}) do
    # Schedule work
    :ok
  end
end

defmodule MyApp.Jobs do
  alias MyApp.Repo
  
  def process_user(user_id) do
    %{user_id: user_id}
    |> MyApp.Worker.new()
    |> Oban.insert()
  end
  
  def schedule_cleanup(days \ 30) do
    %{days: days}
    |> MyApp.Scheduler.new(schedule: "0 0 * * *")
    |> Oban.insert()
  end
  
  def process_bulk(users) do
    Enum.each(users, fn user ->
      process_user(user.id)
    end)
  end
end

# Oban configuration
defmodule MyApp.Application do
  use Application
  
  def start(_type, _args) do
    children = [
      {Oban, oban_config()}
    ]
    
    Supervisor.start_link(children, strategy: :one_for_one)
  end
  
  defp oban_config do
    [
      repo: MyApp.Repo,
      plugins: [Oban.Plugins.Pruner],
      queues: [
        default: 10,
        mailers: 5,
        heavy: 2
      ]
    ]
  end
end
Advanced
58. What is Phoenix with CORS?

Phoenix with CORS enables Cross-Origin Resource Sharing with configurable origins, methods, and headers for API security.

  • CORS Plug: Handle CORS requests
  • Origin Configuration: Allow specific origins
  • Methods: Allow specific HTTP methods
  • Headers: Allow specific headers
  • Preflight: Handle OPTIONS preflight requests
elixir
# Phoenix with CORS
defmodule MyAppWeb.CORSPlug do
  import Plug.Conn
  
  def init(options), do: options
  
  def call(conn, _opts) do
    conn
    |> put_resp_header("access-control-allow-origin", "*")
    |> put_resp_header("access-control-allow-methods", "GET, POST, PUT, DELETE, OPTIONS")
    |> put_resp_header("access-control-allow-headers", "Content-Type, Authorization")
    |> put_resp_header("access-control-max-age", "86400")
    |> handle_preflight()
  end
  
  defp handle_preflight(conn) do
    if conn.method == "OPTIONS" do
      conn
      |> send_resp(204, "")
      |> halt()
    else
      conn
    end
  end
end

defmodule MyAppWeb.Router do
  use MyAppWeb, :router
  
  pipeline :api do
    plug MyAppWeb.CORSPlug
    plug :accepts, ["json"]
  end
  
  scope "/api", MyAppWeb do
    pipe_through :api
    resources "/users", UserController
  end
end
Advanced
59. What is Phoenix with JSON Web Tokens?

Phoenix with JSON Web Tokens provides authentication and authorization using JWT tokens with built-in token generation and verification.

  • JWT: JSON Web Token authentication
  • Token Generation: Create JWT tokens
  • Token Verification: Verify and decode tokens
  • Authentication Plug: Authenticate requests with JWT
  • Refresh Tokens: Token refresh mechanism
elixir
# Phoenix with JSON Web Tokens
defmodule MyApp.Auth do
  import Bcrypt
  
  def generate_token(user) do
    {:ok, token, _claims} = JWT.encode(%{user_id: user.id, exp: exp_time()}, secret_key())
    token
  end
  
  def verify_token(token) do
    case JWT.decode(token, secret_key()) do
      {:ok, claims} ->
        {:ok, claims}
      {:error, reason} ->
        {:error, reason}
    end
  end
  
  def authenticate(email, password) do
    case MyApp.Accounts.get_user_by_email(email) do
      nil -> {:error, "Invalid email or password"}
      user ->
        if verify_password(password, user.password_hash) do
          {:ok, user}
        else
          {:error, "Invalid email or password"}
        end
    end
  end
  
  defp secret_key do
    Application.get_env(:my_app, :secret_key_base)
  end
  
  defp exp_time do
    System.system_time(:second) + 3600
  end
end

defmodule MyAppWeb.AuthPlug do
  import Plug.Conn
  
  def init(options), do: options
  
  def call(conn, _opts) do
    case get_req_header(conn, "authorization") do
      ["Bearer " <> token] ->
        case MyApp.Auth.verify_token(token) do
          {:ok, claims} ->
            user = MyApp.Accounts.get_user(claims["user_id"])
            assign(conn, :current_user, user)
          {:error, _} ->
            conn |> send_resp(401, "Unauthorized") |> halt()
        end
      _ ->
        conn |> send_resp(401, "Unauthorized") |> halt()
    end
  end
end
Advanced
60. What is Phoenix with Websockets?

Phoenix with Websockets provides WebSocket support with channels for real-time communication, enabling bidirectional message passing.

  • WebSocket: Persistent bidirectional connection
  • Channels: Group WebSocket connections
  • PubSub: Broadcast messages to channels
  • Client-side Integration: Phoenix client for JavaScript
  • Authentication: Socket authentication
elixir
# Phoenix with Websockets
defmodule MyAppWeb.RoomChannel do
  use Phoenix.Channel
  alias MyAppWeb.Presence
  
  def join("room:" <> room_id, _params, socket) do
    {:ok, assign(socket, :room_id, room_id)}
  end
  
  def handle_in("new_msg", %{"body" => body}, socket) do
    broadcast!(socket, "new_msg", %{body: body, user: socket.assigns.user_id})
    {:noreply, socket}
  end
  
  def handle_in("typing", %{"typing" => typing}, socket) do
    broadcast!(socket, "typing", %{user: socket.assigns.user_id, typing: typing})
    {:noreply, socket}
  end
  
  def handle_out("new_msg", payload, socket) do
    push(socket, "new_msg", payload)
    {:noreply, socket}
  end
end

# Client-side JavaScript
# let socket = new Phoenix.Socket("/socket", {params: {token: userToken}})
# socket.connect()
# let channel = socket.channel("room:lobby", {})
# channel.join()
#   .receive("ok", resp => { console.log("Joined successfully", resp) })
#   .receive("error", resp => { console.log("Unable to join", resp) })

# channel.on("new_msg", payload => {
#   console.log("New message:", payload.body)
# })

# channel.push("new_msg", {body: "Hello!"})
Advanced
61. What is Phoenix with LiveView Forms?

Phoenix LiveView Forms enables building interactive forms with real-time validation and error handling using Ecto changesets.

  • Form Building: form_for and text_input
  • Real-time Validation: Validate on input change
  • Error Display: Show errors inline
  • Ecto Integration: Changeset-based forms
  • Nested Forms: Handle nested data structures
elixir
# Phoenix with LiveView Forms
defmodule MyAppWeb.UserFormLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, changeset: User.changeset(%User{}, %{}))}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <.form let={f} for={@changeset} phx-submit="save" phx-change="validate">
        <div>
          <%= label(f, :name) %>
          <%= text_input(f, :name) %>
          <%= error_tag(f, :name) %>
        </div>
        
        <div>
          <%= label(f, :email) %>
          <%= email_input(f, :email) %>
          <%= error_tag(f, :email) %>
        </div>
        
        <div>
          <%= label(f, :age) %>
          <%= number_input(f, :age) %>
          <%= error_tag(f, :age) %>
        </div>
        
        <div>
          <%= submit("Save") %>
        </div>
      </.form>
    </div>
    """
  end
  
  def handle_event("validate", %{"user" => user_params}, socket) do
    changeset = User.changeset(%User{}, user_params)
    {:noreply, assign(socket, changeset: changeset)}
  end
  
  def handle_event("save", %{"user" => user_params}, socket) do
    case MyApp.Accounts.create_user(user_params) do
      {:ok, user} ->
        {:noreply, socket |> put_flash(:info, "User created")}
      {:error, changeset} ->
        {:noreply, assign(socket, changeset: changeset)}
    end
  end
end
Advanced
62. What is Phoenix with LiveView Pagination?

Phoenix LiveView Pagination enables handling large datasets with pagination controls and efficient data loading.

  • Pagination Controls: Next/Previous buttons
  • Page Size: Configurable items per page
  • Efficient Loading: Load only visible data
  • Database Pagination: Ecto pagination with offset/limit
  • State Management: Track current page
elixir
# Phoenix with LiveView Pagination
defmodule MyAppWeb.UsersLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, page: 1, users: [], total: 0)}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Users</h2>
      <table>
        <thead>
          <tr><th>Name</th><th>Email</th></tr>
        </thead>
        <tbody>
          <%= for user <- @users do %>
            <tr><td><%= user.name %></td><td><%= user.email %></td></tr>
          <% end %>
        </tbody>
      </table>
      
      <div>
        <%= if @page > 1 do %>
          <button phx-click="prev">Previous</button>
        <% end %>
        <span>Page <%= @page %></span>
        <%= if @page * 10 < @total do %>
          <button phx-click="next">Next</button>
        <% end %>
      </div>
    </div>
    """
  end
  
  def handle_event("next", _params, socket) do
    {:noreply, load_page(socket, socket.assigns.page + 1)}
  end
  
  def handle_event("prev", _params, socket) do
    {:noreply, load_page(socket, socket.assigns.page - 1)}
  end
  
  defp load_page(socket, page) do
    {users, total} = MyApp.Accounts.paginate_users(page)
    assign(socket, page: page, users: users, total: total)
  end
end
Advanced
64. What is Phoenix with LiveView Notifications?

Phoenix LiveView Notifications provides real-time notification delivery with PubSub integration and toast notifications.

  • PubSub Integration: Phoenix PubSub for notifications
  • Real-time Delivery: Instant notification display
  • Toast Notifications: Popup notifications
  • Notification State: Track unread notifications
  • Mark as Read: Handle notification interactions
elixir
# Phoenix with LiveView Notifications
defmodule MyAppWeb.NotificationsLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    if connected?(socket) do
      Phoenix.PubSub.subscribe(MyApp.PubSub, "notifications")
    end
    {:ok, assign(socket, notifications: [])}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Notifications</h2>
      <ul>
        <%= for notification <- @notifications do %>
          <li><%= notification %></li>
        <% end %>
      </ul>
    </div>
    """
  end
  
  def handle_info({:notification, message}, socket) do
    {:noreply, update(socket, :notifications, fn msgs -> [message | msgs] end)}
  end
end

defmodule MyApp.Notifications do
  def notify(message) do
    Phoenix.PubSub.broadcast(MyApp.PubSub, "notifications", {:notification, message})
  end
end
Advanced
65. What is Phoenix with LiveView Components?

Phoenix LiveView Components are reusable UI building blocks that encapsulate rendering logic and state for consistent user interfaces.

  • Components: Reusable UI elements
  • Slots: Content placeholder for components
  • Event Handling: Handle events within components
  • State Management: Component-specific state
  • Composition: Compose complex UIs from components
elixir
# Phoenix with LiveView Components
defmodule MyAppWeb.Components do
  use Phoenix.Component
  
  def button(assigns) do
    ~H"""
    <button class="btn btn-primary" phx-click={@click}>
      <%= @label %>
    </button>
    """
  end
  
  def card(assigns) do
    ~H"""
    <div class="card">
      <div class="card-header">
        <%= @title %>
      </div>
      <div class="card-body">
        <%= render_slot(@inner_block) %>
      </div>
    </div>
    """
  end
  
  def modal(assigns) do
    ~H"""
    <div class="modal" style="display: #{if @open, do: 'block', else: 'none'}">
      <div class="modal-content">
        <div class="modal-header">
          <h3><%= @title %></h3>
          <button phx-click={@close}>Close</button>
        </div>
        <div class="modal-body">
          <%= render_slot(@inner_block) %>
        </div>
      </div>
    </div>
    """
  end
end

# Usage in LiveView
defmodule MyAppWeb.DemoLive do
  use MyAppWeb, :live_view
  import MyAppWeb.Components
  
  def render(assigns) do
    ~H"""
    <div>
      <.button click="increment" label="Increment" />
      <.card title="User Info">
        <p>Name: <%= @user.name %></p>
        <p>Email: <%= @user.email %></p>
      </.card>
      
      <.modal open={@show_modal} title="Edit User" close="close_modal">
        <form phx-submit="save_user">
          <input type="text" name="name" value={@user.name} />
          <input type="email" name="email" value={@user.email} />
          <button type="submit">Save</button>
        </form>
      </.modal>
    </div>
    """
  end
end
Advanced
66. What is Phoenix with LiveView Slots?

Phoenix LiveView Slots enable component composition by providing placeholder content that can be filled by the parent component.

  • Named Slots: Named content placeholders
  • Render Slots: render_slot function
  • Default Slot: @inner_block for default content
  • Component Composition: Build complex UIs
  • Template Inheritance: Reusable layouts
elixir
# Phoenix with LiveView Slots
defmodule MyAppWeb.TableComponent do
  use Phoenix.Component
  
  slot :header
  slot :row, required: true
  slot :footer
  
  def table(assigns) do
    ~H"""
    <table class="table">
      <thead>
        <tr>
          <%= for header <- @header do %>
            <th><%= render_slot(header) %></th>
          <% end %>
        </tr>
      </thead>
      <tbody>
        <%= for row <- @row do %>
          <tr><%= render_slot(row) %></tr>
        <% end %>
      </tbody>
      <tfoot>
        <%= render_slot(@footer) %>
      </tfoot>
    </table>
    """
  end
end

defmodule MyAppWeb.UsersLive do
  use MyAppWeb, :live_view
  import MyAppWeb.TableComponent
  
  def render(assigns) do
    ~H"""
    <.table>
      <:header>Name</:header>
      <:header>Email</:header>
      <:header>Age</:header>
      
      <%= for user <- @users do %>
        <:row>
          <td><%= user.name %></td>
          <td><%= user.email %></td>
          <td><%= user.age %></td>
        </:row>
      <% end %>
      
      <:footer>
        <tr><td colspan="3">Total: <%= length(@users) %> users</td></tr>
      </:footer>
    </.table>
    """
  end
end
Advanced
67. What is Phoenix with LiveView Upload?

Phoenix LiveView Upload enables file uploads with progress tracking and real-time feedback for large file uploads.

  • File Upload: Handle file uploads in LiveView
  • Progress Tracking: Show upload progress
  • File Validation: Validate file types and sizes
  • Multiple Files: Upload multiple files
  • Storage: Store files on disk or cloud
elixir
# Phoenix with LiveView Upload
defmodule MyAppWeb.UploadLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, uploads: [])}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>File Upload</h2>
      <form phx-submit="upload" phx-change="validate">
        <input type="file" name="file" accept=".jpg,.png,.pdf" multiple />
        <button type="submit">Upload</button>
      </form>
      
      <ul>
        <%= for upload <- @uploads do %>
          <li><%= upload.filename %> - <%= upload.size %> bytes</li>
        <% end %>
      </ul>
    </div>
    """
  end
  
  def handle_event("validate", %{"file" => file}, socket) do
    {:noreply, assign(socket, uploads: [file])}
  end
  
  def handle_event("upload", %{"file" => file}, socket) do
    case MyApp.Uploader.store(file) do
      {:ok, path} ->
        uploads = [%{filename: file.filename, path: path} | socket.assigns.uploads]
        {:noreply, assign(socket, uploads: uploads)}
      {:error, reason} ->
        {:noreply, put_flash(socket, :error, reason)}
    end
  end
end
Advanced
68. What is Phoenix with LiveView Charts?

Phoenix LiveView Charts enables creating interactive charts and visualizations with LiveView and JavaScript hooks.

  • Chart Integration: JavaScript libraries like Chart.js
  • LiveView Hooks: phx-hook for client-side integration
  • Real-time Updates: Update charts on data change
  • Data Binding: Bind LiveView data to charts
  • Interactive Charts: User interaction with charts
elixir
# Phoenix with LiveView Charts
defmodule MyAppWeb.ChartLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, data: generate_data())}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Chart</h2>
      <div id="chart" phx-hook="Chart" data-data={inspect(@data)}>
        <canvas id="chart-canvas"></canvas>
      </div>
      
      <button phx-click="refresh">Refresh</button>
    </div>
    """
  end
  
  def handle_event("refresh", _params, socket) do
    {:noreply, assign(socket, data: generate_data())}
  end
  
  defp generate_data do
    Enum.map(1..10, fn i ->
      %{label: "Item #{i}", value: :rand.uniform(100)}
    end)
  end
end

# JavaScript hook
# const Chart = {
#   mounted() {
#     this.drawChart(this.el.dataset.data)
#   },
#   updated() {
#     this.drawChart(this.el.dataset.data)
#   },
#   drawChart(data) {
#     const parsedData = JSON.parse(data)
#     // Draw chart using Chart.js or other library
#   }
# }
# window.Chart = Chart
Advanced
69. What is Phoenix with LiveView Maps?

Phoenix LiveView Maps enables integrating interactive maps with LiveView for location-based features and real-time updates.

  • Map Integration: Leaflet, Mapbox, Google Maps
  • LiveView Hooks: phx-hook for map integration
  • Markers: Add and update map markers
  • Real-time Updates: Update maps on data change
  • Interactive Maps: Click, drag, zoom interactions
elixir
# Phoenix with LiveView Maps
defmodule MyAppWeb.MapLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, markers: [], center: %{lat: 0, lng: 0})}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Map</h2>
      <div id="map" phx-hook="Map" data-markers={inspect(@markers)} data-center={inspect(@center)}>
        <div id="map-container" style="height: 400px;"></div>
      </div>
      
      <button phx-click="add_marker">Add Marker</button>
    </div>
    """
  end
  
  def handle_event("add_marker", _params, socket) do
    markers = [%{lat: :rand.uniform(180) - 90, lng: :rand.uniform(360) - 180} | socket.assigns.markers]
    {:noreply, assign(socket, markers: markers)}
  end
  
  def handle_event("map_click", %{"lat" => lat, "lng" => lng}, socket) do
    markers = [%{lat: lat, lng: lng} | socket.assigns.markers]
    {:noreply, assign(socket, markers: markers)}
  end
end
Advanced
70. What is Phoenix with LiveView Charts?

Phoenix LiveView Charts enables creating interactive charts and visualizations with real-time data updates and client-side rendering.

  • Chart Libraries: Chart.js, Nivo, Recharts
  • LiveView Hooks: phx-hook for chart integration
  • Real-time Data: Update charts with LiveView state
  • Interactive Charts: Hover, click, zoom interactions
  • Data Visualization: Present data visually
elixir
# Phoenix with LiveView Charts
defmodule MyAppWeb.ChartLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, data: generate_data())}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Chart</h2>
      <div id="chart" phx-hook="Chart" data-data={inspect(@data)}>
        <canvas id="chart-canvas"></canvas>
      </div>
      
      <button phx-click="refresh">Refresh</button>
    </div>
    """
  end
  
  def handle_event("refresh", _params, socket) do
    {:noreply, assign(socket, data: generate_data())}
  end
  
  defp generate_data do
    Enum.map(1..10, fn i ->
      %{label: "Item #{i}", value: :rand.uniform(100)}
    end)
  end
end
Advanced
71. What is Phoenix with LiveView Stream?

Phoenix LiveView Stream enables efficient streaming of data with lazy loading and pagination for large datasets.

  • Stream Data: Process data in chunks
  • Lazy Loading: Load data on demand
  • Memory Efficiency: Process large datasets
  • Use Cases: Infinite scroll, large reports
  • Data Processing: Transform data on stream
elixir
# Phoenix with LiveView Stream
defmodule MyAppWeb.StreamLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    stream = 1..1000
    |> Stream.map(& &1)
    |> Stream.chunk_every(10)
    
    {:ok, assign(socket, stream: stream, items: [])}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Stream</h2>
      <ul>
        <%= for item <- @items do %>
          <li><%= item %></li>
        <% end %>
      </ul>
      <button phx-click="load_more">Load More</button>
    </div>
    """
  end
  
  def handle_event("load_more", _params, socket) do
    {items, stream} = Enum.split(socket.assigns.stream, 10)
    {:noreply, assign(socket, items: items, stream: stream)}
  end
end
Advanced
72. What is Phoenix with LiveView Infinite Scroll?

Phoenix LiveView Infinite Scroll provides infinite scrolling with lazy loading and efficient data fetching.

  • Infinite Scroll: Load more data on scroll
  • Lazy Loading: Load data as needed
  • Scroll Detection: Detect scroll end
  • Loading State: Show loading indicators
  • Performance: Efficient data loading
elixir
# Phoenix with LiveView Infinite Scroll
defmodule MyAppWeb.InfiniteScrollLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    items = 1..20 |> Enum.to_list()
    {:ok, assign(socket, items: items, loading: false, page: 1)}
  end
  
  def render(assigns) do
    ~H"""
    <div id="infinite-scroll" phx-hook="InfiniteScroll">
      <ul>
        <%= for item <- @items do %>
          <li><%= item %></li>
        <% end %>
      </ul>
      <div id="loader" class={if @loading, do: "visible", else: "hidden"}>
        Loading...
      </div>
    </div>
    """
  end
  
  def handle_event("load_more", _params, socket) do
    {:noreply, load_more(socket)}
  end
  
  defp load_more(socket) do
    page = socket.assigns.page + 1
    new_items = Enum.map((page - 1) * 20 + 1..page * 20, & &1)
    items = socket.assigns.items ++ new_items
    assign(socket, items: items, page: page, loading: false)
  end
end
Advanced
73. What is Phoenix with LiveView Drag and Drop?

Phoenix LiveView Drag and Drop enables drag-and-drop interactions with real-time reordering and state updates.

  • Drag and Drop: HTML5 drag and drop API
  • Real-time Updates: Update LiveView state on drag
  • Reordering: Drag to reorder items
  • LiveView Hooks: phx-hook for drag events
  • Visual Feedback: Show drag indicators
elixir
# Phoenix with LiveView Drag and Drop
defmodule MyAppWeb.DragDropLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    items = 1..5 |> Enum.map(fn i -> %{id: i, text: "Item #{i}"} end)
    {:ok, assign(socket, items: items)}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Drag and Drop</h2>
      <ul id="drag-drop" phx-hook="DragDrop">
        <%= for item <- @items do %>
          <li data-id={item.id} draggable="true" phx-value-id={item.id}>
            <%= item.text %>
          </li>
        <% end %>
      </ul>
    </div>
    """
  end
  
  def handle_event("drop", %{"from" => from_id, "to" => to_id}, socket) do
    items = reorder_items(socket.assigns.items, from_id, to_id)
    {:noreply, assign(socket, items: items)}
  end
  
  defp reorder_items(items, from_id, to_id) do
    from_idx = Enum.find_index(items, &(&1.id == from_id))
    to_idx = Enum.find_index(items, &(&1.id == to_id))
    
    items
    |> List.delete_at(from_idx)
    |> List.insert_at(to_idx, Enum.at(items, from_idx))
  end
end
Advanced
74. What is Phoenix with LiveView Authentication?

Phoenix LiveView Authentication provides user authentication with login, registration, and session management.

  • Login/Logout: User authentication flows
  • Session Management: Track user sessions
  • Protected Routes: Require authentication
  • Authentication Hooks: on_mount for auth
  • Current User: Access current user in LiveView
elixir
# Phoenix with LiveView Authentication
defmodule MyAppWeb.AuthLive do
  use Phoenix.LiveView
  
  def mount(_params, session, socket) do
    {:ok, assign(socket, current_user: session["current_user"])}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <%= if @current_user do %>
        <div>Welcome, <%= @current_user.name %>!</div>
        <button phx-click="logout">Logout</button>
      <% else %>
        <form phx-submit="login">
          <input type="email" name="email" placeholder="Email" />
          <input type="password" name="password" placeholder="Password" />
          <button type="submit">Login</button>
        </form>
        <a href="/register">Register</a>
      <% end %>
    </div>
    """
  end
  
  def handle_event("login", %{"email" => email, "password" => password}, socket) do
    case MyApp.Auth.authenticate(email, password) do
      {:ok, user} ->
        {:noreply, assign(socket, current_user: user)}
      {:error, _} ->
        {:noreply, put_flash(socket, :error, "Invalid email or password")}
    end
  end
  
  def handle_event("logout", _params, socket) do
    {:noreply, assign(socket, current_user: nil)}
  end
end
Advanced
75. What is Phoenix with LiveView Authorization?

Phoenix LiveView Authorization enables role-based access control with permission checks and authorization hooks.

  • Role-based Access: Admin, user, guest roles
  • Authorization Hooks: on_mount for permissions
  • Protected Views: Restrict access by role
  • Conditional Rendering: Show/hide content by permission
  • Use Cases: Admin panels, user-specific features
elixir
# Phoenix with LiveView Authorization
defmodule MyAppWeb.Auth do
  import Phoenix.LiveView
  
  def on_mount(:current_user, _params, session, socket) do
    {:cont, assign(socket, current_user: session["current_user"])}
  end
  
  def on_mount(:require_user, _params, _session, socket) do
    if socket.assigns.current_user do
      {:cont, socket}
    else
      {:halt, redirect(socket, to: "/login")}
    end
  end
  
  def on_mount(:require_admin, _params, _session, socket) do
    if socket.assigns.current_user && socket.assigns.current_user.is_admin do
      {:cont, socket}
    else
      {:halt, redirect(socket, to: "/unauthorized")}
    end
  end
end

defmodule MyAppWeb.Router do
  use MyAppWeb, :router
  
  live_session :authenticated, on_mount: {MyAppWeb.Auth, :require_user} do
    live "/dashboard", DashboardLive
    live "/profile", ProfileLive
  end
  
  live_session :admin, on_mount: {MyAppWeb.Auth, :require_admin} do
    live "/admin", AdminLive
    live "/admin/users", AdminUsersLive
  end
end
Advanced
76. What is Phoenix with LiveView Charts?

Phoenix LiveView Charts enables creating interactive charts and visualizations with real-time data updates.

  • Chart Integration: Chart.js, ECharts, Nivo
  • LiveView Hooks: phx-hook for chart rendering
  • Real-time Updates: Update charts on data change
  • Interactive Charts: Hover, click, zoom
  • Data Binding: Bind LiveView data to charts
elixir
# Phoenix with LiveView Charts
defmodule MyAppWeb.ChartLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, data: generate_data())}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Chart</h2>
      <div id="chart" phx-hook="Chart" data-data={inspect(@data)}>
        <canvas id="chart-canvas"></canvas>
      </div>
      
      <button phx-click="refresh">Refresh</button>
    </div>
    """
  end
  
  def handle_event("refresh", _params, socket) do
    {:noreply, assign(socket, data: generate_data())}
  end
  
  defp generate_data do
    Enum.map(1..10, fn i ->
      %{label: "Item #{i}", value: :rand.uniform(100)}
    end)
  end
end
Advanced
77. What is Phoenix with LiveView Calendar?

Phoenix LiveView Calendar provides calendar views with event management and navigation through months.

  • Calendar Views: Monthly, weekly, daily views
  • Event Management: Add, edit, delete events
  • Navigation: Previous/next month navigation
  • Click Events: Click on dates for events
  • Real-time Updates: Update calendar on event changes
elixir
# Phoenix with LiveView Calendar
defmodule MyAppWeb.CalendarLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    today = Date.utc_today()
    {:ok, assign(socket, date: today, events: [])}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Calendar</h2>
      <div>
        <button phx-click="prev_month">‹</button>
        <span><%= Calendar.strftime(@date, "%B %Y") %></span>
        <button phx-click="next_month">›</button>
      </div>
      
      <table>
        <thead>
          <tr>
            <th>Mon</th><th>Tue</th><th>Wed</th>
            <th>Thu</th><th>Fri</th><th>Sat</th><th>Sun</th>
          </tr>
        </thead>
        <tbody>
          <%= for week <- calendar(@date) do %>
            <tr>
              <%= for day <- week do %>
                <td class={if day, do: "day", else: "empty"}>
                  <%= if day do %>
                    <span phx-click="select_date" phx-value-day={day}><%= day %></span>
                  <% end %>
                </td>
              <% end %>
            </tr>
          <% end %>
        </tbody>
      </table>
    </div>
    """
  end
  
  defp calendar(date) do
    # Generate calendar grid
    # Implementation omitted for brevity
    []
  end
  
  def handle_event("prev_month", _params, socket) do
    date = Date.add(socket.assigns.date, -30)
    {:noreply, assign(socket, date: date)}
  end
  
  def handle_event("next_month", _params, socket) do
    date = Date.add(socket.assigns.date, 30)
    {:noreply, assign(socket, date: date)}
  end
end
Advanced
78. What is Phoenix with LiveView PDF Generation?

Phoenix LiveView PDF Generation enables generating PDF documents from LiveView content with server-side rendering.

  • PDF Generation: Generate PDF from HTML
  • LiveView Rendering: Render LiveView to HTML
  • Server-side Generation: Generate PDF on server
  • Download: Download generated PDF
  • Use Cases: Invoices, reports, documents
elixir
# Phoenix with LiveView PDF Generation
defmodule MyAppWeb.PDFLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, data: generate_data())}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>PDF Generator</h2>
      <button phx-click="generate_pdf">Generate PDF</button>
      
      <div id="pdf-preview">
        <h3>Preview</h3>
        <table>
          <thead>
            <tr><th>Name</th><th>Value</th></tr>
          </thead>
          <tbody>
            <%= for item <- @data do %>
              <tr><td><%= item.name %></td><td><%= item.value %></td></tr>
            <% end %>
          </tbody>
        </table>
      </div>
    </div>
    """
  end
  
  def handle_event("generate_pdf", _params, socket) do
    html = render_to_string(socket)
    
    case MyApp.PDFGenerator.generate(html) do
      {:ok, pdf} ->
        {:noreply, push_event(socket, "download_pdf", %{data: Base.encode64(pdf)})}
      {:error, reason} ->
        {:noreply, put_flash(socket, :error, reason)}
    end
  end
  
  defp render_to_string(socket) do
    # Render LiveView to HTML string
    # Implementation omitted for brevity
    "<html>...</html>"
  end
end
Advanced
79. What is Phoenix with LiveView Export?

Phoenix LiveView Export enables exporting data in various formats like CSV, JSON, and XML with user interface controls.

  • Export Formats: CSV, JSON, XML, Excel
  • Format Selection: Choose export format
  • Data Processing: Transform data for export
  • Download: Download exported files
  • Progress Tracking: Show export progress
elixir
# Phoenix with LiveView Export
defmodule MyAppWeb.ExportLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, data: [], format: "csv", exporting: false)}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Export Data</h2>
      <div>
        <label>Format:</label>
        <select phx-change="format" phx-value-format={@format}>
          <option value="csv">CSV</option>
          <option value="json">JSON</option>
          <option value="xml">XML</option>
        </select>
      </div>
      
      <button phx-click="export" disabled={@exporting}>
        <%= if @exporting do %>
          Exporting...
        <% else %>
          Export
        <% end %>
      </button>
      
      <div>
        <%= if @exporting do %>
          <div class="progress">Processing...</div>
        <% end %>
      </div>
    </div>
    """
  end
  
  def handle_event("format", %{"format" => format}, socket) do
    {:noreply, assign(socket, format: format)}
  end
  
  def handle_event("export", _params, socket) do
    {:noreply, assign(socket, exporting: true)}
  end
end
Advanced
80. What is Phoenix with LiveView Import?

Phoenix LiveView Import provides data import functionality with file upload, validation, and processing feedback.

  • File Upload: Upload import files
  • Validation: Validate import data
  • Processing: Process imported data
  • Feedback: Show import results and errors
  • Progress Tracking: Show import progress
elixir
# Phoenix with LiveView Import
defmodule MyAppWeb.ImportLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, file: nil, importing: false, results: nil)}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Import Data</h2>
      <form phx-submit="import" phx-change="validate">
        <input type="file" name="file" accept=".csv,.json" />
        <button type="submit" disabled={@importing}>
          <%= if @importing do %>
            Importing...
          <% else %>
            Import
          <% end %>
        </button>
      </form>
      
      <%= if @results do %>
        <div class="results">
          <h3>Import Results</h3>
          <p>Imported: <%= @results.imported %></p>
          <p>Failed: <%= @results.failed %></p>
          <ul>
            <%= for error <- @results.errors do %>
              <li><%= error %></li>
            <% end %>
          </ul>
        </div>
      <% end %>
    </div>
    """
  end
  
  def handle_event("validate", %{"file" => file}, socket) do
    {:noreply, assign(socket, file: file)}
  end
  
  def handle_event("import", _params, socket) do
    {:noreply, assign(socket, importing: true)}
  end
end
Advanced
81. What is Phoenix with LiveView Form Validation?

Phoenix LiveView Form Validation provides real-time form validation with Ecto changesets and inline error messages.

  • Real-time Validation: Validate on input change
  • Changeset Integration: Use Ecto changesets
  • Error Display: Show inline errors
  • Field Errors: Display errors per field
  • Form Submission: Validate on submit
elixir
# Phoenix with LiveView Form Validation
defmodule MyAppWeb.FormLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    changeset = User.changeset(%User{}, %{})
    {:ok, assign(socket, changeset: changeset, submitted: false)}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Form</h2>
      <.form let={f} for={@changeset} phx-submit="save" phx-change="validate">
        <div>
          <label>Name</label>
          <input name="user[name]" value={get_change(f, :name)} />
          <div class="error"><%= error(f, :name) %></div>
        </div>
        
        <div>
          <label>Email</label>
          <input name="user[email]" type="email" value={get_change(f, :email)} />
          <div class="error"><%= error(f, :email) %></div>
        </div>
        
        <div>
          <label>Age</label>
          <input name="user[age]" type="number" value={get_change(f, :age)} />
          <div class="error"><%= error(f, :age) %></div>
        </div>
        
        <button type="submit">Submit</button>
      </.form>
    </div>
    """
  end
  
  def handle_event("validate", %{"user" => user_params}, socket) do
    changeset = User.changeset(%User{}, user_params)
    {:noreply, assign(socket, changeset: changeset)}
  end
  
  def handle_event("save", %{"user" => user_params}, socket) do
    case MyApp.Accounts.create_user(user_params) do
      {:ok, user} ->
        {:noreply, assign(socket, submitted: true)}
      {:error, changeset} ->
        {:noreply, assign(socket, changeset: changeset)}
    end
  end
end
Advanced
82. What is Phoenix with LiveView Multi-step Form?

Phoenix LiveView Multi-step Form enables creating multi-step forms with navigation, data persistence, and validation across steps.

  • Step Navigation: Next/Previous navigation
  • Data Persistence: Preserve data across steps
  • Step Validation: Validate each step
  • Progress Indicator: Show current step
  • Review Step: Review before submission
elixir
# Phoenix with LiveView Multi-step Form
defmodule MyAppWeb.MultiStepFormLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, step: 1, data: %{})}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Multi-step Form</h2>
      <div class="steps">
        <div class={if @step >= 1, do: "active"}>Step 1</div>
        <div class={if @step >= 2, do: "active"}>Step 2</div>
        <div class={if @step >= 3, do: "active"}>Step 3</div>
      </div>
      
      <form phx-submit="next_step">
        <%= if @step == 1 do %>
          <h3>Personal Information</h3>
          <input name="name" placeholder="Name" value={@data[:name]} />
          <input name="email" placeholder="Email" value={@data[:email]} />
        <% end %>
        
        <%= if @step == 2 do %>
          <h3>Address</h3>
          <input name="address" placeholder="Address" value={@data[:address]} />
          <input name="city" placeholder="City" value={@data[:city]} />
        <% end %>
        
        <%= if @step == 3 do %>
          <h3>Review</h3>
          <p>Name: <%= @data[:name] %></p>
          <p>Email: <%= @data[:email] %></p>
          <p>Address: <%= @data[:address] %></p>
          <p>City: <%= @data[:city] %></p>
        <% end %>
        
        <div>
          <%= if @step > 1 do %>
            <button type="button" phx-click="prev_step">Previous</button>
          <% end %>
          
          <button type="submit">
            <%= if @step == 3 do %>
              Submit
            <% else %>
              Next
            <% end %>
          </button>
        </div>
      </form>
    </div>
    """
  end
  
  def handle_event("next_step", params, socket) do
    step = socket.assigns.step
    data = Map.merge(socket.assigns.data, params)
    
    if step == 3 do
      # Submit form
      {:noreply, assign(socket, data: data, submitted: true)}
    else
      {:noreply, assign(socket, step: step + 1, data: data)}
    end
  end
  
  def handle_event("prev_step", _params, socket) do
    {:noreply, assign(socket, step: socket.assigns.step - 1)}
  end
end
Advanced
83. What is Phoenix with LiveView Modal?

Phoenix LiveView Modal enables creating modal dialogs with content rendering, event handling, and state management.

  • Modal Display: Show/hide modals
  • Content Rendering: Render content in modal
  • Event Handling: Handle modal events
  • State Management: Track modal state
  • Customizable: Custom modal styling
elixir
# Phoenix with LiveView Modal
defmodule MyAppWeb.ModalLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, show_modal: false, modal_content: nil)}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Modal Example</h2>
      <button phx-click="open_modal">Open Modal</button>
      
      <%= if @show_modal do %>
        <div class="modal-overlay" phx-click="close_modal">
          <div class="modal">
            <div class="modal-header">
              <h3>Modal Title</h3>
              <button phx-click="close_modal">×</button>
            </div>
            <div class="modal-body">
              <%= @modal_content %>
            </div>
            <div class="modal-footer">
              <button phx-click="close_modal">Close</button>
              <button phx-click="confirm_modal">Confirm</button>
            </div>
          </div>
        </div>
      <% end %>
    </div>
    """
  end
  
  def handle_event("open_modal", _params, socket) do
    {:noreply, assign(socket, show_modal: true, modal_content: "Modal content here")}
  end
  
  def handle_event("close_modal", _params, socket) do
    {:noreply, assign(socket, show_modal: false)}
  end
  
  def handle_event("confirm_modal", _params, socket) do
    {:noreply, assign(socket, show_modal: false)}
  end
end
Advanced
84. What is Phoenix with LiveView Tabs?

Phoenix LiveView Tabs provides tabbed navigation with content switching and state management for multi-panel interfaces.

  • Tab Navigation: Switch between tabs
  • Content Switching: Show/hide tab content
  • Active Tab: Highlight current tab
  • State Management: Track active tab
  • Dynamic Tabs: Create tabs dynamically
elixir
# Phoenix with LiveView Tabs
defmodule MyAppWeb.TabsLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, active_tab: "tab1")}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Tabs</h2>
      <div class="tabs">
        <button class={if @active_tab == "tab1", do: "active"}
                phx-click="switch_tab" phx-value-tab="tab1">
          Tab 1
        </button>
        <button class={if @active_tab == "tab2", do: "active"}
                phx-click="switch_tab" phx-value-tab="tab2">
          Tab 2
        </button>
        <button class={if @active_tab == "tab3", do: "active"}
                phx-click="switch_tab" phx-value-tab="tab3">
          Tab 3
        </button>
      </div>
      
      <div class="tab-content">
        <%= if @active_tab == "tab1" do %>
          <h3>Tab 1 Content</h3>
          <p>Content for tab 1</p>
        <% end %>
        
        <%= if @active_tab == "tab2" do %>
          <h3>Tab 2 Content</h3>
          <p>Content for tab 2</p>
        <% end %>
        
        <%= if @active_tab == "tab3" do %>
          <h3>Tab 3 Content</h3>
          <p>Content for tab 3</p>
        <% end %>
      </div>
    </div>
    """
  end
  
  def handle_event("switch_tab", %{"tab" => tab}, socket) do
    {:noreply, assign(socket, active_tab: tab)}
  end
end
Advanced
85. What is Phoenix with LiveView Accordion?

Phoenix LiveView Accordion enables creating expandable content sections with toggle functionality and state management.

  • Expandable Sections: Toggle section visibility
  • State Management: Track open/closed state
  • Multiple Open: Allow multiple open sections
  • Toggle Events: Handle section toggle
  • Use Cases: FAQs, menus, collapsible content
elixir
# Phoenix with LiveView Accordion
defmodule MyAppWeb.AccordionLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    items = [
      %{id: 1, title: "Section 1", content: "Content 1", open: false},
      %{id: 2, title: "Section 2", content: "Content 2", open: false},
      %{id: 3, title: "Section 3", content: "Content 3", open: false}
    ]
    {:ok, assign(socket, items: items)}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Accordion</h2>
      <div class="accordion">
        <%= for item <- @items do %>
          <div class="accordion-item">
            <div class="accordion-header" phx-click="toggle" phx-value-id={item.id}>
              <%= item.title %>
            </div>
            <div class="accordion-body" style={if item.open, do: "display: block;", else: "display: none;"}>
              <%= item.content %>
            </div>
          </div>
        <% end %>
      </div>
    </div>
    """
  end
  
  def handle_event("toggle", %{"id" => id}, socket) do
    items = Enum.map(socket.assigns.items, fn item ->
      %{item | open: if item.id == id, do: !item.open, else: false}
    end)
    {:noreply, assign(socket, items: items)}
  end
end
Advanced
87. What is Phoenix with LiveView Tooltip?

Phoenix LiveView Tooltip enables creating tooltips with hover events and dynamic content display.

  • Tooltip Display: Show on hover
  • Content Management: Dynamic tooltip content
  • Positioning: Position tooltips
  • Event Handling: Show/hide on hover
  • Customizable: Custom tooltip styling
elixir
# Phoenix with LiveView Tooltip
defmodule MyAppWeb.TooltipLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, tooltip: nil)}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Tooltip</h2>
      <div phx-hook="Tooltip">
        <button phx-click="show_tooltip">Hover me</button>
        
        <%= if @tooltip do %>
          <div class="tooltip">
            <%= @tooltip %>
          </div>
        <% end %>
      </div>
    </div>
    """
  end
  
  def handle_event("show_tooltip", _params, socket) do
    {:noreply, assign(socket, tooltip: "This is a tooltip!")}
  end
end
Advanced
88. What is Phoenix with LiveView Notification?

Phoenix LiveView Notification provides real-time notification delivery with unread counts and notification history.

  • Real-time Notifications: Push notifications via PubSub
  • Unread Count: Track unread notifications
  • Notification List: Show notification history
  • Mark as Read: Mark notifications as read
  • Toast Notifications: Popup notifications
elixir
# Phoenix with LiveView Notification
defmodule MyAppWeb.NotificationLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    if connected?(socket) do
      Phoenix.PubSub.subscribe(MyApp.PubSub, "notifications")
    end
    {:ok, assign(socket, notifications: [], unread_count: 0)}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Notifications</h2>
      <button phx-click="toggle_notifications">
        Notifications (<%= @unread_count %>)
      </button>
      
      <%= if @show_notifications do %>
        <div class="notification-list">
          <%= for notification <- @notifications do %>
            <div class="notification-item">
              <span class={if notification.read, do: "read", else: "unread"}>
                <%= notification.message %>
              </span>
              <small><%= notification.timestamp %></small>
            </div>
          <% end %>
        </div>
      <% end %>
    </div>
    """
  end
  
  def handle_info({:new_notification, message}, socket) do
    notifications = [%{message: message, read: false, timestamp: DateTime.utc_now()} | socket.assigns.notifications]
    {:noreply, assign(socket, notifications: notifications, unread_count: socket.assigns.unread_count + 1)}
  end
  
  def handle_event("toggle_notifications", _params, socket) do
    {:noreply, assign(socket, show_notifications: !socket.assigns.show_notifications)}
  end
end
Advanced
89. What is Phoenix with LiveView Chat?

Phoenix LiveView Chat provides real-time chat functionality with message history, user typing indicators, and presence tracking.

  • Real-time Messages: Send/receive messages instantly
  • Message History: Persistent message storage
  • Typing Indicators: Show user typing status
  • User Presence: Show online users
  • User Authentication: Authenticated chat
elixir
# Phoenix with LiveView Chat
defmodule MyAppWeb.ChatLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    if connected?(socket) do
      Phoenix.PubSub.subscribe(MyApp.PubSub, "chat")
    end
    {:ok, assign(socket, messages: [], message: "", user: "Guest")}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Chat</h2>
      <div class="chat-messages">
        <%= for msg <- @messages do %>
          <div class={if msg.user == @user, do: "mine", else: "theirs"}>
            <strong><%= msg.user %>:</strong>
            <span><%= msg.message %></span>
            <small><%= msg.timestamp %></small>
          </div>
        <% end %>
      </div>
      
      <form phx-submit="send_message">
        <input type="text" name="message" value={@message} phx-change="update_message" />
        <button type="submit">Send</button>
      </form>
      
      <div>
        <input type="text" value={@user} phx-change="update_user" placeholder="Username" />
      </div>
    </div>
    """
  end
  
  def handle_event("send_message", %{"message" => message}, socket) do
    message = %{
      user: socket.assigns.user,
      message: message,
      timestamp: DateTime.utc_now()
    }
    Phoenix.PubSub.broadcast(MyApp.PubSub, "chat", {:new_message, message})
    {:noreply, assign(socket, message: "")}
  end
  
  def handle_event("update_message", %{"message" => message}, socket) do
    {:noreply, assign(socket, message: message)}
  end
  
  def handle_event("update_user", %{"value" => user}, socket) do
    {:noreply, assign(socket, user: user)}
  end
  
  def handle_info({:new_message, message}, socket) do
    {:noreply, update(socket, :messages, fn msgs -> [message | msgs] end)}
  end
end
Advanced
90. What is Phoenix with LiveView Socket?

Phoenix LiveView Socket provides WebSocket socket communication with connection lifecycle management and event handling.

  • Socket Connection: WebSocket connection
  • Socket Authentication: Authenticate sockets
  • Event Handling: Handle socket events
  • Connection State: Track connection status
  • Use Cases: Real-time features, messaging
elixir
# Phoenix with LiveView Socket
defmodule MyAppWeb.SocketLive do
  use Phoenix.LiveView
  
  def mount(_params, session, socket) do
    socket = socket
    |> assign(:current_user, session["current_user"])
    |> assign(:socket_id, Phoenix.Socket.ID)
    
    if connected?(socket) do
      Phoenix.PubSub.subscribe(MyApp.PubSub, "user:#{socket.assigns.current_user.id}")
    end
    
    {:ok, socket}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Socket</h2>
      <p>User: <%= @current_user.name %></p>
      <p>Socket ID: <%= @socket_id %></p>
      
      <div class="events">
        <h3>Events</h3>
        <ul>
          <%= for event <- @events do %>
            <li><%= event %></li>
          <% end %>
        </ul>
      </div>
    </div>
    """
  end
  
  def handle_info({:user_event, event}, socket) do
    {:noreply, update(socket, :events, fn events -> [event | events] end)}
  end
end
Advanced
91. What is Phoenix with LiveView Session?

Phoenix LiveView Session provides session management with user-specific data persistence across requests.

  • Session Data: Store user-specific data
  • Session Management: Create, read, update, delete
  • User Context: Access session data in LiveView
  • Persistence: Data persists across page reloads
  • Secure: Signed session cookies
elixir
# Phoenix with LiveView Session
defmodule MyAppWeb.SessionLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, count: 0, session_data: %{})}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Session</h2>
      <div>
        <p>Count: <%= @count %></p>
        <button phx-click="increment">Increment</button>
        <button phx-click="decrement">Decrement</button>
        <button phx-click="reset">Reset</button>
      </div>
      
      <div>
        <h3>Session Data</h3>
        <p><%= inspect(@session_data) %></p>
        <button phx-click="save_session">Save Session</button>
        <button phx-click="clear_session">Clear Session</button>
      </div>
    </div>
    """
  end
  
  def handle_event("increment", _params, socket) do
    {:noreply, update(socket, :count, &(&1 + 1))}
  end
  
  def handle_event("decrement", _params, socket) do
    {:noreply, update(socket, :count, &(&1 - 1))}
  end
  
  def handle_event("reset", _params, socket) do
    {:noreply, assign(socket, count: 0)}
  end
  
  def handle_event("save_session", _params, socket) do
    session_data = %{count: socket.assigns.count, timestamp: DateTime.utc_now()}
    {:noreply, assign(socket, session_data: session_data)}
  end
  
  def handle_event("clear_session", _params, socket) do
    {:noreply, assign(socket, session_data: %{})}
  end
end
Advanced
93. What is Phoenix with LiveView Storage?

Phoenix LiveView Storage provides client-side storage with localStorage and sessionStorage integration.

  • localStorage: Persistent client storage
  • sessionStorage: Session-specific storage
  • Set/Get/Delete: Storage operations
  • LiveView Integration: Sync storage with LiveView
  • Use Cases: Form data, user preferences, cache
elixir
# Phoenix with LiveView Storage
defmodule MyAppWeb.StorageLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, data: %{}, key: "", value: "")}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Storage</h2>
      <div>
        <h3>Set Value</h3>
        <form phx-submit="set_value">
          <input type="text" name="key" placeholder="Key" value={@key} phx-change="update_key" />
          <input type="text" name="value" placeholder="Value" value={@value} phx-change="update_value" />
          <button type="submit">Set</button>
        </form>
      </div>
      
      <div>
        <h3>Get Value</h3>
        <form phx-submit="get_value">
          <input type="text" name="key" placeholder="Key" />
          <button type="submit">Get</button>
        </form>
      </div>
      
      <div>
        <h3>All Data</h3>
        <pre><%= inspect(@data) %></pre>
        <button phx-click="clear_all">Clear All</button>
      </div>
    </div>
    """
  end
  
  def handle_event("update_key", %{"key" => key}, socket) do
    {:noreply, assign(socket, key: key)}
  end
  
  def handle_event("update_value", %{"value" => value}, socket) do
    {:noreply, assign(socket, value: value)}
  end
  
  def handle_event("set_value", %{"key" => key, "value" => value}, socket) do
    data = Map.put(socket.assigns.data, key, value)
    {:noreply, assign(socket, data: data, key: "", value: "")}
  end
  
  def handle_event("get_value", %{"key" => key}, socket) do
    value = Map.get(socket.assigns.data, key, "Not found")
    {:noreply, put_flash(socket, :info, "Value: #{value}")}
  end
  
  def handle_event("clear_all", _params, socket) do
    {:noreply, assign(socket, data: %{})}
  end
end
Advanced
94. What is Phoenix with LiveView LocalStorage?

Phoenix LiveView LocalStorage enables persistent client-side storage with data persistence across browser sessions.

  • Persistent Storage: Data survives page reloads
  • Data Operations: Set, get, remove items
  • JSON Support: Store complex data structures
  • LiveView Sync: Sync with LiveView state
  • Use Cases: Shopping carts, saved preferences
elixir
# Phoenix with LiveView LocalStorage
defmodule MyAppWeb.LocalStorageLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, items: [], new_item: "")}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Local Storage</h2>
      <div>
        <form phx-submit="add_item">
          <input type="text" name="item" value={@new_item} phx-change="update_item" />
          <button type="submit">Add</button>
        </form>
      </div>
      
      <ul>
        <%= for item <- @items do %>
          <li>
            <%= item %>
            <button phx-click="remove_item" phx-value-item={item}>×</button>
          </li>
        <% end %>
      </ul>
      
      <button phx-click="clear_items">Clear All</button>
    </div>
    """
  end
  
  def handle_event("update_item", %{"item" => item}, socket) do
    {:noreply, assign(socket, new_item: item)}
  end
  
  def handle_event("add_item", %{"item" => item}, socket) do
    {:noreply, update(socket, :items, fn items -> items ++ [item] end)}
  end
  
  def handle_event("remove_item", %{"item" => item}, socket) do
    {:noreply, update(socket, :items, fn items -> List.delete(items, item) end)}
  end
  
  def handle_event("clear_items", _params, socket) do
    {:noreply, assign(socket, items: [])}
  end
end
Advanced
95. What is Phoenix with LiveView History?

Phoenix LiveView History provides browser history management with undo/redo functionality and state tracking.

  • History Tracking: Track state changes
  • Undo/Redo: Navigate through history
  • State Snapshots: Save state snapshots
  • Browser History: Push/pop browser history
  • Use Cases: Form undo, navigation history
elixir
# Phoenix with LiveView History
defmodule MyAppWeb.HistoryLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, history: [], input: "", position: 0)}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>History</h2>
      <div>
        <form phx-submit="add_history">
          <input type="text" name="input" value={@input} phx-change="update_input" />
          <button type="submit">Add</button>
        </form>
      </div>
      
      <div>
        <button phx-click="undo">Undo</button>
        <button phx-click="redo">Redo</button>
        <button phx-click="clear_history">Clear</button>
      </div>
      
      <ul>
        <%= for item <- Enum.reverse(@history) do %>
          <li><%= item %></li>
        <% end %>
      </ul>
    </div>
    """
  end
  
  def handle_event("update_input", %{"input" => input}, socket) do
    {:noreply, assign(socket, input: input)}
  end
  
  def handle_event("add_history", %{"input" => input}, socket) do
    history = [input | socket.assigns.history]
    position = length(history)
    {:noreply, assign(socket, history: history, input: "", position: position)}
  end
  
  def handle_event("undo", _params, socket) do
    if socket.assigns.position > 0 do
      {:noreply, update(socket, :position, &(&1 - 1))}
    else
      {:noreply, socket}
    end
  end
  
  def handle_event("redo", _params, socket) do
    if socket.assigns.position < length(socket.assigns.history) do
      {:noreply, update(socket, :position, &(&1 + 1))}
    else
      {:noreply, socket}
    end
  end
  
  def handle_event("clear_history", _params, socket) do
    {:noreply, assign(socket, history: [], position: 0)}
  end
end
Advanced
96. What is Phoenix with LiveView Timer?

Phoenix LiveView Timer provides countdown and stopwatch functionality with real-time updates and state management.

  • Countdown Timer: Count down from set time
  • Stopwatch: Measure elapsed time
  • Start/Stop/Reset: Timer controls
  • Real-time Updates: Update every second
  • Use Cases: Countdowns, elapsed time tracking
elixir
# Phoenix with LiveView Timer
defmodule MyAppWeb.TimerLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    if connected?(socket) do
      Process.send_after(self(), :tick, 1000)
    end
    {:ok, assign(socket, seconds: 0, running: true)}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Timer</h2>
      <div class="timer">
        <h1><%= format_time(@seconds) %></h1>
        <div>
          <button phx-click="start">Start</button>
          <button phx-click="pause">Pause</button>
          <button phx-click="reset">Reset</button>
        </div>
      </div>
    </div>
    """
  end
  
  def handle_info(:tick, socket) do
    if socket.assigns.running do
      Process.send_after(self(), :tick, 1000)
      {:noreply, update(socket, :seconds, &(&1 + 1))}
    else
      Process.send_after(self(), :tick, 1000)
      {:noreply, socket}
    end
  end
  
  def handle_event("start", _params, socket) do
    {:noreply, assign(socket, running: true)}
  end
  
  def handle_event("pause", _params, socket) do
    {:noreply, assign(socket, running: false)}
  end
  
  def handle_event("reset", _params, socket) do
    {:noreply, assign(socket, seconds: 0, running: false)}
  end
  
  defp format_time(seconds) do
    hours = div(seconds, 3600)
    minutes = div(rem(seconds, 3600), 60)
    seconds = rem(seconds, 60)
    "#{pad(hours)}:#{pad(minutes)}:#{pad(seconds)}"
  end
  
  defp pad(n) when n < 10, do: "0#{n}"
  defp pad(n), do: to_string(n)
end
Advanced
97. What is Phoenix with LiveView Canvas?

Phoenix LiveView Canvas enables interactive canvas drawing with real-time updates and user interactions.

  • Canvas Drawing: Draw on HTML5 canvas
  • Real-time Updates: Sync canvas state
  • Mouse Events: Drawing with mouse/pointer
  • Color/Size Controls: Customize drawing tools
  • Use Cases: Whiteboard, drawing apps, signatures
elixir
# Phoenix with LiveView Canvas
defmodule MyAppWeb.CanvasLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, points: [], color: "#000000", size: 5)}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Canvas</h2>
      <div id="canvas" phx-hook="Canvas" data-points={inspect(@points)}>
        <canvas id="canvas-element" width="400" height="400"></canvas>
      </div>
      
      <div>
        <label>Color:</label>
        <input type="color" name="color" value={@color} phx-change="update_color" />
        <label>Size:</label>
        <input type="range" name="size" min="1" max="20" value={@size} phx-change="update_size" />
        <button phx-click="clear_canvas">Clear</button>
      </div>
    </div>
    """
  end
  
  def handle_event("update_color", %{"color" => color}, socket) do
    {:noreply, assign(socket, color: color)}
  end
  
  def handle_event("update_size", %{"size" => size}, socket) do
    {:noreply, assign(socket, size: String.to_integer(size))}
  end
  
  def handle_event("clear_canvas", _params, socket) do
    {:noreply, assign(socket, points: [])}
  end
  
  def handle_event("draw", %{"points" => points}, socket) do
    {:noreply, assign(socket, points: points)}
  end
end
Advanced
98. What is Phoenix with LiveView Animation?

Phoenix LiveView Animation provides CSS animations and transitions with state-driven animation triggers and controls.

  • CSS Animations: Animate with CSS
  • State-driven: Trigger on state change
  • Transitions: Smooth transitions
  • Animation Controls: Start/Stop/Reset
  • Use Cases: UI animations, interactive elements
elixir
# Phoenix with LiveView Animation
defmodule MyAppWeb.AnimationLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    if connected?(socket) do
      Process.send_after(self(), :animate, 50)
    end
    {:ok, assign(socket, position: 0, direction: 1)}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Animation</h2>
      <div id="animation-container" style="position: relative; height: 200px; overflow: hidden;">
        <div id="animated-element" style="
          position: absolute;
          left: <%= @position %>px;
          width: 50px;
          height: 50px;
          background: blue;
          border-radius: 50%;
          transition: left 0.05s;
        ">
        </div>
      </div>
      
      <div>
        <button phx-click="start_animation">Start</button>
        <button phx-click="stop_animation">Stop</button>
        <button phx-click="reset_animation">Reset</button>
      </div>
    </div>
    """
  end
  
  def handle_info(:animate, socket) do
    if socket.assigns.animation_running do
      position = socket.assigns.position + socket.assigns.direction * 5
      direction = if position >= 350 or position <= 0, do: -socket.assigns.direction, else: socket.assigns.direction
      
      Process.send_after(self(), :animate, 50)
      {:noreply, assign(socket, position: position, direction: direction)}
    else
      Process.send_after(self(), :animate, 50)
      {:noreply, socket}
    end
  end
  
  def handle_event("start_animation", _params, socket) do
    {:noreply, assign(socket, animation_running: true)}
  end
  
  def handle_event("stop_animation", _params, socket) do
    {:noreply, assign(socket, animation_running: false)}
  end
  
  def handle_event("reset_animation", _params, socket) do
    {:noreply, assign(socket, position: 0, animation_running: false)}
  end
end
Advanced
99. What is Phoenix with LiveView Audio?

Phoenix LiveView Audio provides audio player controls with playback, volume, and progress tracking.

  • Audio Playback: Play/pause audio
  • Volume Control: Adjust volume
  • Progress Tracking: Track playback progress
  • Audio Controls: Play, pause, seek, volume
  • Use Cases: Music player, podcast player, audio content
elixir
# Phoenix with LiveView Audio
defmodule MyAppWeb.AudioLive do
  use Phoenix.LiveView
  
  def mount(_params, _session, socket) do
    {:ok, assign(socket, playing: false, volume: 50, current_time: 0)}
  end
  
  def render(assigns) do
    ~H"""
    <div>
      <h2>Audio Player</h2>
      <div class="audio-player">
        <button phx-click="toggle_play">
          <%= if @playing, do: "Pause", else: "Play" %>
        </button>
        
        <input type="range" min="0" max="100" value={@volume} phx-change="set_volume" />
        <span><%= @volume %>%</span>
        
        <div class="progress">
          <input type="range" min="0" max="100" value={@current_time} phx-change="seek" />
          <span><%= format_time(@current_time) %></span>
        </div>
      </div>
    </div>
    """
  end
  
  def handle_event("toggle_play", _params, socket) do
    {:noreply, assign(socket, playing: !socket.assigns.playing)}
  end
  
  def handle_event("set_volume", %{"value" => volume}, socket) do
    {:noreply, assign(socket, volume: String.to_integer(volume))}
  end
  
  def handle_event("seek", %{"value" => time}, socket) do
    {:noreply, assign(socket, current_time: String.to_integer(time))}
  end
  
  defp format_time(seconds) do
    minutes = div(seconds, 60)
    seconds = rem(seconds, 60)
    "#{pad(minutes)}:#{pad(seconds)}"
  end
  
  defp pad(n) when n < 10, do: "0#{n}"
  defp pad(n), do: to_string(n)
end
Advanced
100. How to build a Complete Library Management System in Elixir?

A Complete Library Management System in Elixir demonstrates OTP principles, Ecto for database, and Phoenix for web interface with real-time updates.

  • Book Management: Add, search, track availability
  • Member Management: Registration and borrowing
  • Borrow/Return: Transaction processing with Ecto
  • Real-time Updates: Phoenix Channels for live updates
  • Reports: Generate library statistics and reports
elixir
# Complete Library Management System in Elixir
defmodule MyApp.Library do
  alias MyApp.{Repo, Book, Member, Borrowing}
  
  def add_book(attrs) do
    %Book{}
    |> Book.changeset(attrs)
    |> Repo.insert()
  end
  
  def register_member(attrs) do
    %Member{}
    |> Member.changeset(attrs)
    |> Repo.insert()
  end
  
  def borrow_book(member_id, book_id) do
    member = Repo.get(Member, member_id)
    book = Repo.get(Book, book_id)
    
    if book.available_copies > 0 do
      Repo.transaction(fn ->
        # Decrement available copies
        book
        |> Book.available_changeset(%{available_copies: book.available_copies - 1})
        |> Repo.update!()
        
        # Create borrowing record
        %Borrowing{}
        |> Borrowing.changeset(%{member_id: member_id, book_id: book_id})
        |> Repo.insert!()
      end)
      {:ok, "Book borrowed successfully"}
    else
      {:error, "No copies available"}
    end
  end
  
  def return_book(member_id, book_id) do
    borrowing = Repo.get_by(Borrowing, member_id: member_id, book_id: book_id, returned_at: nil)
    
    if borrowing do
      Repo.transaction(fn ->
        # Update borrowing record
        borrowing
        |> Borrowing.return_changeset()
        |> Repo.update!()
        
        # Increment available copies
        book = Repo.get(Book, book_id)
        book
        |> Book.available_changeset(%{available_copies: book.available_copies + 1})
        |> Repo.update!()
      end)
      {:ok, "Book returned successfully"}
    else
      {:error, "No active borrowing record found"}
    end
  end
  
  def search_books(query) do
    from b in Book,
      where: ilike(b.title, ^"%#{query}%") or ilike(b.author, ^"%#{query}%"),
      order_by: b.title
    |> Repo.all()
  end
  
  def get_member_borrowings(member_id) do
    query = from b in Borrowing,
      where: b.member_id == ^member_id,
      preload: [:book],
      order_by: [desc: b.borrowed_at]
    Repo.all(query)
  end
  
  def get_overdue_books do
    query = from b in Borrowing,
      where: b.returned_at == nil and b.due_date < ^Date.utc_today(),
      preload: [:member, :book]
    Repo.all(query)
  end
  
  def get_available_books do
    query = from b in Book,
      where: b.available_copies > 0,
      order_by: b.title
    Repo.all(query)
  end
  
  def get_library_stats do
    %{
      total_books: Repo.aggregate(Book, :count, :id),
      total_members: Repo.aggregate(Member, :count, :id),
      active_borrowings: Repo.aggregate(Borrowing, :count, :id, where: [returned_at: nil]),
      overdue_books: length(get_overdue_books())
    }
  end
end

# Phoenix Controller
defmodule MyAppWeb.LibraryController do
  use MyAppWeb, :controller
  
  def index(conn, _params) do
    books = MyApp.Library.get_available_books()
    stats = MyApp.Library.get_library_stats()
    
    render(conn, "index.html", books: books, stats: stats)
  end
  
  def search(conn, %{"q" => query}) do
    results = MyApp.Library.search_books(query)
    json(conn, results)
  end
  
  def borrow(conn, %{"member_id" => member_id, "book_id" => book_id}) do
    case MyApp.Library.borrow_book(member_id, book_id) do
      {:ok, message} ->
        json(conn, %{success: true, message: message})
      {:error, error} ->
        conn |> put_status(400) |> json(%{success: false, error: error})
    end
  end
  
  def return(conn, %{"member_id" => member_id, "book_id" => book_id}) do
    case MyApp.Library.return_book(member_id, book_id) do
      {:ok, message} ->
        json(conn, %{success: true, message: message})
      {:error, error} ->
        conn |> put_status(400) |> json(%{success: false, error: error})
    end
  end
end