ReqFly (req_fly v1.0.0)

View Source

A Req plugin for the Fly.io Machines API.

ReqFly provides a convenient interface to interact with Fly.io's Machines API through the Req HTTP client library. It handles authentication, error handling, retries, and telemetry automatically.

Installation

Add ReqFly to your dependencies in mix.exs:

def deps do
  [
    {:req_fly, "~> 0.1.0"}
  ]
end

Quick Start

Get your API token from https://fly.io/user/personal_access_tokens:

req = Req.new() |> ReqFly.attach(token: "your_fly_token")

# List apps
{:ok, apps} = ReqFly.Apps.list(req, org_slug: "personal")

# Create a machine
{:ok, machine} = ReqFly.Machines.create(req,
  app_name: "my-app",
  config: %{image: "nginx:latest"}
)

High-Level APIs

ReqFly provides convenient helper modules for common operations:

Low-Level Plugin Usage

You can also use ReqFly as a Req plugin with direct HTTP calls:

req = Req.new() |> ReqFly.attach(token: "fly_token")
{:ok, %{body: apps}} = Req.get(req, url: "/apps", params: [org_slug: "personal"])
{:ok, %{body: app}} = Req.post(req, url: "/apps", json: %{app_name: "test", org_slug: "personal"})

Configuration

req = Req.new() |> ReqFly.attach(token: "your_fly_token")

Environment Variable

req = Req.new() |> ReqFly.attach(token: System.get_env("FLY_API_TOKEN"))

Application Config

# config/config.exs
config :req_fly, token: "your_fly_token"

# In your code
req = Req.new() |> ReqFly.attach()
# config/runtime.exs
config :req_fly, token: System.get_env("FLY_API_TOKEN")

Options

  • :token - Your Fly.io API token (required, can be set in app config)
  • :base_url - Override the default API base URL (default: "https://api.machines.dev/v1")
  • :retry - Retry strategy (default: :safe_transient)
  • :max_retries - Maximum number of retries (default: 3)
  • :telemetry_prefix - Custom telemetry event prefix (default: [:req_fly])

Authentication

Get your API token from: https://fly.io/user/personal_access_tokens

Important: Never commit your API token to version control. Use environment variables or runtime configuration.

Telemetry

ReqFly emits the following telemetry events:

  • [:req_fly, :request, :start] - Emitted when a request starts
  • [:req_fly, :request, :stop] - Emitted when a request completes successfully
  • [:req_fly, :request, :exception] - Emitted when a request fails

You can attach handlers to monitor your API usage:

:telemetry.attach_many(
  "req-fly-handler",
  [
    [:req_fly, :request, :start],
    [:req_fly, :request, :stop],
    [:req_fly, :request, :exception]
  ],
  fn event_name, measurements, metadata, _config ->
    # Handle telemetry event
    Logger.info("Fly.io API call", event: event_name, metadata: metadata)
  end,
  nil
)

Examples

# Basic usage with token
req = Req.new() |> ReqFly.attach(token: "fly_token")
{:ok, apps} = ReqFly.Apps.list(req, org_slug: "personal")

# Create app and machine
req = Req.new() |> ReqFly.attach(token: System.get_env("FLY_API_TOKEN"))
{:ok, app} = ReqFly.Apps.create(req, app_name: "my-app", org_slug: "personal")

config = %{
  image: "nginx:latest",
  guest: %{cpus: 1, memory_mb: 256}
}
{:ok, machine} = ReqFly.Machines.create(req, app_name: "my-app", config: config)

# Use orchestrator for complex workflows
{:ok, machine} = ReqFly.Orchestrator.create_machine_and_wait(req,
  app_name: "my-app",
  config: config,
  timeout: 60
)

# Custom retry configuration
req = Req.new() |> ReqFly.attach(
  token: "fly_token",
  retry: :transient,
  max_retries: 5
)

# Custom telemetry prefix
req = Req.new() |> ReqFly.attach(
  token: "fly_token",
  telemetry_prefix: [:my_app, :fly]
)

Error Handling

All functions return {:ok, result} or {:error, %ReqFly.Error{}} tuples. Use pattern matching to handle errors:

case ReqFly.Apps.get(req, "my-app") do
  {:ok, app} -> 
    IO.puts("Found app: #{app["name"]}")

  {:error, %ReqFly.Error{status: 404}} ->
    IO.puts("App not found")

  {:error, %ReqFly.Error{status: status, message: message}} ->
    IO.puts("Error #{status}: #{message}")
end

Summary

Functions

Attaches the ReqFly plugin to a Req request.

Helper function to make HTTP requests with the configured ReqFly client.

Functions

attach(request, options \\ [])

@spec attach(
  Req.Request.t(),
  keyword()
) :: Req.Request.t()

Attaches the ReqFly plugin to a Req request.

Options

  • :token - Fly.io API token (required if not configured globally)
  • :base_url - API base URL (default: "https://api.machines.dev/v1")
  • :retry - Retry strategy (default: :safe_transient)
  • :max_retries - Maximum retries (default: 3)
  • :telemetry_prefix - Telemetry event prefix (default: [:req_fly])

Examples

req = Req.new() |> ReqFly.attach(token: "fly_token")

req = Req.new() |> ReqFly.attach(
  token: "fly_token",
  base_url: "https://custom.api.dev",
  retry: :transient,
  max_retries: 5
)

request(req, method, path, opts \\ [])

@spec request(Req.Request.t(), atom(), String.t(), keyword()) ::
  {:ok, term()} | {:error, ReqFly.Error.t()}

Helper function to make HTTP requests with the configured ReqFly client.

This is a convenience function that builds the full URL from base_url + path, makes the HTTP request, and returns a simplified response tuple.

Parameters

  • req - A Req.Request with ReqFly attached
  • method - HTTP method (:get, :post, :put, :delete, etc.)
  • path - URL path (will be appended to base_url)
  • opts - Additional options (:params, :json, etc.)

Returns

  • {:ok, body} - For successful 2xx responses
  • {:error, %ReqFly.Error{}} - For errors

Examples

req = Req.new() |> ReqFly.attach(token: "fly_token")
ReqFly.request(req, :get, "/apps")
# => {:ok, [%{"name" => "my-app"}]}

ReqFly.request(req, :post, "/apps/my-app/machines", json: %{config: %{}})
# => {:ok, %{"id" => "machine123"}}

ReqFly.request(req, :get, "/apps/nonexistent")
# => {:error, %ReqFly.Error{status: 404}}