# 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 `c: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:

```elixir
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:

```elixir
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:

```elixir
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,

      # 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,

      # Optional OpenTelemetry middleware. Be careful adding `Tesla.Middleware.PathParams`
      # before this middleware since you want to have low cardinality attribute
      # values, therefore, you want the URL template.
      # Tesla.Middleware.OpenTelemetry

      # Keep the telemetry and logging as close as possible to the actual request
      # being made.
      # Log requests and responses
      Tesla.Middleware.Logger,
      # Telemetry of the Request
      Tesla.Middleware.Telemetry,

      # Replaces the Path Params, you may want keep it at the end when telemetry
      # packages prefer to work with the URL template.
      Tesla.Middleware.PathParams,
    ]
  end
end
```

See [built-in middlewares](https://github.com/elixir-tesla/tesla/tree/master/lib/tesla/middleware)
for more examples.
