agentsclimarketplace

Flexport sdk patterns

Skill jeremylongshore/claude-code-plugins-plus-skills/plugins/saas-packs/flexport-pack/skills/flexport-sdk-patterns

'Apply production-ready Flexport API patterns for TypeScript and Python.From its SKILL.md

Install
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill flexport-sdk-patterns

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its file declares

Copied from the file, not written here

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

5.3 KB, ~1.2k tokens by cl100k_base, as published. Nobody here has run it

Flexport SDK Patterns

Overview

Production-ready patterns for the Flexport REST API v2. Since Flexport has no official npm/pip SDK, you build typed HTTP clients. Key patterns: singleton client, paginated iteration, retry wrapper, and Zod response validation.

Instructions

Pattern 1: Singleton Client with Auto-Retry

// src/flexport/client.ts
import { z } from 'zod';

class FlexportClient {
  private static instance: FlexportClient | null = null;
  private base = 'https://api.flexport.com';
  private headers: Record<string, string>;

  private constructor(apiKey: string) {
    this.headers = {
      'Authorization': `Bearer ${apiKey}`,
      'Flexport-Version': '2',
      'Content-Type': 'application/json',
    };
  }

  static getInstance(): FlexportClient {
    if (!this.instance) {
      const key = process.env.FLEXPORT_API_KEY;
      if (!key) throw new Error('Missing FLEXPORT_API_KEY');
      this.instance = new FlexportClient(key);
    }
    return this.instance;
  }

  async request<T>(path: string, options: RequestInit = {}): Promise<T> {
    const res = await fetch(`${this.base}${path}`, { ...options, headers: { ...this.headers, ...options.headers } });
    if (res.status === 429) {
      const retryAfter = parseInt(res.headers.get('Retry-After') || '60');
      await new Promise(r => setTimeout(r, retryAfter * 1000));
      return this.request(path, options);  // Retry once
    }
    if (!res.ok) {
      const body = await res.text();
      throw new FlexportAPIError(res.status, body, path);
    }
    return res.json();
  }
}

class FlexportAPIError extends Error {
  constructor(public status: number, public body: string, public path: string) {
    super(`Flexport ${status} on ${path}: ${body}`);
    this.name = 'FlexportAPIError';
  }
}

Pattern 2: Paginated Iterator

// Iterate all pages of a Flexport list endpoint
async function* paginate<T>(path: string, perPage = 25): AsyncGenerator<T> {
  const client = FlexportClient.getInstance();
  let page = 1;
  while (true) {
    const separator = path.includes('?') ? '&' : '?';
    const res = await client.request<{ data: { records: T[]; total_count: number } }>(
      `${path}${separator}page=${page}&per=${perPage}`
    );
    for (const record of res.data.records) yield record;
    if (res.data.records.length < perPage) break;
    page++;
  }
}

// Usage: iterate all shipments
for await (const shipment of paginate<Shipment>('/shipments')) {
  console.log(shipment.id, shipment.status);
}

Pattern 3: Zod Response Validation

const ShipmentSchema = z.object({
  id: z.string(),
  status: z.enum(['booked', 'in_transit', 'arrived', 'delivered']),
  freight_type: z.enum(['ocean', 'air', 'trucking']),
  origin_port: z.object({ code: z.string(), name: z.string() }),
  destination_port: z.object({ code: z.string(), name: z.string() }),
  cargo_ready_date: z.string(),
  estimated_arrival_date: z.string().nullable(),
});

type Shipment = z.infer<typeof ShipmentSchema>;

async function getShipment(id: string): Promise<Shipment> {
  const client = FlexportClient.getInstance();
  const res = await client.request<{ data: unknown }>(`/shipments/${id}`);
  return ShipmentSchema.parse(res.data);  // Throws ZodError on mismatch
}

Pattern 4: Python Typed Client

import os, requests
from dataclasses import dataclass
from typing import Iterator

@dataclass
class Shipment:
    id: str
    status: str
    freight_type: str

class FlexportClient:
    BASE = 'https://api.flexport.com'

    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update({
            'Authorization': f'Bearer {os.environ["FLEXPORT_API_KEY"]}',
            'Flexport-Version': '2',
        })

    def list_shipments(self, per: int = 25) -> Iterator[Shipment]:
        page = 1
        while True:
            r = self.session.get(f'{self.BASE}/shipments', params={'page': page, 'per': per})
            r.raise_for_status()
            records = r.json()['data']['records']
            for rec in records:
                yield Shipment(id=rec['id'], status=rec['status'], freight_type=rec['freight_type'])
            if len(records) < per:
                break
            page += 1

Error Handling

PatternUse CaseBenefit
SingletonAll API callsOne instance, consistent config
PaginatorList endpointsNo data loss from pagination
Zod validationResponse parsingCatches API contract changes early
Error classAll failuresStructured error data for logging

Resources

Next Steps

Apply patterns in flexport-core-workflow-a for real-world usage.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,144. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.