# Tesla: Elixir HTTP client with composable middleware and swappable adapters

> Tesla is an MIT-licensed Elixir HTTP client that stacks middleware modules over a common adapter interface, letting teams swap transport backends without touching request logic. Its default Erlang httpc adapter skips SSL certificate validation, and the project explicitly states it will not fix that.

**elixir-tesla/tesla** — The flexible HTTP client library for Elixir, with support for middleware and multiple adapters.

- Repository: https://github.com/elixir-tesla/tesla
- Stars: 2,075 · Forks: 366
- Language: Elixir
- License: MIT
- Published: 2026-10-11 · Updated: 2026-10-11 · Language: en
- Canonical page: https://hysenlabs.com/projects/elixir-tesla-tesla

## The default adapter skips SSL validation, and the project states it will not change this

The library's default adapter is `httpc`, Erlang's built-in HTTP client. Tesla ships with it to avoid adding dependencies for new projects. The tradeoff is explicit: `httpc` does not validate SSL certificates and carries other problems tracked in its own issue queue.

The project states its position directly: "We believe that such security issues should be addressed by `:httpc` itself and we are not planning to fix them in `Tesla` due to backward compatibility." SSL validation is outside Tesla's scope and will stay there. Any application that calls `Tesla.get!("https://httpbin.org/get")` with no adapter configuration set is using `httpc` without SSL validation, and that includes every project created from a copy-paste of the quick-start example.

The fix requires 2 changes. Add `{:mint, "~> 1.0"}` to the project's dependency list, then set the adapter in `config/config.exs`:

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

Mint is the adapter the documentation recommends. Any application heading to production should have that line in place before the first deploy. The default exists for initial setup convenience, and Tesla says so directly, which is more candid than most library documentation tends to be about a default that carries a known security gap. Skipping this step means every outbound HTTPS request silently bypasses certificate verification.

## Adding Tesla to mix.exs and a discrepancy in the automation-managed dependency block

Tesla is published on Hex.pm. Adding it to a project means a `deps` entry in `mix.exs`. The quick-start dependency block lists 3 entries:

```elixir
defp deps do
  [
    {:tesla, "~> 1.22.0"},
    # optional, required by JSON middleware
    {:jason, "~> 1.4"},
    # optional, required by Mint adapter, recommended
    {:mint, "~> 1.0"}
  ]
end
```

Jason is optional and required only by the JSON middleware. Mint is optional and recommended. Neither is included automatically, so a project that omits Jason cannot use `Tesla.Middleware.JSON`, and a project that omits Mint stays on `httpc`.

One detail in the block is worth noting: the version constraint is `"~> 1.22.0"`, but the repository's published tag list tops out at v1.21.3. A project copying this block will fail to fetch the dependency until a higher tag appears on Hex.pm, because Hex.pm tracks tags and `"~> 1.22.0"` requires a tag in that minor series.

After adding the dependencies, the first request in an interactive session looks like this:

```elixir
Tesla.get!("https://httpbin.org/get").status
# => 200
```

That call uses no middleware and the global default adapter. The `.status` field holds the HTTP response code.

## Tesla.client builds a reusable struct with a fixed middleware stack

Rather than configuring each request individually, Tesla builds a client struct that holds a fixed middleware stack. The `Tesla.client` function takes the middleware list as its first argument:

```elixir
client = Tesla.client([
  {Tesla.Middleware.BaseUrl, "https://httpbin.org/"},
  Tesla.Middleware.JSON,
])
```

Creating that client fixes the base URL at construction time. A call to `Tesla.get!(client, "/json")` appends the path to the stored base and decodes the response body through the JSON middleware automatically, with no repeated configuration on each call.

A specific adapter can be fixed per client by passing it as the second argument, overriding the global config:

```elixir
client = Tesla.client([], {Tesla.Adapter.Hackney, pool: :my_pool})
```

2 clients in the same application can therefore use different adapters. One might route external API calls through Mint, while another uses a named Hackney pool for internal service requests. The global adapter setting and the per-client adapter are independent settings, which is what allows an application to make different architectural decisions for different parts of its network traffic without a shared configuration interfering. Teams building multiple API clients in one service find this particularly useful when one external service has strict rate limits and another expects long-lived connections.

## 19 built-in middleware modules split across format, auth and error categories

The built-in middleware list covers 19 named modules across 4 groups. 9 handle request shaping: `BaseUrl`, `Headers`, `Query`, `Opts`, `FollowRedirects`, `MethodOverride`, `Logger`, `KeepRequest` and `PathParams`. 4 cover body formats: `FormUrlencoded`, `JSON`, `Compression` and `DecodeRels`. 3 handle authentication: `BasicAuth`, `BearerAuth` and `DigestAuth`. 3 more address errors: `Timeout`, `Retry` and `Fuse`.

`Tesla.Middleware.PathParams` resolves URL templates, letting paths carry named parameters that the middleware fills in from request options. `Tesla.Middleware.DecodeRels` parses the `Link` response header into the `opts[:rels]` field on the response object, which is the structure used by APIs that follow the GitHub-style pagination link pattern. `Tesla.Middleware.KeepRequest` retains the original request `body` and `headers` on the response struct, which matters in workflows that need to log or compare what was sent against what came back.

`Tesla.Middleware.Query` adds OpenAPI-style query serialization in modern mode, a capability noted in the module description without detail on what changes relative to the default mode.

Stack order matters throughout. Each middleware wraps the layers below it in the list, so placing `Logger` before `Retry` means failed retry attempts appear in the log alongside the initial failure. Custom middleware fits the same single-function interface as the built-in modules.

## 6 adapters: Gun is the only one that names WebSocket alongside HTTP

6 adapters ship with Tesla and cover different transport backends. `Tesla.Adapter.Httpc` is the default Erlang built-in, not recommended for production. `Tesla.Adapter.Hackney` wraps hackney, a simple HTTP client in Erlang. `Tesla.Adapter.Ibrowse` wraps ibrowse, another Erlang HTTP client. `Tesla.Adapter.Gun` wraps gun, described as an "HTTP/1.1, HTTP/2 and Websocket client for Erlang/OTP." `Tesla.Adapter.Mint` wraps mint, a functional HTTP client for Elixir with support for HTTP/1 and HTTP/2. `Tesla.Adapter.Finch` wraps finch, described as "An HTTP client with a focus on performance, built on top of Mint and NimblePool."

Gun is the only adapter in the list whose description names WebSocket alongside HTTP. Teams that need WebSocket connections through the same backend as their regular HTTP requests should evaluate Gun. Finch is the choice where performance is the stated priority, built on NimblePool for connection management.

Configuration options for individual adapters are largely absent from the main quick-start documentation. The only configuration key shown inline is `pool: :my_pool` in the Hackney client example. All other adapter-specific settings point to each backend library's own reference documentation, so switching adapters means consulting 2 documentation sources rather than 1.

## Timeout cuts the request, Retry reconnects, and Fuse opens the circuit on repeated failures

The 3 error-handling middleware modules target distinct failure conditions. `Tesla.Middleware.Timeout` cancels the request "after X milliseconds despite of server response," firing regardless of what the server is doing at the time. This covers a stalling server that sends partial data and then stops responding, a situation that would otherwise hold the calling process indefinitely without a timeout in place.

`Tesla.Middleware.Retry` retries "few times in case of connection refused," targeting refused connections at the network layer. Its description covers refused connections specifically, not application-layer responses, so it handles cases where a server process is not listening rather than cases where a running server returns an error.

`Tesla.Middleware.Fuse` integrates the Fuse circuit breaker library. A circuit breaker accumulates failures until a threshold, then opens the circuit and rejects calls immediately rather than sending them to an already-failing endpoint. Retry keeps trying after a failure; Fuse eventually stops trying and holds the circuit open until a reset interval passes. This distinction matters when an upstream service is under load: Retry adds traffic, while Fuse removes it.

Default values for timeout duration, retry count and fuse threshold are not in the quick-start documentation. Those details appear in each middleware module's reference page on hexdocs.pm. Using all 3 of these modules in a single client is valid; the key is setting them in a deliberate order, since placing Timeout before Retry means the timeout fires per attempt.

## What the guides cover that the quick-start documentation skips

Beyond the quick-start documentation, the repository's guides directory holds 7 documents in total. 5 cover explanations: Client, Testing, Middleware, Adapter and OpenAPI. A how-to document covers working with OpenAPI parameters, and a migration document covers the changes from the earlier API surface. None of this content appears in the main quick-start text, which links to each guide without reproducing it.

The Testing guide covers test adapters that intercept requests before they reach the network, letting tests verify that a client sends the right headers and body without making real HTTP calls. This pattern is not visible in the quick-start examples, so teams writing integration tests for Tesla-backed code need to read that guide before building a test harness.

The OpenAPI integration spans 2 documents. The Middleware explanation covers the OpenAPI-style query serialization mode on `Tesla.Middleware.Query`, and the parameters how-to covers integrating Tesla with OpenAPI-generated clients more broadly.

Tesla was inspired by Ruby's Faraday. The MIT licence text is in the LICENSE file at the repository root, and the last push was on 2026-10-05.

## Conclusion

Tesla fits Elixir teams calling multiple external APIs from one application, where each API needs different authentication, logging or retry behaviour. The middleware model handles that without repeating configuration on every call. For WebSocket-heavy work, only the Gun adapter documents WebSocket support, and the middleware layer is built for request-response cycles. Before deploying, switch from the default httpc adapter by adding Mint or Finch to mix.exs and setting the adapter in config/config.exs, then verify that the Retry and Fuse thresholds match the failure modes your application encounters.

## FAQ

### What is Tesla in Elixir?

Tesla is an Elixir HTTP client library that uses composable middleware modules to handle authentication, logging, retries and request formatting over a common adapter interface. It is published on Hex.pm and was inspired by the Ruby Faraday library.

### How do I add Tesla to an Elixir project?

Add tesla to the deps list in mix.exs alongside jason for JSON decoding and mint for a production-safe adapter. Then set `config :tesla, adapter: Tesla.Adapter.Mint` in config/config.exs to replace the default httpc adapter before going to production.

### Which Tesla adapter should I use in production?

Tesla explicitly warns against the default httpc adapter in production because it does not validate SSL certificates. Mint, Finch or Hackney are the documented alternatives, and the quick-start provides an example of setting Mint as the global adapter in config/config.exs.

### Can Tesla handle WebSocket connections?

Among the 6 built-in adapters, only Gun is described as supporting WebSocket alongside HTTP/1.1 and HTTP/2. The middleware layer is designed for request-response cycles and does not include WebSocket-specific middleware.

### How does Tesla middleware order affect requests?

Tesla wraps each middleware around the layers below it in the list passed to Tesla.client, so order determines which module runs first on the outgoing request and last on the incoming response. Placing Timeout before Retry means each individual retry attempt is separately timed out.

## Sources

- [elixir-tesla/tesla on GitHub](https://github.com/elixir-tesla/tesla)
- [Issues](https://github.com/elixir-tesla/tesla/issues)
- [License: MIT](https://github.com/elixir-tesla/tesla/blob/master/LICENSE)
- [README](https://github.com/elixir-tesla/tesla/blob/master/README.md)
- [Releases](https://github.com/elixir-tesla/tesla/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/elixir-tesla-tesla
