Getting Started with ReqFly
View SourceThis guide will walk you through installing and using ReqFly to interact with the Fly.io Machines API.
Prerequisites
Before you begin, make sure you have:
- Elixir 1.14 or later installed on your system
- A Fly.io account - Sign up at https://fly.io if you don't have one
- Fly CLI installed (optional, but recommended) - See https://fly.io/docs/hands-on/install-flyctl/
Installation
Add ReqFly to your project's dependencies in mix.exs:
def deps do
[
{:req_fly, "~> 0.1.0"}
]
endThen fetch the dependencies:
mix deps.get
Getting Your API Token
To use the Fly.io API, you need a personal access token:
- Visit https://fly.io/user/personal_access_tokens
- Click "Create Token"
- Give it a descriptive name (e.g., "ReqFly Development")
- Copy the token - you won't be able to see it again!
Storing Your Token
For development, you can use environment variables:
# Add to your .env or .envrc file
export FLY_API_TOKEN="your_token_here"
Important: Never commit your API token to version control!
Your First Request
Let's start with a simple example that lists your Fly.io apps:
# In iex
iex> req = Req.new() |> ReqFly.attach(token: System.get_env("FLY_API_TOKEN"))
iex> {:ok, apps} = ReqFly.Apps.list(req, org_slug: "personal")
{:ok, [%{"name" => "my-app", "status" => "running", ...}]}Congratulations! You've just made your first API call with ReqFly.
Common Workflows
Creating and Managing Apps
# Create a Req client
req = Req.new() |> ReqFly.attach(token: System.get_env("FLY_API_TOKEN"))
# Create a new app
{:ok, app} = ReqFly.Apps.create(req,
app_name: "my-test-app",
org_slug: "personal"
)
# Get app details
{:ok, app_details} = ReqFly.Apps.get(req, "my-test-app")
IO.inspect(app_details, label: "App Details")
# List all apps
{:ok, apps} = ReqFly.Apps.list(req, org_slug: "personal")
Enum.each(apps, fn app ->
IO.puts("App: #{app["name"]} - Status: #{app["status"]}")
end)Creating and Managing Machines
req = Req.new() |> ReqFly.attach(token: System.get_env("FLY_API_TOKEN"))
# Define machine configuration
config = %{
image: "flyio/hellofly:latest",
env: %{
"PORT" => "8080"
},
guest: %{
cpus: 1,
memory_mb: 256
}
}
# Create a machine
{:ok, machine} = ReqFly.Machines.create(req,
app_name: "my-test-app",
config: config,
region: "ewr"
)
machine_id = machine["id"]
IO.puts("Created machine: #{machine_id}")
# Start the machine
{:ok, _} = ReqFly.Machines.start(req,
app_name: "my-test-app",
machine_id: machine_id
)
# Wait for it to be ready
{:ok, ready_machine} = ReqFly.Machines.wait(req,
app_name: "my-test-app",
machine_id: machine_id,
instance_id: machine["instance_id"],
state: "started",
timeout: 60
)
IO.puts("Machine is ready!")Using the Orchestrator
The Orchestrator module provides high-level workflows that combine multiple operations:
req = Req.new() |> ReqFly.attach(token: System.get_env("FLY_API_TOKEN"))
# Create app and wait for it to be active
{:ok, app} = ReqFly.Orchestrator.create_app_and_wait(req,
app_name: "orchestrated-app",
org_slug: "personal",
timeout: 120
)
# Create machine and wait for it to start
config = %{
image: "nginx:latest",
guest: %{cpus: 1, memory_mb: 256}
}
{:ok, machine} = ReqFly.Orchestrator.create_machine_and_wait(req,
app_name: "orchestrated-app",
config: config,
region: "ewr",
timeout: 60
)
IO.puts("Everything is ready!")Managing Secrets
req = Req.new() |> ReqFly.attach(token: System.get_env("FLY_API_TOKEN"))
# Set an environment variable secret
{:ok, _} = ReqFly.Secrets.create(req,
app_name: "my-app",
label: "DATABASE_URL",
type: "env",
value: "postgres://localhost/mydb"
)
# Generate a random secret
{:ok, secret} = ReqFly.Secrets.generate(req,
app_name: "my-app",
label: "SECRET_KEY_BASE",
type: "env"
)
IO.puts("Generated secret value: #{secret["value"]}")
# List all secrets
{:ok, secrets} = ReqFly.Secrets.list(req, app_name: "my-app")Working with Volumes
req = Req.new() |> ReqFly.attach(token: System.get_env("FLY_API_TOKEN"))
# Create a persistent volume
{:ok, volume} = ReqFly.Volumes.create(req,
app_name: "my-app",
name: "postgres_data",
region: "ewr",
size_gb: 10
)
# Extend the volume
{:ok, _} = ReqFly.Volumes.extend(req,
app_name: "my-app",
volume_id: volume["id"],
size_gb: 20
)
# Create a snapshot for backup
{:ok, snapshot} = ReqFly.Volumes.create_snapshot(req,
app_name: "my-app",
volume_id: volume["id"]
)Error Handling
ReqFly uses pattern matching for error handling:
req = Req.new() |> ReqFly.attach(token: System.get_env("FLY_API_TOKEN"))
case ReqFly.Apps.get(req, "nonexistent-app") do
{:ok, app} ->
IO.puts("Found app: #{app["name"]}")
{:error, %ReqFly.Error{status: 404, message: message}} ->
IO.puts("App not found: #{message}")
{:error, %ReqFly.Error{status: 401}} ->
IO.puts("Authentication failed - check your API token")
{:error, %ReqFly.Error{status: status, message: message}} ->
IO.puts("API error #{status}: #{message}")
endConfiguration Options
ReqFly supports several configuration patterns:
1. Explicit Token (Recommended for Libraries)
req = Req.new() |> ReqFly.attach(token: "your_token")2. Environment Variable
req = Req.new() |> ReqFly.attach(token: System.get_env("FLY_API_TOKEN"))3. Application Configuration
# config/config.exs
config :req_fly, token: "your_token"
# In your code
req = Req.new() |> ReqFly.attach()4. Runtime Configuration (Recommended for Applications)
# config/runtime.exs
config :req_fly, token: System.get_env("FLY_API_TOKEN")
# In your code
req = Req.new() |> ReqFly.attach()Advanced Options
req = Req.new() |> ReqFly.attach(
token: System.get_env("FLY_API_TOKEN"),
base_url: "https://api.machines.dev/v1", # Custom API endpoint
retry: :safe_transient, # Retry safe methods
max_retries: 5, # More aggressive retries
telemetry_prefix: [:my_app, :fly] # Custom telemetry prefix
)Troubleshooting
Authentication Errors
If you get a 401 error:
- Check that your token is correct
- Ensure the token hasn't expired
- Verify you're using the correct token format (starts with
fly_)
Rate Limiting
The Fly.io API has rate limits. ReqFly automatically retries with exponential backoff, but if you're making many requests, consider:
- Adding delays between requests
- Batching operations where possible
- Using the Orchestrator for complex workflows
Timeout Errors
For long-running operations:
# Increase timeout for operations that might take longer
{:ok, machine} = ReqFly.Machines.wait(req,
app_name: "my-app",
machine_id: machine_id,
instance_id: instance_id,
state: "started",
timeout: 300 # 5 minutes
)Next Steps
Now that you're familiar with the basics, you can:
- Explore the API Documentation - Visit https://hexdocs.pm/req_fly for detailed module documentation
- Read the Fly.io API Docs - Learn more at https://fly.io/docs/machines/api/
- Check Out Examples - See the README for more comprehensive examples
- Build Something! - Start deploying and managing your applications with ReqFly
Getting Help
- Issues: Report bugs at https://github.com/yourorg/req_fly/issues
- Discussions: Ask questions in GitHub Discussions
- Fly.io Community: Join the Fly.io community forum at https://community.fly.io
Happy coding! 🚀