EctoMiddleware.Repo behaviour (ecto_middleware v2.0.0)

View Source

Enables middleware support for Ecto.Repo modules.

Add use EctoMiddleware.Repo to your Repo to enable middleware pipelines for database operations.

Setup

defmodule MyApp.Repo do
  use Ecto.Repo, otp_app: :my_app
  use EctoMiddleware.Repo

  @impl EctoMiddleware.Repo
  def middleware(action, resource) when is_insert(action, resource) do
    [NormalizeEmail, HashPassword, AuditLog]
  end

  def middleware(action, resource) when is_delete(action, resource) do
    [SoftDelete, AuditLog]
  end

  def middleware(_action, _resource) do
    [AuditLog]
  end
end

Defining Middleware

Implement the middleware/2 callback to specify which middleware run for each operation.

Pattern Matching on Actions

The first argument is the action atom (function name without arity):

@impl EctoMiddleware.Repo
def middleware(:insert, _), do: [ValidateEmail]

def middleware(:get, _), do: [EnrichData]

def middleware(:delete, _), do: [SoftDelete]

Available actions:

  • Read: :get, :get!, :get_by, :get_by!, :one, :one!, :all, :reload, :reload!, :preload
  • Write: :insert, :insert!, :update, :update!, :delete, :delete!, :insert_or_update, :insert_or_update!

Pattern Matching on Resources

The second argument is the resource being operated on:

# Specific schema
def middleware(:insert, %User{}), do: [NormalizeEmail, HashPassword]

# Multiple schemas with same middleware
def middleware(:insert, %{__struct__: schema})
  when schema in [User, Admin], do: [AuditLog]

# Changesets
def middleware(:update, %Ecto.Changeset{data: %User{}}), do: [CheckOwnership]

Using Guards

You can use EctoMiddleware.Utils guards in your middleware/2 definitions:

@impl EctoMiddleware.Repo
def middleware(action, resource) when is_insert(action, resource) do
  [SetCreatedAt, AuditLog]
end

def middleware(action, resource) when is_update(action, resource) do
  [SetUpdatedAt, AuditLog]
end

def middleware(_action, _resource), do: []

See EctoMiddleware.Utils for available guards.

Default Middleware

Always provide a catch-all clause:

def middleware(_action, _resource), do: []

Middleware Execution

Middleware execute in the order specified. If any middleware returns {:halt, value}, execution stops immediately.

Summary

Functions

Enables the ability for a given Ecto.Repo to define and execute middleware.

Returns the configured middleware for the given repo.

Types

action()

@type action() ::
  :all
  | :delete!
  | :delete
  | :get!
  | :get
  | :get_by!
  | :get_by
  | :insert!
  | :insert
  | :insert_or_update!
  | :insert_or_update
  | :one!
  | :one
  | :reload!
  | :reload
  | :preload
  | :update!
  | :update

middleware()

@type middleware() :: module()

resource()

@type resource() ::
  %Ecto.Queryable{}
  | %Ecto.Changeset{}
  | %{__meta__: Ecto.Schema.Metadata}
  | {%Ecto.Queryable{}, Keyword.t()}

Callbacks

middleware(action, resource)

@callback middleware(action :: action(), resource :: resource()) :: [middleware()]

Functions

__using__(opts)

(macro)

Enables the ability for a given Ecto.Repo to define and execute middleware.

middleware(repo, action, resource)

@spec middleware(repo :: module(), action(), resource()) :: [middleware()]

Returns the configured middleware for the given repo.