EctoMiddleware.Engine (ecto_middleware v2.0.0)
View SourceInternal engine for validating and executing middleware chains.
See EctoMiddleware for information on writing middleware. This module is used
internally by EctoMiddleware.Repo to execute middleware chains.
Silencing Deprecation Warnings
During migration from v1 to v2, you may want to silence deprecation warnings. This can be done via application configuration:
# In config/config.exs
config :ecto_middleware, :silence_deprecation_warnings, trueThis will suppress all deprecation warnings from EctoMiddleware. Note that this
should only be used temporarily during migration - the deprecated APIs will be
removed in v3.0.
Middleware Execution
Middleware are executed in a chain. You can think of this as a matroshka doll, where each middleware wraps the next one in the chain.
Each middleware is expected to call yield/2 to continue execution to the next middleware
in the chain, but may also choose to halt execution by not calling yield/2 and returning
a value directly.
For example, given the following middleware chain:
[LoggerMiddleware, AuthMiddleware, VirtualFieldMiddleware]Execution proceeds as follows:
c:Ecto.Repo.insert/2is called.EctoMiddleware.process/2is invoked onLoggerMiddleware.- It logs "Before insert".
- It calls
yield/2to continue execution.
EctoMiddleware.process/2is invoked onAuthMiddleware.- It checks authorization.
- It calls
yield/2to continue execution.
EctoMiddleware.process/2is invoked onVirtualFieldMiddleware.- It immediately calls
yield/2to continue execution.
- It immediately calls
- There aren't any more middleware to execute, so the super function (the actual
c:Ecto.Repo.insert/2call) is invoked.- The database insert occurs, returning
{:ok, user}.
- The database insert occurs, returning
- Control returns to
VirtualFieldMiddleware.- It resolves some virtual fields on the
userstruct. - It returns the user struct w/ virtual fields up the chain.
- It resolves some virtual fields on the
- Control returns to
AuthMiddleware.- It doesn't do anything further, so it returns the user struct w/ virtual fields up the chain.
- Control returns to
LoggerMiddleware.process/2.- It logs "After insert: {:ok, user w/ virtual fields}".
- It returns the user struct up the chain.
- The caller of
c:Ecto.Repo.insert/2sees the function result as{:ok, user_with_virtual_fields}.
For simplicity, middleware authors can either implement the EctoMiddleware.process_before/2 or
EctoMiddleware.process_after/2 callbacks to only operate in the "before" or "after" phases.
Alternatively, you can implement EctoMiddleware.process/2 for full control, but you must
call yield/2 at the appropriate time. Not calling yield/2 will halt execution at that
middleware.
Important: yield/2 returns {result, updated_resolution}. You must destructure this
tuple when calling yield.
See the EctoMiddleware docs for more information.
Backwards Compatibility
Prior versions of EctoMiddleware (v1.x) had a different middleware contract and execution engine.
In those versions of the library, all middleware were expected to implement middleware/2 in a
middleware module (note: this was not a behaviour callback). This function was expected to take some
"resource" and return that "resource" transformed by the middleware.
Middleware executed exactly once, either before or after the super function, depending on their
position relative to EctoMiddleware.Super in the middleware list.
An example of this follows:
defmodule Repo do
use EctoMiddleware
@impl EctoMiddleware
def middleware(:insert, _resource) do
[BeforeMiddleware, EctoMiddleware.Super, AfterMiddleware]
end
end
defmodule BeforeMiddleware do
def middleware(resource, _resolution) do
transform_before(resource)
end
end
defmodule AfterMiddleware do
def middleware(resource, _resolution) do
transform_after(resource)
end
endThe current version of EctoMiddleware (v2.x) supports v1 middleware for backwards compatibility.
This is implemented by dynamically replacing any v1 middleware with a v2 middleware that wraps
the v1 middleware and calls its middleware/2 function either before or after the super function
is executed.
This functionality is temporary and will be removed in v3.0, at which point all middleware must implement the v2 middleware contract.
Using v1 middleware in this way will emit a deprecation warning. We strongly recommend updating
any existing v1 middleware to implement the v2 middleware contract to avoid this warning and
ensure compatibility with future versions of EctoMiddleware.
Summary
Functions
Validates each middleware implements the required callbacks.
Yields execution to the next middleware in the chain.
Functions
Validates each middleware implements the required callbacks.
This function is executed prior to execution of any given middleware chain to ensure that the specified middleware pipeline is valid.
Note: This function also wraps any v1 middleware to be compatible with the v2 middleware execution engine. This functionality is temporary and will be removed in v3 when v1 middleware is no longer supported.
@spec yield(resource :: term(), resolution :: EctoMiddleware.Resolution.t()) :: {term(), EctoMiddleware.Resolution.t()}
Yields execution to the next middleware in the chain.
Returns a tuple of {result, updated_resolution} where the resolution may have been
updated during middleware execution (e.g., for V1 compatibility fields like before_output).
- If the result of a middleware's
process/2function is{:halt, value}, execution is halted. - If there are no more middleware to execute, the
superfunction is invoked. - Otherwise, execution continues to the next middleware in the chain.