Как обращаться к внешним REST API из Elixir?

В Elixir для обращения к внешним REST API чаще всего используют HTTP-клиенты, такие как HTTPoison, Finch, Tesla, Mint и другие. Наиболее распространённым является HTTPoison, благодаря простому интерфейсу и использованию библиотеки Hackney внутри. Ниже приведён подробный обзор способов обращения к REST API из Elixir, включая установку зависимостей, отправку запросов и обработку ответов.

1. Установка HTTP-клиента

Самый популярный и удобный для начала — HTTPoison.

Добавьте в файл mix.exs:

defp deps do
\[
{:httpoison, "~> 1.8"},
{:jason, "~> 1.2"} # для работы с JSON
\]
end

Затем:

mix deps.get

2. Отправка GET-запроса

url = "https://jsonplaceholder.typicode.com/posts/1"
case HTTPoison.get(url) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
{:ok, data} = Jason.decode(body)
IO.inspect(data)
{:ok, %HTTPoison.Response{status_code: status}} ->
IO.puts("Ошибка: #{status}")
{:error, %HTTPoison.Error{reason: reason}} ->
IO.inspect(reason)
end

Функция Jason.decode/1 преобразует JSON-строку в Elixir-структуру (map или list).

3. Отправка POST-запроса с телом

url = "https://jsonplaceholder.typicode.com/posts"
headers = \[{"Content-Type", "application/json"}\]
body = Jason.encode!(%{title: "foo", body: "bar", userId: 1})
case HTTPoison.post(url, body, headers) do
{:ok, %HTTPoison.Response{status_code: 201, body: body}} ->
IO.puts("Создано:")
IO.inspect(Jason.decode!(body))
{:ok, %HTTPoison.Response{status_code: status}} ->
IO.puts("Ошибка: #{status}")
{:error, %HTTPoison.Error{reason: reason}} ->
IO.inspect(reason)
end

POST-запрос включает заголовки, особенно Content-Type, и сериализованное тело запроса.

4. Использование других методов: PUT, PATCH, DELETE

HTTPoison.put(url, json_body, headers)
HTTPoison.patch(url, json_body, headers)
HTTPoison.delete(url, headers)

Эти функции работают аналогично post, только используют соответствующий HTTP-метод.

5. Параметры запроса

Для добавления query-параметров используйте URI:

uri = URI.encode_query(%{page: 1, limit: 10})
url = "https://api.example.com/items?" <> uri
HTTPoison.get(url)

6. Обработка ошибок

Всегда обрабатывайте оба случая {:ok, response} и {:error, reason}. Например, может быть:

  • таймаут,

  • отсутствие интернета,

  • 5xx или 4xx ошибки,

  • неожиданный формат ответа.

7. Заголовки и токены авторизации

headers = \[
{"Authorization", "Bearer #{token}"},
{"Accept", "application/json"}
\]

Для Basic Auth:

headers = \[
{"Authorization", "Basic " <> Base.encode64("username:password")}
\]

8. Альтернатива: Tesla

Tesla — более функциональный и модульный HTTP-клиент. Позволяет подключать middleware.

defmodule MyClient do
use Tesla
plug Tesla.Middleware.BaseUrl, "https://jsonplaceholder.typicode.com"
plug Tesla.Middleware.JSON
def get_post(id), do: get("/posts/#{id}")
end
MyClient.get_post(1)

Tesla требует установки :tesla и HTTP-адаптера (например, Finch или Hackney).

9. Асинхронные запросы

HTTPoison и другие клиенты работают синхронно. Для параллельных запросов используйте:

Task.async(fn -> HTTPoison.get(url1) end)
Task.async(fn -> HTTPoison.get(url2) end)
\[res1, res2\] = Task.yield_many(\[task1, task2\])

Или через Task.await.

10. Примеры использования

Получение данных:

{:ok, %HTTPoison.Response{body: body}} = HTTPoison.get("https://api.agify.io/?name=michael")
IO.inspect(Jason.decode!(body))

Авторизация:

token = System.get_env("API_TOKEN")
headers = \[{"Authorization", "Bearer #{token}"}\]
HTTPoison.get("https://api.example.com/protected", headers)

Работа с GitHub API:

url = "https://api.github.com/repos/elixir-lang/elixir"
headers = \[{"User-Agent", "Elixir Client"}\]
case HTTPoison.get(url, headers) do
{:ok, %HTTPoison.Response{body: body}} -> IO.inspect(Jason.decode!(body))
_ -> IO.puts("Ошибка при подключении")
end

Таким образом, в Elixir HTTP-запросы отправляются с помощью сторонних библиотек, чаще всего через HTTPoison или Tesla. Запросы полностью контролируются разработчиком, JSON обрабатывается явно, и функциональная модель кода обеспечивает чистоту и предсказуемость выполнения.