#development #elixir

I came across this example of how nice and concise Elixir can be. It's a simple Github API client that does quite a bit in a small space. I like it.

Elixir can pack a lot into a little. I do have doubts if I'm over compacting it a bit but but frankly I like this. Here's a simple snippet of a Github API Client doing quite a bit of lifting in a small space.

 1defmodule Github.API do
 2  @moduledoc """
 3  Provides common functions to extract data from the Github API.
 4  """
 5  @base_api_url "https://api.github.com/"
 6
 7  alias Github.UserRepository
 8  alias Github.UserProfile
 9  alias Github.UserEvents
10  alias Github.UserGists
11
12  @doc "Get the user profile"
13  def get_user_profile(username),
14    do: get("users/#{username}") |> handle_response(&UserProfile.new/1)
15
16  @doc "get the user profile, raise an exception if not ok"
17  def get_user_profile!(username), do: get_user_profile(username) |> unwrap_or_raise!()
18
19  @doc "get the users' repositories"
20  def repositories(username),
21    do: get("users/#{username}/repos") |> handle_response(&UserRepository.new/1)
22
23  @doc "get the users' repositories or raise an exception"
24  def repositories!(username), do: repositories(username) |> unwrap_or_raise!()
25
26  @doc "get the events for a user"
27  def events(username),
28    do: get("users/#{username}/events") |> handle_response(&UserEvents.new/1)
29
30  @doc "get the events for a user, raise an exception on error"
31  def events!(username), do: events(username) |> unwrap_or_raise!()
32
33  @doc "get the gists for a user"
34  def gists(username),
35    do: get("users/#{username}/gists") |> handle_response(&UserGists.new/1)
36
37  @doc "get the gists for a user, raise an exception on error"
38  def gists!(username), do: gists(username) |> unwrap_or_raise!()
39
40  # Handle unwrap functions
41  defp unwrap_or_raise!({:ok, res}), do: res
42  defp unwrap_or_raise!({:error, reason}),
43    do: raise("Github API request failed: #{inspect(reason)}")
44
45  # Start basic client functions here, these focus on the actual request and body
46  defp get_service_token(), do: Application.fetch_env!(:service, :github_api_token)
47
48  defp headers(), do: [{"Authorization", "Bearer #{get_service_token()}"}]
49
50  defp request_url(path), do: "#{@base_api_url}#{path}"
51
52  defp get(path), do: HTTPoison.get(request_url(path), headers())
53
54  defp handle_response({:ok, %HTTPoison.Response{status_code: 200, body: body}}, _transform) do
55    case Poison.decode(body) do
56      {:ok, parsed_body} when is_list(parsed_body) ->
57        {:ok, Enum.map(parsed_body, _transform)}
58
59      {:ok, parsed_body} ->
60        {:ok, transform(parsed_body)}
61
62      {:error, reason} ->
63        {:error, {:decode_failed, reason}}
64    end
65  end
66
67  defp handle_response({:ok, %HTTPoison.Response{status_code: code}}, _transform), do: {:error, {:unhandled_status_code, code}}
68end

Thanks to ChatGPT for converting the code in the source image to actual code.

source