View Source Middleware

TL;DR: adapter(middleware3(middleware2(middleware1(env, next, options))))

Middleware in Tesla extends the request/response pipeline. Requests pass through a stack of middleware before reaching the adapter, allowing modifications to both requests and responses.

Middleware can be a module implementing Tesla.Middleware behaviour or a function matching Tesla.Middleware.call/3. There is no distinction between request and response middleware; it's about when you execute Tesla.run/2.

The middleware stack is processed by calling Tesla.run/2 until it reaches the adapter.

Writing Middleware

Example of a custom middleware module:

defmodule Tesla.Middleware.MyCustomMiddleware do
  @behaviour Tesla.Middleware

  @impl Tesla.Middleware
  def call(env, next, options) do
    # Actions before calling the next middleware
    # ...
    Tesla.run(env, next)
    # Actions after calling the next middleware
    # ...
  end
end

A request logger middleware example:

defmodule MyApp.Tesla.Middleware.Logger do
  require Logger

  @behaviour Tesla.Middleware

  def call(env, next, _) do
    Logger.info("Request: #{inspect(env)}")
    case Tesla.run(env, next) do
      {:ok, env} ->
        Logger.info("Response: #{inspect(env)}")
        {:ok, env}

      {:error, reason} ->
        Logger.error("Error: #{inspect(reason)}")
        {:error, reason}
    end
  end
end

Production-Ready Middleware Pipeline Example

In a production application, you might combine built-in and custom middleware. Here's an example pipeline:

defmodule MyApp.ServiceName do
  defp middleware do
    base_url = "..."
    token = "..."

    [
      # Preserve the original request, should be the first middleware in the
      # pipeline to preserve the original request.
      Tesla.Middleware.KeepRequest,
      # Preserve original path parameters, should be after KeepRequest
      # to preserve the original path parameters tokens
      Tesla.Middleware.PathParams,
      # Set the base URL
      {Tesla.Middleware.BaseUrl, base_url},
      # Add headers
      {Tesla.Middleware.Headers, [{"user-agent", "MyApp/1.0"}]},
      # Add authorization
      {Tesla.Middleware.BearerAuth, [token: token]},
      # Process the body (encoding, compression)
      # Use string keys for untrusted input
      Tesla.Middleware.JSON,
      # Compress the request and response
      #Tesla.Middleware.Compression,

      # ------------------------------------------------------------------------
      # Keep the telemetry and logging as close as possible to the actual request
      # being made.
      # Log requests and responses
      Tesla.Middleware.Logger,
      # Telemetry for timing
      {Tesla.Middleware.Telemetry},
      # Optional OpenTelemetry middleware
      # Tesla.Middleware.OpenTelemetry
    ]
  end
end

See built-in middlewares for more examples.