# `Tesla`
[🔗](https://github.com/elixir-tesla/tesla/blob/v1.17.0/lib/tesla.ex#L1)

A HTTP toolkit for building API clients using middlewares.

## Building API client

Use `Tesla.client/2` to build a client with the given middleware and adapter.

### Examples

```elixir
defmodule ExampleApi do
  def client do
    Tesla.client([
      {Tesla.Middleware.BaseUrl, "http://api.example.com"},
      Tesla.Middleware.JSON
    ])
  end

  def fetch_data(client) do
    Tesla.get(client, "/data")
  end
end
```

Now you can use `ExampleApi.client/0` to make requests to the API.

```elixir
client = ExampleApi.client()
ExampleApi.fetch_data(client)
```

## Direct usage

It is also possible to do request directly with `Tesla` module.

```elixir
Tesla.get("https://example.com")
```

## Default adapter

By default `Tesla` is using `Tesla.Adapter.Httpc`, because `:httpc` is
included in Erlang/OTP and does not require installation of any additional
dependency. It can be changed globally with config:

```elixir
config :tesla, :adapter, Tesla.Adapter.Mint
```

## Assigns and Private

`Tesla.Env` has two map fields for storing additional data: `:assigns` and `:private`.

`:assigns` is a place for user data. It can be used to carry application-specific metadata
through the middleware pipeline.

`:private` is a map reserved for libraries and middleware to use. The keys must be atoms.
Prefix the keys with the name of your project to avoid any future conflicts. The `tesla_`
prefix is reserved for Tesla.

# `encoding_strategy`

```elixir
@type encoding_strategy() :: :rfc3986 | :www_form
```

# `option`

```elixir
@type option() ::
  {:method, Tesla.Env.method()}
  | {:url, Tesla.Env.url()}
  | {:query, Tesla.Env.query()}
  | {:headers, Tesla.Env.headers()}
  | {:body, Tesla.Env.body()}
  | {:opts, Tesla.Env.opts()}
```

Options that may be passed to a request function. See `request/2` for detailed descriptions.

# `build_adapter`

> This function is deprecated. Use client/1 or client/2 instead.

# `build_client`

> This function is deprecated. Use client/1 or client/2 instead.

# `build_url`

```elixir
@spec build_url(Tesla.Env.t()) :: String.t()
```

Builds a URL from the given `t:Tesla.Env.t/0` struct.

Combines the `url` and `query` fields, and allows specifying the `encoding`
strategy before calling `build_url/3`.

# `build_url`

```elixir
@spec build_url(Tesla.Env.url(), Tesla.Env.query(), encoding_strategy()) :: binary()
```

Builds URL with the given URL and query params.

Useful when you need to create a URL with dynamic query params from a Keyword
list

Allows to specify the `encoding` strategy to be one either `:www_form` or
`:rfc3986`. Read more about encoding at `URI.encode_query/2`.

- `url` - the base URL to which the query params will be appended.
- `query` - a list of key-value pairs to be encoded as query params.
- `encoding` - the encoding strategy to use. Defaults to `:www_form`

## Examples

    iex> Tesla.build_url("https://api.example.com", [user: 3, page: 2])
    "https://api.example.com?user=3&page=2"

URL that already contains query params:

    iex> url = "https://api.example.com?user=3"
    iex> Tesla.build_url(url, [page: 2, status: true])
    "https://api.example.com?user=3&page=2&status=true"

Default encoding `:www_form`:

    iex> Tesla.build_url("https://api.example.com", [user_name: "John Smith"])
    "https://api.example.com?user_name=John+Smith"

Specified encoding strategy `:rfc3986`:

    iex> Tesla.build_url("https://api.example.com", [user_name: "John Smith"], :rfc3986)
    "https://api.example.com?user_name=John%20Smith"

# `client`
*since 1.2.0* 

```elixir
@spec client([Tesla.Client.middleware()], Tesla.Client.adapter()) :: Tesla.Client.t()
```

Dynamically build client from list of middlewares and/or adapter.

```
# add dynamic middleware
client = Tesla.client([{Tesla.Middleware.Headers, [{"authorization", token}]}])
Tesla.get(client, "/path")

# configure adapter in runtime
client = Tesla.client([], Tesla.Adapter.Hackney)
client = Tesla.client([], {Tesla.Adapter.Hackney, pool: :my_pool})
Tesla.get(client, "/path")

# complete module example
defmodule MyApi do
  @middleware [
    {Tesla.Middleware.BaseUrl, "https://example.com"},
    Tesla.Middleware.JSON,
    Tesla.Middleware.Logger
  ]

  @adapter Tesla.Adapter.Hackney

  def new(opts) do
    # do any middleware manipulation you need
    middleware = [
      {Tesla.Middleware.BasicAuth, username: opts[:username], password: opts[:password]}
    ] ++ @middleware

    # allow configuring adapter in runtime
    adapter = opts[:adapter] || @adapter

    # use Tesla.client/2 to put it all together
    Tesla.client(middleware, adapter)
  end

  def get_something(client, id) do
    # pass client directly to Tesla.get/2
    Tesla.get(client, "/something/#{id}")
    # ...
  end
end

client = MyApi.new(username: "admin", password: "secret")
MyApi.get_something(client, 42)
```

# `delete`

```elixir
@spec delete(Tesla.Env.client(), Tesla.Env.url(), [option()]) :: Tesla.Env.result()
```

Perform a DELETE request.

See `request/1` or `request/2` for options definition.

    delete("/users")
    delete("/users", query: [scope: "admin"])
    delete(client, "/users")
    delete(client, "/users", query: [scope: "admin"])
    delete(client, "/users", body: %{name: "Jon"})

# `delete!`

```elixir
@spec delete!(Tesla.Env.client(), Tesla.Env.url(), [option()]) ::
  Tesla.Env.t() | no_return()
```

Perform a DELETE request.

See `request!/1` or `request!/2` for options definition.

    delete!("/users")
    delete!("/users", query: [scope: "admin"])
    delete!(client, "/users")
    delete!(client, "/users", query: [scope: "admin"])
    delete!(client, "/users", body: %{name: "Jon"})

# `delete_header`

```elixir
@spec delete_header(Tesla.Env.t(), binary()) :: Tesla.Env.t()
```

# `encode_query`

# `get`

```elixir
@spec get(Tesla.Env.client(), Tesla.Env.url(), [option()]) :: Tesla.Env.result()
```

Perform a GET request.

See `request/1` or `request/2` for options definition.

    get("/users")
    get("/users", query: [scope: "admin"])
    get(client, "/users")
    get(client, "/users", query: [scope: "admin"])
    get(client, "/users", body: %{name: "Jon"})

# `get!`

```elixir
@spec get!(Tesla.Env.client(), Tesla.Env.url(), [option()]) ::
  Tesla.Env.t() | no_return()
```

Perform a GET request.

See `request!/1` or `request!/2` for options definition.

    get!("/users")
    get!("/users", query: [scope: "admin"])
    get!(client, "/users")
    get!(client, "/users", query: [scope: "admin"])
    get!(client, "/users", body: %{name: "Jon"})

# `get_header`

```elixir
@spec get_header(Tesla.Env.t(), binary()) :: binary() | nil
```

Returns value of header specified by `key` from `:headers` field in `Tesla.Env`.

## Examples

    # non existing header
    iex> env = %Tesla.Env{headers: [{"server", "Cowboy"}]}
    iex> Tesla.get_header(env, "some-key")
    nil

    # existing header
    iex> env = %Tesla.Env{headers: [{"server", "Cowboy"}]}
    iex> Tesla.get_header(env, "server")
    "Cowboy"

    # first of multiple headers with the same name
    iex> env = %Tesla.Env{headers: [{"cookie", "chocolate"}, {"cookie", "biscuits"}]}
    iex> Tesla.get_header(env, "cookie")
    "chocolate"

# `get_headers`

```elixir
@spec get_headers(Tesla.Env.t(), binary()) :: [binary()]
```

# `head`

```elixir
@spec head(Tesla.Env.client(), Tesla.Env.url(), [option()]) :: Tesla.Env.result()
```

Perform a HEAD request.

See `request/1` or `request/2` for options definition.

    head("/users")
    head("/users", query: [scope: "admin"])
    head(client, "/users")
    head(client, "/users", query: [scope: "admin"])
    head(client, "/users", body: %{name: "Jon"})

# `head!`

```elixir
@spec head!(Tesla.Env.client(), Tesla.Env.url(), [option()]) ::
  Tesla.Env.t() | no_return()
```

Perform a HEAD request.

See `request!/1` or `request!/2` for options definition.

    head!("/users")
    head!("/users", query: [scope: "admin"])
    head!(client, "/users")
    head!(client, "/users", query: [scope: "admin"])
    head!(client, "/users", body: %{name: "Jon"})

# `options`

```elixir
@spec options(Tesla.Env.client(), Tesla.Env.url(), [option()]) :: Tesla.Env.result()
```

Perform a OPTIONS request.

See `request/1` or `request/2` for options definition.

    options("/users")
    options("/users", query: [scope: "admin"])
    options(client, "/users")
    options(client, "/users", query: [scope: "admin"])
    options(client, "/users", body: %{name: "Jon"})

# `options!`

```elixir
@spec options!(Tesla.Env.client(), Tesla.Env.url(), [option()]) ::
  Tesla.Env.t() | no_return()
```

Perform a OPTIONS request.

See `request!/1` or `request!/2` for options definition.

    options!("/users")
    options!("/users", query: [scope: "admin"])
    options!(client, "/users")
    options!(client, "/users", query: [scope: "admin"])
    options!(client, "/users", body: %{name: "Jon"})

# `patch`

```elixir
@spec patch(Tesla.Env.client(), Tesla.Env.url(), Tesla.Env.body(), [option()]) ::
  Tesla.Env.result()
```

Perform a PATCH request.

See `request/1` or `request/2` for options definition.

    patch("/users", %{name: "Jon"})
    patch("/users", %{name: "Jon"}, query: [scope: "admin"])
    patch(client, "/users", %{name: "Jon"})
    patch(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
    

# `patch!`

```elixir
@spec patch!(Tesla.Env.client(), Tesla.Env.url(), Tesla.Env.body(), [option()]) ::
  Tesla.Env.t() | no_return()
```

Perform a PATCH request.

See `request!/1` or `request!/2` for options definition.

    patch!("/users", %{name: "Jon"})
    patch!("/users", %{name: "Jon"}, query: [scope: "admin"])
    patch!(client, "/users", %{name: "Jon"})
    patch!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
    

# `post`

```elixir
@spec post(Tesla.Env.client(), Tesla.Env.url(), Tesla.Env.body(), [option()]) ::
  Tesla.Env.result()
```

Perform a POST request.

See `request/1` or `request/2` for options definition.

    post("/users", %{name: "Jon"})
    post("/users", %{name: "Jon"}, query: [scope: "admin"])
    post(client, "/users", %{name: "Jon"})
    post(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
    

# `post!`

```elixir
@spec post!(Tesla.Env.client(), Tesla.Env.url(), Tesla.Env.body(), [option()]) ::
  Tesla.Env.t() | no_return()
```

Perform a POST request.

See `request!/1` or `request!/2` for options definition.

    post!("/users", %{name: "Jon"})
    post!("/users", %{name: "Jon"}, query: [scope: "admin"])
    post!(client, "/users", %{name: "Jon"})
    post!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
    

# `put`

```elixir
@spec put(Tesla.Env.client(), Tesla.Env.url(), Tesla.Env.body(), [option()]) ::
  Tesla.Env.result()
```

Perform a PUT request.

See `request/1` or `request/2` for options definition.

    put("/users", %{name: "Jon"})
    put("/users", %{name: "Jon"}, query: [scope: "admin"])
    put(client, "/users", %{name: "Jon"})
    put(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
    

# `put!`

```elixir
@spec put!(Tesla.Env.client(), Tesla.Env.url(), Tesla.Env.body(), [option()]) ::
  Tesla.Env.t() | no_return()
```

Perform a PUT request.

See `request!/1` or `request!/2` for options definition.

    put!("/users", %{name: "Jon"})
    put!("/users", %{name: "Jon"}, query: [scope: "admin"])
    put!(client, "/users", %{name: "Jon"})
    put!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
    

# `put_assign`

```elixir
@spec put_assign(Tesla.Env.t(), atom(), any()) :: Tesla.Env.t()
```

Assigns key/value to `Tesla.Env`.

## Examples

    iex> env = %Tesla.Env{}
    iex> env = Tesla.put_assign(env, :user_id, 123)
    iex> env.assigns
    %{user_id: 123}

# `put_assign_new`

```elixir
@spec put_assign_new(Tesla.Env.t(), atom(), any()) :: Tesla.Env.t()
```

Assigns key/value to `Tesla.Env` unless the key is already set.

## Examples

    iex> env = %Tesla.Env{}
    iex> env = Tesla.put_assign_new(env, :user_id, 123)
    iex> env = Tesla.put_assign_new(env, :user_id, 456)
    iex> env.assigns
    %{user_id: 123}

# `put_assigns`

```elixir
@spec put_assigns(Tesla.Env.t(), Tesla.Env.assigns()) :: Tesla.Env.t()
```

Merges the given assigns into `Tesla.Env`.

## Examples

    iex> env = %Tesla.Env{}
    iex> env = Tesla.put_assigns(env, %{user_id: 123, role: :admin})
    iex> env.assigns
    %{user_id: 123, role: :admin}

    iex> env = %Tesla.Env{assigns: %{user_id: 123}}
    iex> env = Tesla.put_assigns(env, %{role: :admin})
    iex> env.assigns
    %{user_id: 123, role: :admin}

# `put_body`

```elixir
@spec put_body(Tesla.Env.t(), Tesla.Env.body()) :: Tesla.Env.t()
```

# `put_header`

```elixir
@spec put_header(Tesla.Env.t(), binary(), binary()) :: Tesla.Env.t()
```

# `put_headers`

```elixir
@spec put_headers(Tesla.Env.t(), [{binary(), binary()}]) :: Tesla.Env.t()
```

# `put_opt`

```elixir
@spec put_opt(Tesla.Env.t(), atom(), any()) :: Tesla.Env.t()
```

Adds given key/value pair to `:opts` field in `Tesla.Env`.

Useful when there's a need to store additional middleware data in `Tesla.Env`

## Examples

    iex> %Tesla.Env{opts: []} |> Tesla.put_opt(:option, "value")
    %Tesla.Env{opts: [option: "value"]}

# `request`

```elixir
@spec request(Tesla.Env.client(), [option()]) :: Tesla.Env.result()
```

Perform a request.

## Options

- `:method` - the request method, one of [`:head`, `:get`, `:delete`, `:trace`, `:options`, `:post`, `:put`, `:patch`]
- `:url` - either full url e.g. "http://example.com/some/path" or just "/some/path" if using `Tesla.Middleware.BaseUrl`
- `:query` - a keyword list of query params, e.g. `[page: 1, per_page: 100]`
- `:headers` - a keyword list of headers, e.g. `[{"content-type", "text/plain"}]`
- `:body` - depends on used middleware:
    - by default it can be a binary
    - if using e.g. JSON encoding middleware it can be a nested map
    - if adapter supports it it can be a Stream with any of the above
- `:opts` - custom, per-request middleware or adapter options

## Examples

    ExampleApi.request(method: :get, url: "/users/path")

    # use shortcut methods
    ExampleApi.get("/users/1")
    ExampleApi.post(client, "/users", %{name: "Jon"})

# `request!`

```elixir
@spec request!(Tesla.Env.client(), [option()]) :: Tesla.Env.t() | no_return()
```

Perform request and raise in case of error.

This is similar to `request/2` behaviour from Tesla 0.x

See `request/2` for list of available options.

# `run`

```elixir
@spec run(Tesla.Env.t(), Tesla.Env.stack()) :: Tesla.Env.result()
```

# `run_default_adapter`

# `trace`

```elixir
@spec trace(Tesla.Env.client(), Tesla.Env.url(), [option()]) :: Tesla.Env.result()
```

Perform a TRACE request.

See `request/1` or `request/2` for options definition.

    trace("/users")
    trace("/users", query: [scope: "admin"])
    trace(client, "/users")
    trace(client, "/users", query: [scope: "admin"])
    trace(client, "/users", body: %{name: "Jon"})

# `trace!`

```elixir
@spec trace!(Tesla.Env.client(), Tesla.Env.url(), [option()]) ::
  Tesla.Env.t() | no_return()
```

Perform a TRACE request.

See `request!/1` or `request!/2` for options definition.

    trace!("/users")
    trace!("/users", query: [scope: "admin"])
    trace!(client, "/users")
    trace!(client, "/users", query: [scope: "admin"])
    trace!(client, "/users", body: %{name: "Jon"})

# `update_assign`

```elixir
@spec update_assign(Tesla.Env.t(), atom(), any(), (any() -&gt; any())) :: Tesla.Env.t()
```

Updates assign `key` in `Tesla.Env` with the given function or `default`.

If `key` is present in assigns then the existing value is passed to `fun` and its
result is used as the updated value of `key`. If `key` is not present in assigns,
`default` is inserted as the value of `key`. The `default` value will not be passed
through the update function.

See also `update_assign!/3`.

## Examples

    iex> env = %Tesla.Env{assigns: %{counter: 10}}
    iex> env = Tesla.update_assign(env, :counter, 1, &(&1 + 1))
    iex> env = Tesla.update_assign(env, :other, 1, &(&1 + 1))
    iex> env.assigns
    %{counter: 11, other: 1}

# `update_assign!`

```elixir
@spec update_assign!(Tesla.Env.t(), atom(), (any() -&gt; any())) :: Tesla.Env.t()
```

Updates assign `key` in `Tesla.Env` with the given function.

Raises if the `key` is not set.

See also `update_assign/4`.

## Examples

    iex> env = %Tesla.Env{assigns: %{counter: 1}}
    iex> env = Tesla.update_assign!(env, :counter, &(&1 + 1))
    iex> env.assigns
    %{counter: 2}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
