AshCommanded.Commanded.Middleware.CommandMiddleware behaviour (AshCommanded v0.1.0)

View Source

Protocol defining the behavior for command middleware in AshCommanded.

Command middleware allow you to intercept, modify, or enhance commands before they're processed. This is useful for cross-cutting concerns like:

  • Validation
  • Logging
  • Authorization
  • Rate limiting
  • Auditing
  • Parameter transformation
  • Error handling

Middleware is applied in the order it's defined, with each middleware able to:

  1. Modify the command
  2. Pass it to the next middleware
  3. Short-circuit the chain by returning an error

Implementing Middleware

To create custom middleware, implement this protocol:

defmodule MyApp.MyMiddleware do
  @behaviour AshCommanded.Commanded.Middleware.CommandMiddleware
  
  @impl true
  def before_dispatch(%{} = command, context, next) do
    # Modify command or context here
    modified_command = update_in(command.metadata, &Map.put(&1, :traced, true))
    modified_context = Map.put(context, :middleware_timestamp, DateTime.utc_now())
    
    # Call the next middleware in the chain
    next.(modified_command, modified_context)
  end
  
  @impl true
  def after_dispatch({:ok, result} = success, _command, _context) do
    # Handle successful result
    success
  end
  
  def after_dispatch({:error, _reason} = error, _command, _context) do
    # Handle errors
    error
  end
end

Using Middleware

Middleware can be specified globally for all commands, per resource, or per command:

# Global middleware in application config
config :ash_commanded, :global_middleware, [
  MyApp.LoggingMiddleware,
  MyApp.AuditingMiddleware
]

# Resource-level middleware
defmodule MyApp.User do
  use Ash.Resource,
    extensions: [AshCommanded.Commanded.Dsl]
    
  commanded do
    middleware [
      MyApp.AuthorizationMiddleware,
      MyApp.ValidationMiddleware
    ]
    
    commands do
      # Command-specific middleware
      command :register_user do
        fields [:id, :email, :name]
        middleware [MyApp.RegistrationMiddleware]
      end
    end
  end
end

Summary

Callbacks

Called after a command is dispatched.

Called before a command is dispatched.

Callbacks

after_dispatch(result, command, context)

@callback after_dispatch(result :: any(), command :: struct(), context :: map()) ::
  {:ok, any()} | {:error, any()}

Called after a command is dispatched.

Parameters

  • result - The result of the command handler
  • command - The original command that was dispatched
  • context - The context for this command dispatch

Returns

The potentially modified result.

before_dispatch(command, context, next)

@callback before_dispatch(command :: struct(), context :: map(), next :: function()) ::
  {:ok, any()} | {:error, any()}

Called before a command is dispatched.

Parameters

  • command - The command to be dispatched
  • context - The context for this command dispatch
  • next - Function to call the next middleware in the chain

Returns

The result of the last middleware or command handler in the chain.