EctoMiddleware.Repo behaviour (ecto_middleware v2.1.0)
View SourceEnables 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
endDefining 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! - Bulk:
:insert_all,:update_all,:delete_all(opt-in, see below)
Bulk Operations
insert_all/3, update_all/3, and delete_all/2 are intercepted too, but middleware
must opt in to run on them. This avoids passing a schema/source or queryable to
middleware written for single-record changesets.
Opt a middleware in with the bulk_operations: true option:
defmodule AuditBulk do
use EctoMiddleware, bulk_operations: true
def process_before(resource, %{action: action}) when is_bulk_action(resource, action) do
{:cont, resource}
end
def process_before(changeset, _resolution), do: {:cont, changeset}
endMiddleware that do not opt in are dropped from the chain for bulk actions, even when
your middleware/2 returns them. Before callbacks receive the first argument to the Repo
operation: the schema/source for insert_all, or an Ecto.Queryable for update_all
and delete_all. The full argument list, including rows, updates, and options, is
available in resolution.args. After callbacks receive {count, records_or_nil}.
The Repo's middleware/2 callback runs before this filter to choose the middleware
list, even if no middleware have opted in. If your callback assumes write resources are
structs or changesets, add a bulk clause before those clauses:
def middleware(action, resource) when is_bulk_action(resource, action), do: []Return bulk-aware middleware instead of [] to enable middleware for these calls.
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
@type action() ::
:all
| :delete!
| :delete
| :delete_all
| :get!
| :get
| :get_by!
| :get_by
| :insert!
| :insert
| :insert_all
| :insert_or_update!
| :insert_or_update
| :one!
| :one
| :reload!
| :reload
| :preload
| :update!
| :update
| :update_all
@type middleware() :: module()
@type resource() :: %Ecto.Queryable{} | %Ecto.Changeset{} | %{__meta__: Ecto.Schema.Metadata} | {%Ecto.Queryable{}, Keyword.t()}
Callbacks
@callback middleware(action :: action(), resource :: resource()) :: [middleware()]
Functions
Enables the ability for a given Ecto.Repo to define and execute middleware.
@spec middleware(repo :: module(), action(), resource()) :: [middleware()]
Returns the configured middleware for the given repo.