Agent Skills: Figma Reliability Patterns

|

UncategorizedID: jeremylongshore/claude-code-plugins-plus-skills/figma-reliability-patterns

Install this agent skill to your local

pnpm dlx add-skill https://github.com/jeremylongshore/claude-code-plugins-plus-skills/tree/HEAD/plugins/saas-packs/figma-pack/skills/figma-reliability-patterns

Skill Files

Browse the full folder contents for figma-reliability-patterns.

Download Skill

Loading file tree…

plugins/saas-packs/figma-pack/skills/figma-reliability-patterns/SKILL.md

Skill Metadata

Name
figma-reliability-patterns
Description
|

Figma Reliability Patterns

Overview

Production reliability patterns for Figma REST API integrations. Figma is an external dependency -- your application must handle its outages, rate limits, and slow responses without cascading failures.

Prerequisites

  • Working Figma API integration
  • Understanding of circuit breaker pattern
  • Cache or file system for fallback data

Instructions

Step 1: Circuit Breaker

// Prevent cascading failures when Figma is down
class FigmaCircuitBreaker {
  private failures = 0;
  private lastFailure = 0;
  private state: 'closed' | 'open' | 'half-open' = 'closed';

  constructor(
    private threshold = 5,        // Open after 5 failures
    private resetTimeMs = 30_000  // Try again after 30s
  ) {}

  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.state === 'open') {
      if (Date.now() - this.lastFailure > this.resetTimeMs) {
        this.state = 'half-open';
        console.log('[figma-circuit] State: half-open (testing recovery)');
      } else {
        throw new Error('Figma circuit breaker is OPEN -- failing fast');
      }
    }

    try {
      const result = await fn();
      if (this.state === 'half-open') {
        this.state = 'closed';
        this.failures = 0;
        console.log('[figma-circuit] State: closed (recovered)');
      }
      return result;
    } catch (error) {
      this.failures++;
      this.lastFailure = Date.now();

      if (this.failures >= this.threshold) {
        this.state = 'open';
        console.warn(`[figma-circuit] State: OPEN after ${this.failures} failures`);
      }
      throw error;
    }
  }

  getState() { return this.state; }
}

const figmaBreaker = new FigmaCircuitBreaker();

// Usage
async function safeFigmaCall<T>(fn: () => Promise<T>): Promise<T> {
  return figmaBreaker.execute(fn);
}

Step 2: Cached Fallback

import { readFileSync, writeFileSync, existsSync } from 'fs';

// Serve cached data when Figma is unavailable
class FigmaFallbackCache {
  constructor(private cacheDir = '.figma-cache') {}

  private getPath(key: string) {
    return `${this.cacheDir}/${key.replace(/[^a-zA-Z0-9]/g, '_')}.json`;
  }

  save(key: string, data: any) {
    const { mkdirSync } = require('fs');
    mkdirSync(this.cacheDir, { recursive: true });
    writeFileSync(this.getPath(key), JSON.stringify({
      data,
      cachedAt: new Date().toISOString(),
    }));
  }

  load(key: string): { data: any; cachedAt: string } | null {
    const path = this.getPath(key);
    if (!existsSync(path)) return null;
    return JSON.parse(readFileSync(path, 'utf-8'));
  }
}

const fallbackCache = new FigmaFallbackCache();

async function fetchWithFallback<T>(
  cacheKey: string,
  fetcher: () => Promise<T>
): Promise<{ data: T; fromCache: boolean; cachedAt?: string }> {
  try {
    const data = await safeFigmaCall(fetcher);
    // Update cache with fresh data
    fallbackCache.save(cacheKey, data);
    return { data, fromCache: false };
  } catch (error) {
    console.warn(`Figma unavailable, loading cached ${cacheKey}`);
    const cached = fallbackCache.load(cacheKey);
    if (cached) {
      return { data: cached.data as T, fromCache: true, cachedAt: cached.cachedAt };
    }
    throw new Error(`Figma unavailable and no cached data for ${cacheKey}`);
  }
}

Step 3: Retry with Backoff (Respecting Retry-After)

async function figmaRetry<T>(
  fn: () => Promise<Response>,
  maxRetries = 3
): Promise<T> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const res = await fn();

    if (res.ok) return res.json();

    if (res.status === 429) {
      const retryAfter = parseInt(res.headers.get('Retry-After') || '60');
      if (attempt < maxRetries) {
        console.warn(`429 -- waiting ${retryAfter}s (attempt ${attempt + 1}/${maxRetries})`);
        await new Promise(r => setTimeout(r, retryAfter * 1000));
        continue;
      }
    }

    if (res.status >= 500 && attempt < maxRetries) {
      const delay = Math.min(1000 * Math.pow(2, attempt), 30_000);
      const jitter = Math.random() * 1000;
      await new Promise(r => setTimeout(r, delay + jitter));
      continue;
    }

    throw new FigmaApiError(res.status, await res.text());
  }
  throw new Error('Max retries exceeded');
}

Step 4: Request Timeout

// Prevent requests from hanging indefinitely
async function figmaFetchWithTimeout(
  path: string,
  token: string,
  timeoutMs = 15_000
): Promise<Response> {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), timeoutMs);

  try {
    return await fetch(`https://api.figma.com${path}`, {
      headers: { 'X-Figma-Token': token },
      signal: controller.signal,
    });
  } catch (error) {
    if (error instanceof Error && error.name === 'AbortError') {
      throw new Error(`Figma request timed out after ${timeoutMs}ms: ${path}`);
    }
    throw error;
  } finally {
    clearTimeout(timeout);
  }
}

Step 5: Health-Aware Request Routing

// Only make non-critical Figma calls when the API is healthy
class FigmaHealthTracker {
  private healthy = true;
  private lastCheck = 0;
  private checkIntervalMs = 30_000;

  async isHealthy(token: string): Promise<boolean> {
    if (Date.now() - this.lastCheck < this.checkIntervalMs) {
      return this.healthy;
    }

    try {
      const res = await figmaFetchWithTimeout('/v1/me', token, 5000);
      this.healthy = res.ok;
    } catch {
      this.healthy = false;
    }
    this.lastCheck = Date.now();
    return this.healthy;
  }
}

const healthTracker = new FigmaHealthTracker();

async function conditionalFigmaCall<T>(
  token: string,
  critical: boolean,
  fn: () => Promise<T>,
  fallback: () => Promise<T>
): Promise<T> {
  const healthy = await healthTracker.isHealthy(token);

  if (!healthy && !critical) {
    console.log('Figma unhealthy, using fallback for non-critical call');
    return fallback();
  }

  return fetchWithFallback('default', fn).then(r => r.data);
}

Output

  • Circuit breaker preventing cascading failures
  • Cached fallback serving stale data during outages
  • Retry logic respecting Figma's Retry-After header
  • Request timeouts preventing hung connections
  • Health-aware routing for non-critical calls

Error Handling

| Issue | Cause | Solution | |-------|-------|----------| | Circuit stays open | Threshold too low | Increase threshold or decrease reset time | | Stale fallback data | Cache not refreshed | Refresh cache on successful calls | | Retry loops | Not respecting Retry-After | Always use the header value | | Timeout too short | Large file responses | Increase timeout for /v1/files calls |

Resources

Next Steps

For policy enforcement, see figma-policy-guardrails.