Node.js TypeScript Client

A complete TypeScript API client for Flash Americas ezship API with real production examples, cross-border shipping, and location services. This example is based on our working integration that has been tested with live production data.

Overview

This client provides:

  • Real production quotes from 25+ carriers (FedEx, UPS, USPS, LTL)
  • Cross-border shipping between US and Mexico
  • Location services with ZIP code lookup and address validation
  • SAT code lookup for Mexican customs compliance
  • Complete TypeScript support with accurate type definitions
  • Error handling for production reliability

Installation

Install dependenciesbash
npm install axios @types/node
# or
yarn add axios @types/node

Type Definitions

src/types.tstypescript
export interface Address {
address1: string;
address2?: string;
city: string;
state: string;
postalCode: string;
country: string;
company?: string;
contact?: string;
phone?: string;
email?: string;
}

export interface Cargo {
weight: number;
length: number;
width: number;
height: number;
quantity: number;
description: string;
value: number;
hazmat?: boolean;
packaging?: string;
nmfc?: string;
satCode?: string;
}

export interface QuoteRequest {
originAddress: Address;
destinationAddress: Address;
cargo: Cargo[];
shipDate: string;
returnJson: boolean;
accessorials?: string[];
}

export interface Rate {
id: string;
provider: string;
service: string;
cost: number;
currency: string;
transitDays: number;
deliveryDate: string;
estimated: boolean;
breakdown: {
  baseCost: number;
  fuelSurcharge: number;
  accessorials: Record<string, number>;
};
}

export interface QuoteResponse {
success: boolean;
data: {
  quotes: Rate[];
  total: number;
};
}

export interface LocationResult {
zip: string;
city: string;
state: string;
full_state: string;
country: string;
county?: string;
lat?: number;
lng?: number;
}

export interface LocationResponse {
success: boolean;
data: LocationResult[];
total: number;
search_type: string;
}

export interface SATCode {
sat_code: string;
description_es: string;
description_en: string;
category: string;
subcategory: string;
}

export interface SATCodeResponse {
success: boolean;
data: SATCode[];
total: number;
}

export interface ClientConfig {
apiKey: string;
baseUrl?: string;
timeout?: number;
}

Client Implementation

src/client.tstypescript
import axios, { AxiosInstance } from 'axios';
import {
ClientConfig,
QuoteRequest,
QuoteResponse,
LocationResponse,
SATCodeResponse,
Address
} from './types';

export class FlashAmericasClient {
private http: AxiosInstance;
private config: ClientConfig;

constructor(config: ClientConfig) {
  this.config = {
    baseUrl: 'https://ship.flashamericas.com/api/v1',
    timeout: 30000,
    ...config
  };

  this.http = axios.create({
    baseURL: this.config.baseUrl,
    timeout: this.config.timeout,
    headers: {
      'Authorization': `Bearer ${this.config.apiKey}`,
      'Content-Type': 'application/json',
      'Accept': 'application/json'
    }
  });

  // Add response interceptor for error handling
  this.http.interceptors.response.use(
    (response) => response,
    (error) => {
      console.error('API Error:', error.response?.data || error.message);
      throw error;
    }
  );
}

/**
 * Get shipping quotes from multiple carriers
 */
async getQuote(request: QuoteRequest): Promise<QuoteResponse> {
  const response = await this.http.post('/quote', {
    ...request,
    returnJson: true
  });
  return response.data;
}

/**
 * Search locations by ZIP code or city name
 */
async searchLocations(location: string, country?: string): Promise<LocationResponse> {
  const endpoint = country 
    ? `/location/search/${country}/${encodeURIComponent(location)}`
    : `/location/search/${encodeURIComponent(location)}`;
  
  const response = await this.http.get(endpoint);
  return response.data;
}

/**
 * Validate and correct a shipping address
 */
async validateAddress(address: Partial<Address>): Promise<any> {
  const response = await this.http.post('/address/validate', address);
  return response.data;
}

/**
 * Search SAT codes for Mexican tax classification
 */
async searchSATCodes(searchTerm: string, language: 'en' | 'es' = 'en'): Promise<SATCodeResponse> {
  const response = await this.http.get(`/sat-code/lookup/${language}/${encodeURIComponent(searchTerm)}`);
  return response.data;
}
}

Usage Examples

Basic Quote Request

examples/basic-quote.tstypescript
import { FlashAmericasClient } from '../src/client';

const client = new FlashAmericasClient({
apiKey: process.env.FLASH_AMERICAS_API_KEY!
});

async function getDomesticQuote() {
try {
  const quote = await client.getQuote({
    originAddress: {
      address1: "123 Main Street",
      city: "Dallas",
      state: "TX",
      postalCode: "75201",
      country: "US"
    },
    destinationAddress: {
      address1: "456 Oak Avenue",
      city: "Austin",
      state: "TX", 
      postalCode: "78701",
      country: "US"
    },
    cargo: [{
      weight: 25,
      length: 18,
      width: 12,
      height: 10,
      quantity: 1,
      description: "Electronics",
      value: 1500
    }],
    shipDate: "2025-03-01",
    returnJson: true
  });

  console.log(`Found ${quote.data.quotes.length} rates:`);
  quote.data.quotes.forEach(rate => {
    console.log(`${rate.provider} ${rate.service}: $${rate.cost} (${rate.transitDays} days)`);
  });

  return quote;
} catch (error) {
  console.error('Quote request failed:', error);
  throw error;
}
}

// Run the example
getDomesticQuote();

Cross-Border Shipping

examples/cross-border.tstypescript
import { FlashAmericasClient } from '../src/client';

const client = new FlashAmericasClient({
apiKey: process.env.FLASH_AMERICAS_API_KEY!
});

async function getCrossBorderQuote() {
try {
  // First, find appropriate SAT code
  const satCodes = await client.searchSATCodes('plastic', 'en');
  const satCode = satCodes.data[0]?.sat_code;

  const quote = await client.getQuote({
    originAddress: {
      address1: "3300 NE 192nd St",
      city: "Aventura",
      state: "FL",
      postalCode: "33180",
      country: "US"
    },
    destinationAddress: {
      address1: "Av. Javier Rojo GΓ³mez No. 278",
      city: "CDMX",
      state: "CDMX",
      postalCode: "09300", 
      country: "MX"
    },
    cargo: [{
      weight: 500,
      length: 40,
      width: 30,
      height: 20,
      quantity: 1,
      description: "Plastic components",
      value: 2500,
      satCode: satCode // Required for Mexico
    }],
    shipDate: "2025-03-01",
    returnJson: true
  });

  console.log('Cross-border quote results:');
  quote.data.quotes.forEach(rate => {
    console.log(`${rate.provider}: $${rate.cost} (${rate.transitDays} business days)`);
  });

  return quote;
} catch (error) {
  console.error('Cross-border quote failed:', error);
  throw error;
}
}

getCrossBorderQuote();

Location Services

examples/location-services.tstypescript
import { FlashAmericasClient } from '../src/client';

const client = new FlashAmericasClient({
apiKey: process.env.FLASH_AMERICAS_API_KEY!
});

async function demonstrateLocationServices() {
try {
  // ZIP code autocomplete
  console.log('πŸ” ZIP Code Autocomplete:');
  const zipResults = await client.searchLocations('752');
  zipResults.data.slice(0, 5).forEach(location => {
    console.log(`  ${location.zip} - ${location.city}, ${location.state}`);
  });

  // City search
  console.log('\nπŸ™οΈ City Search:');
  const cityResults = await client.searchLocations('MIAMI', 'US');
  cityResults.data.slice(0, 3).forEach(location => {
    console.log(`  ${location.city}, ${location.state} ${location.zip}`);
  });

  // Address validation
  console.log('\nβœ… Address Validation:');
  const validation = await client.validateAddress({
    street1: "1600 Pennsylvania Avenue",
    city: "Washington",
    state: "DC",
    zip: "20500",
    country: "US"
  });
  
  if (validation.success) {
    console.log('Address validated successfully');
    if (validation.requires_correction) {
      console.log('Suggested correction:', validation.validated_address);
    }
  }

} catch (error) {
  console.error('Location services error:', error);
}
}

demonstrateLocationServices();

SAT Code Lookup

examples/sat-codes.tstypescript
import { FlashAmericasClient } from '../src/client';

const client = new FlashAmericasClient({
apiKey: process.env.FLASH_AMERICAS_API_KEY!
});

async function demonstrateSATCodes() {
try {
  // Search for SAT codes in English
  const englishResults = await client.searchSATCodes('electronics', 'en');
  console.log('Electronics SAT Codes (English):');
  englishResults.data.slice(0, 3).forEach(code => {
    console.log(`  ${code.sat_code}: ${code.description_en}`);
  });

  // Search for SAT codes in Spanish
  const spanishResults = await client.searchSATCodes('componentes', 'es');
  console.log('\nComponentes SAT Codes (EspaΓ±ol):');
  spanishResults.data.slice(0, 3).forEach(code => {
    console.log(`  ${code.sat_code}: ${code.description_es}`);
  });

  // Auto parts example
  const autoResults = await client.searchSATCodes('auto parts', 'en');
  console.log('\nAuto Parts SAT Codes:');
  autoResults.data.slice(0, 3).forEach(code => {
    console.log(`  ${code.sat_code}: ${code.description_en}`);
    console.log(`    Category: ${code.category} β†’ ${code.subcategory}`);
  });

} catch (error) {
  console.error('SAT code search error:', error);
}
}

demonstrateSATCodes();

Complete Integration Example

examples/complete-workflow.tstypescript
import { FlashAmericasClient } from '../src/client';

const client = new FlashAmericasClient({
apiKey: process.env.FLASH_AMERICAS_API_KEY!
});

async function completeShippingWorkflow() {
try {
  console.log('πŸš€ Complete Flash Americas API Workflow');
  console.log('=====================================\n');

  // Step 1: Validate origin address
  console.log('πŸ“ Step 1: Validate Origin Address');
  const originValidation = await client.validateAddress({
    street1: "123 Main Street",
    city: "Dallas",
    state: "TX",
    zip: "75201",
    country: "US"
  });
  console.log('Origin address validated:', originValidation.success);

  // Step 2: Search destination location
  console.log('\nπŸ” Step 2: Search Destination');
  const locations = await client.searchLocations('78701');
  const destinationLocation = locations.data[0];
  console.log(`Found destination: ${destinationLocation.city}, ${destinationLocation.state}`);

  // Step 3: Find SAT code for product (if shipping to Mexico)
  console.log('\nπŸ“‹ Step 3: Find SAT Code');
  const satCodes = await client.searchSATCodes('electronics', 'en');
  const selectedSatCode = satCodes.data[0];
  console.log(`Selected SAT code: ${selectedSatCode.sat_code} - ${selectedSatCode.description_en}`);

  // Step 4: Get shipping quotes
  console.log('\nπŸ’° Step 4: Get Shipping Quotes');
  const quote = await client.getQuote({
    originAddress: {
      address1: "123 Main Street",
      city: "Dallas",
      state: "TX",
      postalCode: "75201",
      country: "US"
    },
    destinationAddress: {
      address1: "456 Oak Avenue", 
      city: destinationLocation.city,
      state: destinationLocation.state,
      postalCode: destinationLocation.zip,
      country: "US"
    },
    cargo: [{
      weight: 25,
      length: 18,
      width: 12,
      height: 10,
      quantity: 1,
      description: "Electronics",
      value: 1500,
      satCode: selectedSatCode.sat_code
    }],
    shipDate: "2025-03-01",
    returnJson: true
  });

  console.log(`\nβœ… Found ${quote.data.quotes.length} shipping options:`);
  quote.data.quotes.slice(0, 3).forEach((rate, index) => {
    console.log(`  ${index + 1}. ${rate.provider} ${rate.service}`);
    console.log(`     Cost: $${rate.cost} | Transit: ${rate.transitDays} days`);
    console.log(`     Delivery: ${rate.deliveryDate}`);
  });

  // Step 5: Select best option
  const bestRate = quote.data.quotes.reduce((prev, current) => 
    (prev.cost < current.cost) ? prev : current
  );
  
  console.log(`\n🎯 Best Option: ${bestRate.provider} ${bestRate.service} - $${bestRate.cost}`);
  
  return {
    validation: originValidation,
    destination: destinationLocation,
    satCode: selectedSatCode,
    quote: quote,
    recommendation: bestRate
  };

} catch (error) {
  console.error('Workflow failed:', error);
  throw error;
}
}

// Run the complete workflow
completeShippingWorkflow()
.then(result => {
  console.log('\nπŸŽ‰ Workflow completed successfully!');
  console.log('Ready to integrate with your shipping process.');
})
.catch(error => {
  console.error('Workflow failed:', error);
});

Error Handling

src/error-handling.tstypescript
import { AxiosError } from 'axios';

export class FlashAmericasError extends Error {
public status: number;
public code: string;
public details?: any;

constructor(message: string, status: number, code: string, details?: any) {
  super(message);
  this.name = 'FlashAmericasError';
  this.status = status;
  this.code = code;
  this.details = details;
}
}

export function handleAPIError(error: AxiosError): never {
if (error.response) {
  const { status, data } = error.response;
  throw new FlashAmericasError(
    data?.error?.message || 'API request failed',
    status,
    data?.error?.code || 'UNKNOWN_ERROR',
    data?.error?.details
  );
} else if (error.request) {
  throw new FlashAmericasError(
    'Network error - no response received',
    0,
    'NETWORK_ERROR'
  );
} else {
  throw new FlashAmericasError(
    'Request configuration error',
    0,
    'CONFIG_ERROR'
  );
}
}

// Usage in client:
export class FlashAmericasClient {
// ... existing code ...

private async makeRequest<T>(config: any): Promise<T> {
  try {
    const response = await this.http.request(config);
    return response.data;
  } catch (error) {
    if (error.response?.status === 401) {
      throw new FlashAmericasError(
        'Invalid API key',
        401,
        'AUTHENTICATION_ERROR'
      );
    } else if (error.response?.status === 429) {
      throw new FlashAmericasError(
        'Rate limit exceeded',
        429,
        'RATE_LIMIT_ERROR'
      );
    }
    throw handleAPIError(error as AxiosError);
  }
}
}

Environment Configuration

src/config.tstypescript
export interface Environment {
apiKey: string;
baseUrl: string;
timeout: number;
}

export const environments: Record<string, Environment> = {
production: {
  apiKey: process.env.FLASH_AMERICAS_API_KEY!,
  baseUrl: 'https://ship.flashamericas.com/api/v1',
  timeout: 30000
},
// Contact support for sandbox access
development: {
  apiKey: process.env.FLASH_AMERICAS_API_KEY!,
  baseUrl: 'https://ship.flashamericas.com/api/v1',
  timeout: 30000
}
};

export function getEnvironment(env: string = 'production'): Environment {
const config = environments[env];
if (!config) {
  throw new Error(`Unknown environment: ${env}`);
}
if (!config.apiKey) {
  throw new Error('FLASH_AMERICAS_API_KEY environment variable is required');
}
return config;
}

Testing

tests/client.test.tstypescript
import { FlashAmericasClient } from '../src/client';

// Mock API responses for testing
const mockQuoteResponse = {
success: true,
data: {
  quotes: [
    {
      id: 'test-rate-1',
      provider: 'TEST_CARRIER',
      service: 'TEST_GROUND',
      cost: 12.45,
      currency: 'USD',
      transitDays: 1,
      deliveryDate: '2025-03-02',
      estimated: true,
      breakdown: {
        baseCost: 10.95,
        fuelSurcharge: 1.50,
        accessorials: {}
      }
    }
  ],
  total: 1
}
};

describe('FlashAmericasClient', () => {
let client: FlashAmericasClient;

beforeEach(() => {
  client = new FlashAmericasClient({
    apiKey: 'test-api-key'
  });
});

test('should create client with default config', () => {
  expect(client).toBeInstanceOf(FlashAmericasClient);
});

test('should get quotes successfully', async () => {
  // Mock the HTTP request
  jest.spyOn(client['http'], 'post').mockResolvedValue({
    data: mockQuoteResponse
  });

  const result = await client.getQuote({
    originAddress: {
      address1: "123 Test St",
      city: "Dallas",
      state: "TX",
      postalCode: "75201",
      country: "US"
    },
    destinationAddress: {
      address1: "456 Test Ave",
      city: "Austin",
      state: "TX",
      postalCode: "78701",
      country: "US"
    },
    cargo: [{
      weight: 25,
      length: 18,
      width: 12,
      height: 10,
      quantity: 1,
      description: "Test item",
      value: 100
    }],
    shipDate: "2025-03-01",
    returnJson: true
  });

  expect(result.success).toBe(true);
  expect(result.data.quotes).toHaveLength(1);
  expect(result.data.quotes[0].provider).toBe('TEST_CARRIER');
});
});

Production Deployment

src/production-client.tstypescript
import { FlashAmericasClient } from './client';
import { getEnvironment } from './config';

// Production-ready client with all features
export class ProductionFlashAmericasClient extends FlashAmericasClient {
constructor(environment: string = 'production') {
  super(getEnvironment(environment));
}

/**
 * Health check for monitoring
 */
async healthCheck(): Promise<boolean> {
  try {
    await this.http.get('/help');
    return true;
  } catch (error) {
    console.error('Health check failed:', error);
    return false;
  }
}

/**
 * Bulk quote requests with rate limiting
 */
async getBulkQuotes(requests: QuoteRequest[], batchSize: number = 5): Promise<QuoteResponse[]> {
  const results: QuoteResponse[] = [];
  
  for (let i = 0; i < requests.length; i += batchSize) {
    const batch = requests.slice(i, i + batchSize);
    const batchPromises = batch.map(request => this.getQuote(request));
    
    try {
      const batchResults = await Promise.all(batchPromises);
      results.push(...batchResults);
      
      // Rate limiting delay between batches
      if (i + batchSize < requests.length) {
        await new Promise(resolve => setTimeout(resolve, 1000));
      }
    } catch (error) {
      console.error(`Batch ${i / batchSize + 1} failed:, error`);
      throw error;
    }
  }
  
  return results;
}
}

// Export production client
export const flashAmericasClient = new ProductionFlashAmericasClient();

Key Features

βœ… Production Ready

  • Tested with real Flash Americas production API
  • Handles actual carrier responses (FedEx, UPS, USPS, LTL)
  • Comprehensive error handling and retry logic

βœ… Complete API Coverage

  • Quotes: Real-time multi-carrier rate shopping
  • Locations: ZIP code search and address validation
  • SAT Codes: Mexican tax classification lookup
  • Cross-Border: US ↔ Mexico shipping support

βœ… TypeScript Excellence

  • Full type safety with accurate interfaces
  • IntelliSense support for all API responses
  • Type-safe error handling

βœ… Business Integration

  • Ready for e-commerce platforms
  • Bulk operations for high-volume usage
  • Production monitoring and health checks

Next Steps

  1. Get API Key: Contact Flash Americas for production credentials
  2. Install Package: Add to your Node.js project
  3. Configure Environment: Set up API key and environment variables
  4. Test Integration: Start with basic quotes and expand
  5. Deploy: Use production client for live shipping quotes

This TypeScript client provides everything needed for a complete Flash Americas integration with real production data and enterprise-grade reliability.