0.1.1 • Published 9 months ago

@fynlink/sdk v0.1.1

Weekly downloads
-
License
MIT
Repository
github
Last release
9 months ago

Fyn SDK

⚠️ BETA VERSION - This SDK is currently in beta and not recommended for production use. APIs and functionality may change without notice.

The privacy-first URL shortener SDK for Node.js and TypeScript.

Installation

npm install @fynlink/sdk

Quick Start

Initialize the SDK:

import { Fyn } from '@fynlink/sdk';

const fyn = new Fyn({
  token: 'token_<your-token>',
  secret: 'secret_<your-secret>' // Required only to view/list or update links
});

Basic Usage

Create a simple link:

const link = await fyn.links.create({
  target: 'https://example.com',
  domain: 'fyn.li'
});

// Response
{
  data: {
    id: 'link_abc123',
    short_url: 'https://fyn.li/xyz789',
    target: {
      default: 'https://example.com',
      ios: null,
      android: null,
      geo: {}
    },
    clicks: 0,
    created_at: '2024-03-15T10:30:00Z'
  }
}

Get a link:

const link = await fyn.links.get('link_abc123');

// Response
{
  data: {
    id: 'link_abc123',
    short_url: 'https://fyn.li/xyz789',
    target: {
      default: 'https://example.com',
      ios: null,
      android: null,
      geo: {}
    },
    clicks: 42,
    created_at: '2024-03-15T10:30:00Z',
    updated_at: '2024-03-15T10:30:00Z'
  }
}

List links:

const links = await fyn.links.list();

// Response
{
  data: [/* array of link objects */],
  meta: {
    current_page: 1,
    from: 1,
    to: 10,
    last_page: 5,
    per_page: 10,
    total: 42
  }
}

Delete a link:

await fyn.links.delete('link_abc123');

// Response
{
  data: null
}

Advanced Usage

SDK Configuration

const fyn = new Fyn({
  token: 'token_<your-base64-encoded-token>',
  secret: 'your-secret-key',
  baseUrl: 'https://api.fyn.link/v1',
  timeout: 10000, // 10 seconds
  maxRetries: 3,
  retryDelay: 1000,
  debug: true
});

Automatic Retries

The SDK includes a smart retry mechanism for handling transient failures:

// Retry behavior
- Only retries on server errors (500-504)
- No retries on client errors (400-499)
- Maximum of 1 retry with 1-second delay
- Respects rate limits (429 responses)
- Immediate failure on permission errors (403)

// Example: Server error with retry
try {
  const link = await fyn.links.get('link_123');
} catch (error) {
  if (error.status >= 500) {
    // The SDK already attempted one retry after 1 second
    console.error('Server error persisted after retry');
  }
}

// Example: Rate limit handling
try {
  const link = await fyn.links.get('link_123');
} catch (error) {
  if (error instanceof RateLimitError) {
    console.log('Rate limit exceeded');
    console.log('Reset at:', error.details.reset);
    // No automatic retries on rate limits
  }
}

Caching Strategy

The SDK implements a sophisticated caching system:

// Cache features
- In-memory cache with 5-minute TTL by default
- Supports stale-while-revalidate pattern
- Respects Cache-Control headers
- Automatic background revalidation
- Cache invalidation on write operations

// Example: Cache behavior
const link = await fyn.links.get('link_123');
// First request: Makes API call, caches response

const sameLink = await fyn.links.get('link_123');
// Subsequent request within TTL: Returns cached data

// Cache statistics
const metrics = fyn.links.getMetrics();
console.log('Cache hit ratio:', metrics.cache.hitRatio);
console.log('Cache hits:', metrics.cache.hits);
console.log('Cache misses:', metrics.cache.misses);

// Manual cache control
fyn.links.clearCache(); // Clear all cached data

Caching and ETags

The SDK automatically handles caching using ETags for improved performance:

// First request - will fetch data from server
const link = await fyn.links.get('link_abc123');

// Subsequent requests - will use cached data if not modified
const sameLink = await fyn.links.get('link_abc123');
// If the resource hasn't changed, returns cached data without making a full request

// Clear cache if needed
fyn.links.clearCache(); // Clears both response cache and ETags

Response Headers:

// First request
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

// Subsequent request
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

When the resource hasn't changed, the server will respond with 304 Not Modified, and the SDK will automatically use the cached data. This reduces bandwidth usage and improves response times.

Creating Links with Advanced Options

const link = await fyn.links.create({
  target: 'https://example.com',
  domain: 'fyn.li',
  title: 'My Example Link',
  notes: 'Important link for documentation',
  tags: ['docs', 'example'],
  privateLink: true,
  safeMode: true,
  password: 'secretpass123',
  expiry: 86400, // 24 hours in seconds
  iosLink: 'https://apps.apple.com/app/myapp',
  androidLink: 'https://play.google.com/store/apps/myapp',
  geoTargets: {
    US: 'https://us.example.com',
    UK: 'https://uk.example.com'
  }
});

// Response
{
  data: {
    id: 'link_abc123',
    short_url: 'https://fyn.li/xyz789',
    target: {
      default: 'https://example.com',
      ios: 'https://apps.apple.com/app/myapp',
      android: 'https://play.google.com/store/apps/myapp',
      geo: {
        US: 'https://us.example.com',
        UK: 'https://uk.example.com'
      }
    },
    title: 'My Example Link',
    notes: 'Important link for documentation',
    tags: ['docs', 'example'],
    is_private: true,
    safe_mode: true,
    password_enabled: true,
    clicks: 0,
    created_at: '2024-03-15T10:30:00Z',
    updated_at: '2024-03-15T10:30:00Z',
    expire_at: '2024-03-16T10:30:00Z',
    tracking: 'FULL_TRACKING'
  }
}

Listing Links with Filters and Sorting

import { SortBy, SortOrder, LinkStatus } from 'fyn';

const links = await fyn.links.list({
  page: 1,
  limit: 20,
  sortBy: SortBy.CreatedAt,
  sortOrder: SortOrder.Desc,
  filters: [
    {
      is_private: true,
      safe_mode: true,
      status: LinkStatus.Active
    }
  ]
});

// Response
{
  data: [/* array of link objects */],
  meta: {
    current_page: 1,
    from: 1,
    to: 20,
    last_page: 5,
    per_page: 20,
    total: 100
  }
}

Error Handling

The SDK throws typed errors that you can catch and handle:

import { 
  FynError, 
  ValidationError, 
  AuthError, 
  NotFoundError, 
  RateLimitError,
  TimeoutError
} from 'fyn';

try {
  const link = await fyn.links.get('invalid_id');
} catch (error) {
  if (error instanceof ValidationError) {
    // Handle validation errors (400)
    console.error('Invalid parameters:', error.message);
  } else if (error instanceof AuthError) {
    // Handle authentication errors (401)
    console.error('Authentication failed:', error.message);
  } else if (error instanceof NotFoundError) {
    // Handle not found errors (404)
    console.error('Link not found:', error.message);
  } else if (error instanceof RateLimitError) {
    // Handle rate limit errors (429)
    console.error('Rate limit exceeded:', error.message);
    console.log('Reset at:', error.details?.reset);
  } else if (error instanceof TimeoutError) {
    // Handle timeout errors (408)
    console.error('Request timed out:', error.message);
    console.log('Timeout setting:', error.details?.timeout);
  } else if (error instanceof FynError) {
    // Handle other API errors
    console.error('API error:', error.message);
    console.log('Status:', error.status);
    console.log('Details:', error.details);
  } else {
    // Handle unexpected errors
    console.error('Unexpected error:', error);
  }
}

Error Response Examples:

// Validation Error
{
  error: {
    message: 'Invalid parameters',
    status: 400,
    details: {
      validation: {
        target: ['Target URL is required']
      }
    }
  }
}

// Authentication Error
{
  error: {
    message: 'Authentication failed',
    status: 401,
    details: {
      reason: 'Invalid or expired token'
    }
  }
}

// Not Found Error
{
  error: {
    message: 'Link not found',
    status: 404
  }
}

// Rate Limit Error
{
  error: {
    message: 'Rate limit exceeded',
    status: 429,
    details: {
      remaining: 0,
      reset: 1678892400,
      limit: 100
    }
  }
}

// Timeout Error
{
  error: {
    message: 'Request timed out after 10000ms',
    status: 408,
    details: {
      operation: 'get link',
      timeout: 10000
    }
  }
}

Performance Metrics

The SDK includes built-in performance monitoring:

// Get metrics for the last 5 minutes
const metrics = fyn.links.getMetrics(5);

// Response
{
  totalRequests: 150,
  averageResponseTime: 245.5,
  successRate: 99.3,
  rateLimit: {
    remaining: 850,
    reset: 1678892400,
    limit: 1000
  },
  cache: {
    hits: 45,
    misses: 15,
    hitRatio: 75.0
  }
}

For detailed API documentation, visit our documentation.