Skip to content
Nicolás Garnil edited this page Mar 23, 2018 · 18 revisions

Usage examples

Pass Phoenix x-request-id header to upstream resources (by @timbuchwaldt)

defmodule MyAPI do
  use Tesla

  plug Tesla.Middleware.BaseUrl, "http://127.0.0.1:4000"

  def foo(client, login) do
    get(client, "/foo")
  end

  def client(%{resp_headers: resp_headers}) do
    {"x-request-id", id} = resp_headers
    |> List.keyfind("x-request-id", 0)

    Tesla.build_client [
      {PhoenixRequestIDMiddleware, id}
    ]
  end
end

defmodule PhoenixRequestIDMiddleware do
  def call(env, next, opts) do
    env
    |> add_request_id(opts)
    |> IO.inspect
    |> Tesla.run(next)
  end

  def add_request_id(env, id) do
    Map.update!(env, :headers, &Map.merge(&1, %{"x-request-id" => id}))
  end
end

Custom middleware for Appsignal

defmodule Tesla.Middleware.Appsignal do
  import Appsignal.Instrumentation.Helpers, only: [instrument: 3]

  def call(env, next, _opts) do
    verb = env.method |> to_string |> String.upcase
    instrument "net.http", "#{verb} #{env.url}", fn ->
      Tesla.run(env, next)
    end
  end
end

Request url template + params via opts

Sometimes you may need to do something with the url template (like sending metrics) before the path is replaced with the path parameters.

Usage:

defmodule MyAPI do
  use Tesla

  plug Tesla.Middleware.BaseUrl, "http://example.com"

  plug MyAPI.Middleware.UrlBuilder

  def get_user(user_id) do
    get("/user/:id", opts: [params: [id: user_id]])
  end
end

Middleware:

defmodule MyAPI.Middleware.UrlBuilder do
  @moduledoc """
  Builds the request url
  """

  @behaviour Tesla.Middleware

  @impl Tesla.Middleware
  def call(env, next, _) do
    url = build_url(env.url, env.opts[:params])
    opts = Keyword.merge(env.opts, url: env.url)

    Tesla.run(%{env | url: url, opts: opts}, next)
  end

  defp build_url(url, nil), do: url

  defp build_url(url, params) do
    Enum.reduce(params, url, fn {k, v}, u ->
      String.replace(u, ":#{k}", to_string(v))
    end)
  end
end

Decode As

Decode response as a struct.

Usage:

defmodule MyAPI do
  use Tesla

  plug Tesla.Middleware.BaseUrl, "http://example.com"

  plug MyAPI.Middleware.DecodeJsonAs

  def get_user(user_id) do
    get("/user/" <> user_id, opts: [decode_as: %User{}])
  end
end

Middleware:

defmodule MyApp.Middleware.DecodeJsonAs do
  @moduledoc """
  Decodes JSON response conveniently
  """

  alias Tesla.Env

  @behaviour Tesla.Middleware

  @impl Tesla.Middleware
  def call(env, next, _) do
    decode(Tesla.run(env, next), env.opts[:decode_as])
  end

  defp decode({:ok, %Env{status: status, body: body}}, as) when status in 200..299 do
    case body do
      "" ->
        {:ok, nil}

      body ->
        Poison.decode(body, as: as)
    end
  end

  defp decode({:ok, %Env{body: ""} = env}, _), do: {:error, %Env{env | body: %{}}}

  defp decode({:ok, %Env{body: body} = env}, _) do
    with {:ok, body} <- Poison.decode(body) do
      {:error, %Env{env | body: body}}
    end
  end

  defp decode(error, _), do: error
end