🛡️ Resilience System

Built-in retry logic, rate limiting, and fallback mechanisms for reliable API operations.

Welcome to the Dymo API Resilience System documentation. This powerful feature ensures your applications remain stable and responsive even when facing network issues, API rate limits, or temporary service disruptions.

About the Resilience System

What is the Resilience System?

The Resilience System is a comprehensive error-handling and recovery mechanism built into all Dymo API SDKs. It provides automatic retry logic, intelligent rate limiting, and fallback data generation to ensure your applications continue functioning smoothly even under challenging conditions.

Key Features

  • 🔄 Automatic Retries: Configurable retry attempts with exponential backoff
  • 🚦 Rate Limiting: Intelligent tracking and management of API rate limits
  • 🛡️ Fallback Data: Automatic generation of safe fallback responses when API is unavailable
  • 📊 Client Tracking: Per-client rate limit tracking and management
  • ⚡ Smart Error Handling: Differentiates between retry-worthy and non-retry-worthy errors

Configuration

The resilience system can be configured when instantiating any Dymo API client:

Basic Configuration

const dymo = new DymoAPI({
    apiKey: "",
    resilience: {
        fallbackEnabled: true,    // Enable fallback data generation
        retryAttempts: 2,         // Number of additional retry attempts
        retryDelay: 1000          // Base delay in milliseconds
    }
});

Configuration Parameters

ParameterTypeDefaultDescription
fallbackEnabledbooleanfalseWhether to use fallback data when API fails
retryAttemptsnumber2Number of additional retry attempts beyond the normal attempt (0 = no retries)
retryDelaynumber1000Base delay in milliseconds for exponential backoff

Important: retryAttempts refers to the number of additional retries, not total attempts.
Example: retryAttempts: 2 = 1 normal attempt + 2 retries = 3 total attempts.

Usage Examples

Main SDK Usage

import DymoAPI from "dymo-api";

const dymo = new DymoAPI({
  apiKey: "",
  resilience: {
      fallbackEnabled: true,
      retryAttempts: 3,
      retryDelay: 1000
  }
});

// This will automatically retry on failure and use fallback if needed
try {
  const result = await dymo.isValidEmail("[email protected]");
  console.log("Success:", result.allow);
} catch (error) {
  console.log("All attempts failed:", error.message);
}

Better Auth SDK Usage

import { betterAuth } from "better-auth";
import { dymoEmailPlugin } from "dymo-api-better-auth";

const auth = betterAuth({
  plugins: [
      dymoEmailPlugin({
          apiKey: "",
          emailRules: { deny: ["FRAUD", "INVALID"] },
          resilience: {
              fallbackEnabled: true,
              retryAttempts: 2,
              retryDelay: 500
          }
      })
  ]
});

// The plugin will automatically handle retries and fallbacks
// when validating emails during authentication

Advanced Configuration

Exponential Backoff

The resilience system uses exponential backoff for retries. With a base delay of 1000ms and 2 retry attempts:

  • Attempt 1: No delay (original request)
  • Attempt 2: 1000ms delay (1000 × 2^0)
  • Attempt 3: 2000ms delay (1000 × 2^1)

Total Attempts = 1 normal + N retries
Example: retryAttempts: 2 = 1 normal + 2 retries = 3 total attempts

Rate Limiting Behavior

When the API returns a 429 (Too Many Requests) status code:

  1. Automatic Wait: The system reads the retry-after header and waits the specified time
  2. No Retries: Rate-limited requests are not retried to avoid worsening the situation
  3. Client Tracking: Rate limits are tracked per client (identified by API key, rootApiKey, or "anonymous")
  4. Header Tracking: Monitors rate limit headers:
    • X-Ratelimit-Limit-Requests: Total requests per minute
    • X-Ratelimit-Remaining-Requests: Remaining requests
    • X-Ratelimit-Reset-Requests: Time until quota resets
    • retry-after: Wait time on 429 responses

Rate limited requests are handled separately and never count towards retry attempts. The system will wait the required time instead of retrying.

Fallback Data Generation

When fallbackEnabled: true and all retries fail, the system generates safe fallback responses:

Production Configuration

For production environments, consider these settings:

const productionConfig = {
    fallbackEnabled: true,     // Keep users working during outages
    retryAttempts: 2,         // 1 normal + 2 retries = 3 total attempts
    retryDelay: 500          // Faster retries for better UX
};

Development Configuration

For development environments:

const developmentConfig = {
    fallbackEnabled: false,    // See actual errors during development
    retryAttempts: 0,         // Only normal attempt, no retries
    retryDelay: 200          // Quick retries if enabled
};

No Retries Configuration

For real-time systems where retries cause delays:

const noRetriesConfig = {
    fallbackEnabled: false,    // Fail fast
    retryAttempts: 0           // No retries, only single attempt
};

High Reliability Configuration

For critical applications needing maximum reliability:

const highReliabilityConfig = {
    fallbackEnabled: true,     // Always provide fallback
    retryAttempts: 3,         // 1 normal + 3 retries = 4 total attempts
    retryDelay: 500           // Moderate retry speed
};

Monitoring and Logging

Monitor the console for resilience warnings. They provide valuable insights into:

  • Network connectivity issues
  • API rate limiting
  • Service degradation
  • Fallback data usage

Common log messages to watch for:

[WARN] [Dymo API] Client xxx is rate limited. Waiting 60 seconds...
[WARN] [Dymo API] Attempt 1 failed. Retrying in 1000ms...
[WARN] [Dymo API] Request failed after 3 attempts. Using fallback data.
[WARN] [Dymo API] Rate limited. Waiting 30 seconds (no retries)

Performance Considerations

Impact on Response Time

  • First Request: Normal response time
  • Retry Attempts: Add exponential backoff delays
  • Rate Limit Waits: Can add significant delays (up to minutes)
  • Fallback Generation: Minimal overhead (< 10ms)

Memory Usage

  • Rate Limit Tracking: Stores rate limit info per client
  • Client Identification: Uses apiKey, rootApiKey, or "anonymous" as client ID
  • Automatic Cleanup: Expired entries removed after 5 minutes
  • Minimal Overhead: < 1KB per active client

Rate Limit Headers Tracked

The system automatically tracks these headers per client:

HeaderPurpose
X-Ratelimit-Limit-RequestsTotal requests per minute
X-Ratelimit-Remaining-RequestsRemaining requests in current window
X-Ratelimit-Reset-RequestsTime until quota resets
retry-afterWait time for 429 responses

Rate limit tracking is per client - each API key or unique identifier maintains its own rate limit state.

When to Disable Resilience

Consider disabling resilience in these scenarios:

  • Real-time Systems: Where fallback data is unacceptable
  • Critical Security: Where false positives are dangerous
  • High-Performance: Where every millisecond counts
  • Debugging: When you need to see actual API errors

Behavior Flow

  1. Check Rate Limit Status: If client is rate limited, wait for retry-after header value
  2. Make Request: Attempt the API call (Attempt 1 - normal attempt)
  3. Update Rate Limit Tracking: Parse and store rate limit headers for future requests
  4. Handle Response:
    • 2xx (Success): Return response immediately
    • 429 (Rate Limited): Wait for retry-after header, do not retry
    • 5xx (Server Error): Retry with exponential backoff
    • 4xx (Client Error): Do not retry, return error immediately
    • Network Error: Retry with exponential backoff
  5. Retry Logic: For each retry attempt:
    • Calculate delay: retryDelay × 2^(attempt-1)
    • Wait the calculated time
    • Make new request attempt
  6. Fallback: If all additional retries fail and fallbackEnabled is true, use fallback data
  7. Final Failure: If no fallback enabled, throw the last error

Example Flow with retryAttempts: 2 and retryDelay: 1000:
• Attempt 1: Normal request (no delay)
• If fails: Wait 1000ms → Attempt 2
• If fails again: Wait 2000ms → Attempt 3

• If all fail: Use fallback or throw error

Migration Guide

From Basic Client

// Before (no resilience)
const dymo = new DymoAPI({ apiKey: "" });

// After (with resilience)
const dymo = new DymoAPI({
    apiKey: "",
    resilience: {
        fallbackEnabled: true,
        retryAttempts: 2,    // 1 normal + 2 retries = 3 total attempts
        retryDelay: 1000
    }
});

From Custom Retry Logic

// Before (manual retry)
async function validateEmail(email) {
    for (let i = 0; i < 3; i++) {
        try {
            return await client.isValidEmail(email);
        } catch (error) {
            if (i === 2) throw error;
            await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
        }
    }
}

// After (built-in resilience)
async function validateEmail(email) {
    return await client.isValidEmail(email); // Automatic retries and fallbacks
}

Migration from Previous Version

The retryAttempts parameter has changed in the updated version:
Before: Total number of attempts (max 3)
After: Number of additional retries beyond normal attempt

To maintain the same behavior:

// Previous behavior (3 total attempts max)
resilience: { retryAttempts: 2 }  // Was: 1 normal + 2 retries = 3 total

// Same behavior now
resilience: { retryAttempts: 1 }  // Now: 1 normal + 1 retry = 2 total

// For 3 total attempts like before
resilience: { retryAttempts: 2 }  // Now: 1 normal + 2 retries = 3 total

Troubleshooting

Common Issues

Support

If you encounter issues with the resilience system:

  1. Check Logs: Review console warnings for clues
  2. Test Network: Verify internet connectivity
  3. Verify API Key: Ensure your key is valid and active
  4. Monitor Status: Check the API Status Page
  5. Contact Support: Reach out with your logs and configuration

When contacting support about resilience issues, please include:

  • Your resilience configuration
  • Relevant console logs
  • Approximate time of issues
  • Your API key (last 4 characters only)