# Tesla

[![CircleCI Status](](

Tesla is an HTTP client losely based on [Faraday](
It embraces the concept of middleware when processing the request/response cycle.

## Direct usage

# Example get request
response = Tesla.get("")
response.status   # => 200
response.body     # => '{\n  "origin": ""\n}\n'
response.headers  # => %{'Content-Type' => 'application/json' ...}

response = Tesla.get("", query: [a: 1, b: "foo"])
response.url     # => ""

# Example post request
response ="", "data", headers: %{"Content-Type" => "application/json"})

## Installation

Add `tesla` as dependency in `mix.exs`

defp deps do
  [{:tesla, "~> 0.5.0"},
   {:poison, ">= 1.0.0"}] # for JSON middleware

When using `ibrowse` or `hackney` adapters remember to alter applications list in `mix.exs`

def application do
  [applications: [:ibrowse, ...], ...] # or :hackney

## Creating API clients

Use `Tesla` module to create API wrappers.

For example

defmodule GitHub do
  use Tesla

  plug Tesla.Middleware.BaseUrl, ""
  plug Tesla.Middleware.Headers, %{'Authorization' => 'xyz'}
  plug Tesla.Middleware.JSON

  adapter Tesla.Adapter.Hackney

  def user_repos(login) do
    get("/user/" <> login <> "/repos")

Then use it like this:


## Adapters

Tesla has support for different adapters that do the actual HTTP request processing.

### [httpc](

The default adapter, available in all erlang installations

### [hackney](

This adapter supports real streaming body.
To use it simply include `adapter :hackney` line in your API client definition.
NOTE: Remember to include hackney in applications list.

### [ibrowse](

Tesla has built-in support for [ibrowse]( Erlang HTTP client.
To use it simply include `adapter :ibrowse` line in your API client definition.
NOTE: Remember to include ibrowse in applications list.

### Test / Mock

When testing it might be useful to use simple function as adapter:

defmodule MyApi do
  use Tesla

  adapter fn (env) ->
    case env.url do
      "/"       -> %{env | status: 200, body: "home"}
      "/about"  -> %{env | status: 200, body: "about us"}

## Middleware

### Basic

- `Tesla.Middleware.BaseUrl` - set base url for all request
- `Tesla.Middleware.Headers` - set request headers
- `Tesla.Middleware.Query` - set query parameters
- `Tesla.Middleware.DecodeRels` - decode `Link` header into `opts[:rels]` field in response

### JSON

NOTE: requires [poison]( (or other engine) as dependency

- `Tesla.Middleware.JSON`` - encode/decode request/response bodies as JSON

If you are using different json library it can be easily configured:

plug Tesla.Middleware.JSON, engine: JSX, engine_opts: [strict: [:comments]]
# or
plug Tesla.Middleware.JSON, decode: &JSX.decode/1, encode: &JSX.encode/1

See [`json.ex`]( for implementation details.

### Logging

- `Tesla.Middleware.Logger` - log each request in single line including method, path, status and execution time (colored)
- `Tesla.Middleware.DebugLogger` - log full request and response (incl. headers and body)

## Dynamic middleware

All methods can take a middleware function as the first parameter.
This allow to use convenient syntax for modifying the behaviour in runtime.

Consider the following case: GitHub API can be accessed using OAuth token authorization.

We can't use `plug Tesla.Middleware.Headers, %{"Authorization" => "token here"}` since this would be compiled only once and there is no way to insert dynamic user token.

Instead, we can use `Tesla.build_client` to create a dynamic middleware function:

defmodule GitHub do
  # same as above

  def client(token) do
    Tesla.build_client [
      {Tesla.Middleware.Headers, %{"Authorization" => "token: " <> token }}

and then:

client = GitHub.client(user_token)
client |> GitHub.user_repos("teamon")
client |> GitHub.get("/me")

## Writing your own middleware

A Tesla middleware is a module with `call/3` function, that at some point calls `, next)` to process
the rest of stack

defmodule MyMiddleware do
  def call(env, next, options) do
    |> do_something_with_request
    |> do_something_with_response

The arguments are:
- `env` - `Tesla.Env` instance
- `next` - middleware continuation stack; to be executed with `, next)`
- `options` - arguments passed during middleware configuration (`plug MyMiddleware, options`)

There is no distinction between request and response middleware, it's all about executing `` function at the correct time.

For example, z request logger middleware could be implemented like this:

defmodule Tesla.Middleware.RequestLogger do
  def call(env, next, _) do
    IO.inspect env # print request env, next)

and response logger middleware like this:

defmodule Tesla.Middleware.ResponseLogger do
  def call(env, next, _) do
    res =, next)
    IO.inspect res # print response env

See [`core.ex`]( and [`json.ex`]( for more examples.

## Streaming body

If adapter supports it, you can pass a [Stream]( as body, e.g.:

defmodule ES do
  use Tesla.Builder

  plug Tesla.Middleware.BaseUrl, "http://localhost:9200"

  plug Tesla.Middleware.DecodeJson
  plug Tesla.Middleware.EncodeJson

  def index(records) do
    stream = records |> record -> %{index: [some, data]})
    post("/_bulk", stream)

Each piece of stream will be encoded as json and sent as a new line (conforming to json stream format)