Skip to content

Instantly share code, notes, and snippets.

@PJUllrich
Last active January 25, 2024 09:09
Show Gist options
  • Save PJUllrich/f6ff06d261557433607578ab3207cc10 to your computer and use it in GitHub Desktop.
Save PJUllrich/f6ff06d261557433607578ab3207cc10 to your computer and use it in GitHub Desktop.
HTTP Wrapper with optional Caching
defmodule MyApp.Http do
@moduledoc """
Exposes functions to make HTTP requests and optionally cache the response.
If you want to cache the request, simply add `cache: true` to the
request options. You can also define an option time-to-live (TTL) with
`cache_ttl: ttl_in_milliseconds`. The default TTL is 5min.
Only caches 2xx responses.
"""
require Logger
@default_cache_ttl :timer.minutes(5)
def get(url, opts \\ [], http_opts \\ []) do
request = fn -> adapter().get(url, opts) end
do_request(request, url, http_opts)
end
def get!(url, opts \\ [], http_opts \\ []) do
request = fn -> adapter().get!(url, opts) end
do_request(request, url, http_opts)
end
def post(url, opts \\ [], http_opts \\ []) do
request = fn -> adapter().post(url, opts) end
do_request(request, url, http_opts)
end
def delete(url, opts \\ [], http_opts \\ []) do
request = fn -> adapter().delete(url, opts) end
do_request(request, url, http_opts)
end
# Private functions
defp do_request(request, url, opts) do
request
|> maybe_read_cache(url, opts)
|> maybe_write_cache(url, opts)
end
defp maybe_read_cache(request, url, opts) do
with true <- Keyword.get(opts, :cache),
{:ok, results} <- read_cache(url) do
Logger.debug("Cache hit for #{url}")
results
else
_ ->
Logger.debug("Requesting from #{url}")
request.()
end
end
defp maybe_write_cache({:ok, results}, url, opts) do
results = maybe_write_cache(results, url, opts)
{:ok, results}
end
defp maybe_write_cache(%{status: status} = results, url, opts) when status in 200..299 do
if Keyword.get(opts, :cache), do: write_cache(url, results, opts)
results
end
defp maybe_write_cache(result, _url, _opts), do: result
defp read_cache(url) do
case Cachex.get(:http, url) do
{:ok, nil} -> {:error, :not_found}
{:ok, results} -> {:ok, results}
{:error, _} -> {:error, :not_found}
end
end
defp write_cache(url, results, opts) do
ttl = Keyword.get(opts, :cache_ttl, @default_cache_ttl)
{:ok, true} = Cachex.put(:http, url, results, ttl: ttl)
end
defp adapter(), do: Application.get_env(:my_app, :http_adapter)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment