npm.io
4.0.0 • Published 6 months ago

vies-checker

Licence
MIT
Version
4.0.0
Deps
0
Size
57 kB
Vulns
0
Weekly
0
Stars
5

VIES Checker

EU API Works

Modern European VIES VAT number validator with full TypeScript support.

Features

  • Native fetch API - Uses Node.js 18+ built-in fetch (no dependencies!)
  • Full TypeScript support - Comprehensive types for all API responses
  • Rich company data - Get company name, address, and validation details
  • Robust error handling - Custom error class with helper methods
  • Timeout & retry support - Configurable timeouts and automatic retries
  • Input normalization - Automatically handles spaces, dashes, and case
  • ESM & CommonJS - Dual module support for maximum compatibility
  • Zero dependencies - Lightweight and secure

Requirements

  • Node.js >= 18.0.0 (for native fetch API support)

Installation

npm install vies-checker
# or
pnpm add vies-checker
# or
yarn add vies-checker

Quick Start

Basic Validation with Full Details
import { checkVAT } from 'vies-checker';

const result = await checkVAT('LU', '26375245');

console.log(result.isValid);     // true
console.log(result.name);        // "AMAZON EUROPE CORE S.A R.L."
console.log(result.address);     // "38, AVENUE JOHN F. KENNEDY\nL-1855  LUXEMBOURG"
console.log(result.requestDate); // Date object
Simple Boolean Check
import { isValid } from 'vies-checker';

const valid = await isValid('LU', '26375245');
console.log(valid); // true

API Reference

checkVAT(country, vatNumber, options?)

Validates a VAT number and returns comprehensive company information.

Parameters:

  • country (string) - Two-letter EU country code (e.g., 'LU', 'DE', 'FR', 'IT')
  • vatNumber (string) - VAT number to validate (spaces/dashes will be removed automatically)
  • options (object, optional)
    • timeout (number) - Request timeout in milliseconds (default: 10000)
    • retries (number) - Number of retry attempts on network errors (default: 0)

Returns: Promise<ViesCheckResult>

interface ViesCheckResult {
  isValid: boolean;
  requestDate: Date;
  country: EuropeanMemberState;
  vatNumber: string;
  name?: string;              // Company name (if valid)
  address?: string;           // Company address (if valid)
  approximateMatch?: object;  // Approximate matching details
}

Throws: ViesError - When the VIES service returns an error state

Examples:

// Basic usage
const result = await checkVAT('LU', '26375245');

// With timeout
const result = await checkVAT('LU', '26375245', { timeout: 5000 });

// With retries
const result = await checkVAT('LU', '26375245', { retries: 2 });

// Input is normalized automatically
const result = await checkVAT('lu', '26 37 52 45'); // Works fine!

isValid(country, vatNumber)

Deprecated - Consider using checkVAT() for richer response data.

Simple boolean validation check for backward compatibility.

Parameters:

  • country (EuropeanMemberState) - Two-letter EU country code
  • vatNumber (string) - VAT number to validate

Returns: Promise<boolean | null>

  • true - VAT number is valid
  • false - VAT number is invalid or network error occurred
  • null - Missing country or VAT number

Throws: ViesError - On service errors (rate limits, unavailable, etc.)

Example:

const valid = await isValid('LU', '26375245');
console.log(valid); // true

Response Format

The checkVAT() function returns a comprehensive result object:

{
  isValid: boolean;           // true if VAT is valid in VIES
  requestDate: Date;          // When the validation was performed
  country: string;            // Country code (e.g., 'LU')
  vatNumber: string;          // Validated VAT number
  name?: string;              // Company name (only if valid)
  address?: string;           // Full company address (only if valid)
  approximateMatch?: {        // Approximate matching details
    name: string;
    street: string;
    postalCode: string;
    city: string;
    companyType: string;
    matchName: 1 | 2 | 3;     // 1=valid, 2=invalid, 3=not processed
    matchStreet: 1 | 2 | 3;
    matchPostalCode: 1 | 2 | 3;
    matchCity: 1 | 2 | 3;
    matchCompanyType: 1 | 2 | 3;
  }
}

Error Handling

The package uses a custom ViesError class for API-related errors:

import { checkVAT, ViesError } from 'vies-checker';

try {
  const result = await checkVAT('LU', '26375245');
  console.log(result.name);
} catch (error) {
  if (error instanceof ViesError) {
    console.log('Error code:', error.code);
    console.log('Error message:', error.message);

    // Check error type
    if (error.isTransient()) {
      console.log('Temporary error, retry might work');
    }

    if (error.isRateLimitError()) {
      console.log('Rate limit hit, slow down requests');
    }
  }
}
Error Codes
Error Code Description Transient? Action
VALID VAT number is valid N/A Success
INVALID VAT number is invalid N/A Expected
INVALID_INPUT Input validation failed No Fix input format
GLOBAL_MAX_CONCURRENT_REQ Too many concurrent requests globally Yes Wait and retry
MS_MAX_CONCURRENT_REQ Too many concurrent requests for member state Yes Wait and retry
SERVICE_UNAVAILABLE VIES service is unavailable Yes Retry later
MS_UNAVAILABLE Member state service unavailable Yes Retry later
TIMEOUT Request timed out Yes Retry with longer timeout
ViesError Helper Methods
  • error.isTransient() - Returns true if error is temporary and worth retrying
  • error.isRateLimitError() - Returns true if error is due to rate limiting

Advanced Usage

Timeout Configuration
// Set a custom timeout (in milliseconds)
const result = await checkVAT('LU', '26375245', {
  timeout: 5000  // 5 seconds
});
Retry Logic
// Automatically retry on network errors
const result = await checkVAT('LU', '26375245', {
  retries: 2  // Retry up to 2 times
});
Combined Options
const result = await checkVAT('LU', '26375245', {
  timeout: 15000,  // 15 second timeout
  retries: 3       // Retry 3 times on failure
});
TypeScript Integration

Full TypeScript support with exported types:

import {
  checkVAT,
  ViesCheckResult,
  ViesError,
  EuropeanMemberState
} from 'vies-checker';

async function validateCompany(
  country: EuropeanMemberState,
  vat: string
): Promise<ViesCheckResult> {
  return await checkVAT(country, vat);
}

// Type-safe error handling
try {
  const result = await validateCompany('LU', '26375245');
  console.log(result.name);
} catch (error) {
  if (error instanceof ViesError && error.isTransient()) {
    // Retry logic
  }
}
Input Normalization

The package automatically normalizes inputs for convenience:

// All of these work:
await checkVAT('LU', '26375245');
await checkVAT('lu', '26375245');      // Lowercase country
await checkVAT('LU', '26 37 52 45');   // Spaces
await checkVAT('LU', '26-37-52-45');   // Dashes
await checkVAT('LU', '26.37.52.45');   // Dots

// All normalize to: country='LU', vatNumber='26375245'

Supported Countries

All 27 EU member states plus Northern Ireland:

AT, BE, BG, CY, CZ, DE, DK, EE, EL (Greece), ES, FI, FR, HR, HU, IE, IT, LT, LU, LV, MT, NL, PL, PT, RO, SE, SI, SK, XI (Northern Ireland)

Migration from v3.x

Breaking Changes
  • Node.js 18+ required (v3.x worked on any Node.js version)
  • TypeScript 5.x recommended (v3.x used TypeScript 4.x)
Non-Breaking Changes
  • The isValid() function works exactly the same way
  • All imports remain compatible
  • Return values are unchanged

For new code, use checkVAT() to access rich company data:

// v3.x
const valid = await isValid('LU', '26375245');

// v4.x - same as v3.x (still works!)
const valid = await isValid('LU', '26375245');

// v4.x - recommended (get more data!)
const result = await checkVAT('LU', '26375245');
console.log(result.isValid);  // same boolean
console.log(result.name);     // NEW: company name
console.log(result.address);  // NEW: company address

Examples

Validate and Display Company Info
import { checkVAT } from 'vies-checker';

const result = await checkVAT('LU', '26375245');

if (result.isValid) {
  console.log(`✅ Valid VAT`);
  console.log(`Company: ${result.name}`);
  console.log(`Address: ${result.address}`);
} else {
  console.log(`❌ Invalid VAT`);
}
Batch Validation with Error Handling
import { checkVAT, ViesError } from 'vies-checker';

const companies = [
  { country: 'LU', vat: '26375245' },
  { country: 'DE', vat: '12345678' },
  { country: 'FR', vat: '98765432' },
];

for (const company of companies) {
  try {
    const result = await checkVAT(company.country, company.vat, {
      timeout: 10000,
      retries: 2
    });

    console.log(`${company.country}${company.vat}: ${result.isValid ? '✅' : '❌'}`);
    if (result.name) {
      console.log(`  → ${result.name}`);
    }
  } catch (error) {
    if (error instanceof ViesError) {
      console.log(`${company.country}${company.vat}: Error (${error.code})`);

      if (error.isTransient()) {
        console.log('  → Will retry later...');
      }
    }
  }
}
Retry on Transient Errors
import { checkVAT, ViesError } from 'vies-checker';

async function validateWithRetry(country: string, vat: string, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await checkVAT(country, vat, { timeout: 10000 });
    } catch (error) {
      if (error instanceof ViesError && error.isTransient() && attempt < maxRetries) {
        console.log(`Attempt ${attempt} failed, retrying...`);
        await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2s
        continue;
      }
      throw error;
    }
  }
}

const result = await validateWithRetry('LU', '26375245');

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT Iñigo Taibo

Keywords