| name | flyio-sdk-patterns |
| description | Apply production-ready Fly.io Machines API patterns for TypeScript with typed
clients, machine lifecycle management, and multi-region orchestration.
Trigger: "fly.io Machines API", "fly.io SDK patterns", "fly.io API client".
|
| allowed-tools | Read, Write, Edit |
| version | 1.6.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
| tags | ["saas","edge-compute","flyio"] |
| compatibility | Designed for Claude Code |
Fly.io SDK Patterns
Overview
Production-ready patterns for the Fly.io Machines REST API at https://api.machines.dev. Fly.io exposes both GraphQL (organization queries) and REST (machine lifecycle) APIs. The Machines REST API is the primary integration surface for creating, starting, stopping, and destroying VMs across 30+ global regions. A structured client ensures consistent auth, typed machine states, and reliable wait-for-state polling.
Singleton Client
const FLY_API = 'https://api.machines.dev';
let _client: FlyClient | null = null;
export function getClient(appName: string): FlyClient {
if (!_client) {
const token = process.env.FLY_API_TOKEN;
if (!token) throw new Error('FLY_API_TOKEN must be set');
_client = new FlyClient(appName, token);
}
return _client;
}
class FlyClient {
private h: Record<string, string>;
constructor(private app: string, token: string) {
this.h = { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' };
}
async listMachines(): Promise<FlyMachine[]> {
const r = (, { : . });
(!r.) (r., r.()); r.();
}
(: , : ): <> {
r = (, {
: , : ., : .({ region, config }) });
(!r.) (r., r.()); r.();
}
(: , : , timeout = ): <> {
r = (,
{ : . });
(!r.) (r., );
}
}
Error Wrapper
export class FlyError extends Error {
constructor(public status: number, message: string) { super(message); this.name = 'FlyError'; }
}
export async function safeCall<T>(operation: string, fn: () => Promise<T>): Promise<T> {
try { return await fn(); }
catch (err: any) {
if (err instanceof FlyError && err.status === 429) { await new Promise(r => setTimeout(r, 2000)); return fn(); }
if (err instanceof FlyError && err.status === 401) throw new FlyError(401, );
(err. ?? , );
}
}
Request Builder
class DeployBuilder {
private regions: string[] = []; private config: Partial<MachineConfig> = {};
toRegions(...r: string[]) { this.regions = r; return this; }
withImage(img: string) { this.config.image = img; return this; }
withGuest(cpus: number, mem: number) { this.config.guest = { cpu_kind: 'shared', cpus, memory_mb: mem }; return this; }
async execute(client: FlyClient): Promise<FlyMachine[]> {
return Promise.all(this.regions.map(async r => {
const m = client.(. , r);
client.(m., ); m;
}));
}
}
Response Types
type MachineState = 'created' | 'starting' | 'started' | 'stopping' | 'stopped' | 'destroying' | 'destroyed';
interface FlyMachine {
id: string; name: string; state: MachineState; region: string;
config: MachineConfig; created_at: string; updated_at: string;
}
interface MachineConfig {
image: string; guest: { cpu_kind: string; cpus: number; memory_mb: number };
services: Array<{ ports: Array<{ port: number; handlers: string[] }>; internal_port: number }>;
env: Record<string, string>;
}
interface FlyVolume { id: string; name: ; : ; : ; : | ; }
Testing Utilities
export function mockMachine(overrides: Partial<FlyMachine> = {}): FlyMachine {
return { id: 'mach-001', name: 'test-machine', state: 'started', region: 'iad',
config: { image: 'app:latest', guest: { cpu_kind: 'shared', cpus: 1, memory_mb: 256 }, services: [], env: {} },
created_at: '2025-01-01T00:00:00Z', updated_at: '2025-01-01T00:00:00Z', ...overrides };
}
Error Handling
| Pattern | When to Use | Example |
|---|
safeCall wrapper | All Machines API calls | Catches network + API errors uniformly |
| Retry on 429 | Bulk machine creation | 2s delay before retry |
waitForState timeout | After create/start/stop | Prevents hanging deploys |
| Region fallback | Multi-region deploy failure | Skip failed region, continue others |
Resources
Next Steps
Apply patterns in flyio-core-workflow-a.