npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@digiphilo/opencage-typescript

v1.0.0

Published

TypeScript SDK for OpenCage Geocoding API - Type-safe frontend geocoding with zero dependencies

Readme

OpenCage TypeScript SDK

NPM Version Bundle Size License TypeScript Test Coverage

A type-safe, zero-dependency TypeScript SDK for the OpenCage Geocoding API. Built from the ground up with TypeScript for maximum type safety, developer experience, and frontend optimization.

✨ Features

  • 🔷 100% TypeScript - Built with TypeScript for TypeScript developers
  • 🛡️ Complete Type Safety - Full type coverage with strict TypeScript definitions
  • 🚀 Zero Dependencies - Pure TypeScript with no external dependencies
  • 🎯 Frontend Optimized - Designed specifically for browser environments
  • 💾 Smart Caching - Type-safe memory cache with LRU eviction and TTL
  • Debounced Requests - Built-in rate limiting with configurable debouncing
  • 🔄 Retry Logic - Automatic retry with exponential backoff and error handling
  • 📊 Statistics Tracking - Comprehensive usage analytics with type safety
  • 🛡️ Input Validation - Runtime validation with detailed TypeScript error messages
  • 🌐 Multiple Formats - ES modules, CommonJS with full type definitions
  • 📱 Mobile Friendly - Optimized for mobile and progressive web apps
  • 🧩 Tree Shakable - Import only what you need for smaller bundles

🚀 Quick Start

Installation

npm install @geonot/typescript-sdk

Basic Usage

import { OpenCageClient } from '@geonot/typescript-sdk'
import type { OpenCageResponse, GeocodingResult } from '@geonot/typescript-sdk'

// Initialize client with full type safety
const client = new OpenCageClient({
  apiKey: 'YOUR_API_KEY'
})

// Forward geocoding with automatic type inference
const forwardResult: OpenCageResponse = await client.geocode('1600 Pennsylvania Avenue, Washington, DC')
const coordinates = forwardResult.results[0]?.geometry // { lat: number, lng: number } | undefined
console.log(`Coordinates: ${coordinates?.lat}, ${coordinates?.lng}`)

// Reverse geocoding with type safety
const reverseResult: OpenCageResponse = await client.reverseGeocode(38.8976633, -77.0365739)
const address = reverseResult.results[0]?.formatted // string | undefined
console.log(`Address: ${address}`)

// Get rate limit info with full typing
const rateInfo = client.getRateLimitInfo()
if (rateInfo) {
  console.log(`${rateInfo.remaining}/${rateInfo.limit} requests remaining`)
}

Advanced Type-Safe Usage

import { 
  OpenCageClient, 
  COUNTRY_CODES, 
  LANGUAGE_CODES,
  type GeocodingQuery,
  type ReverseGeocodingOptions 
} from '@geonot/typescript-sdk'

const client = new OpenCageClient({
  apiKey: 'YOUR_API_KEY',
  cache: true,
  timeout: 10000
})

// Type-safe query object
const query: GeocodingQuery = {
  query: '10 Downing Street',
  country: COUNTRY_CODES.UNITED_KINGDOM,
  language: LANGUAGE_CODES.ENGLISH,
  limit: 5,
  bounds: [51.3, -0.5, 51.7, 0.2], // Type: [number, number, number, number]
  minConfidence: 7
}

const result = await client.geocode(query)

// Type-safe options for reverse geocoding
const options: ReverseGeocodingOptions = {
  language: LANGUAGE_CODES.SPANISH,
  limit: 1,
  noAnnotations: true
}

const reverseResult = await client.reverseGeocode(40.7128, -74.0060, options)

📚 Complete API Documentation

Client Configuration

import type { OpenCageClientOptions } from '@geonot/typescript-sdk'

const options: OpenCageClientOptions = {
  apiKey: 'YOUR_API_KEY',        // Required: Your OpenCage API key
  timeout: 10000,                // Request timeout in milliseconds
  retryAttempts: 3,              // Number of retry attempts for failed requests
  retryDelay: 1000,              // Base retry delay in milliseconds
  cache: true,                   // Enable/disable caching
  cacheSize: 100,                // Maximum cache entries
  cacheTtl: 300000,              // Cache TTL in milliseconds (5 minutes)
  debounce: 300,                 // Debounce delay for rapid requests
  baseUrl: 'https://api.opencagedata.com/geocode/v1', // API base URL
  userAgent: 'MyApp/1.0.0'       // Custom User-Agent string
}

const client = new OpenCageClient(options)

Forward Geocoding with Full Type Safety

import type { Query, GeocodingQuery, OpenCageResponse } from '@geonot/typescript-sdk'

// String queries are automatically typed
const stringQuery: string = 'Paris, France'
const result1: OpenCageResponse = await client.geocode(stringQuery)

// Object queries with full IntelliSense support
const objectQuery: GeocodingQuery = {
  query: '123 Main Street',
  country: 'us',               // Type: string
  language: 'en',              // Type: string
  limit: 5,                    // Type: number
  bounds: [40, -75, 41, -74],  // Type: [number, number, number, number]
  proximity: [40.7, -74.0],   // Type: [number, number]
  minConfidence: 7,            // Type: number
  abbrv: true,                 // Type: boolean
  noAnnotations: true          // Type: boolean
}

const result2: OpenCageResponse = await client.geocode(objectQuery)

// Type-safe access to results
const firstResult = result2.results[0]
if (firstResult) {
  const lat: number = firstResult.geometry.lat
  const lng: number = firstResult.geometry.lng
  const confidence: number = firstResult.confidence
  const formatted: string = firstResult.formatted
  const country: string | undefined = firstResult.components.country
}

// Debounced geocoding with the same type safety
const debouncedResult: OpenCageResponse = await client.debouncedGeocode(objectQuery)

Reverse Geocoding with Type Safety

import type { ReverseGeocodingOptions } from '@geonot/typescript-sdk'

// Basic reverse geocoding - coordinates are strongly typed as numbers
const lat: number = 40.7128
const lng: number = -74.0060
const basicResult: OpenCageResponse = await client.reverseGeocode(lat, lng)

// With typed options
const options: ReverseGeocodingOptions = {
  language: 'es',              // Type: string
  limit: 1,                    // Type: number
  minConfidence: 5,            // Type: number
  noAnnotations: true,         // Type: boolean
  abbrv: false,               // Type: boolean
  addRequest: true,           // Type: boolean
  pretty: false               // Type: boolean
}

const advancedResult: OpenCageResponse = await client.reverseGeocode(lat, lng, options)

// Type-safe access to reverse geocoding results
const address = advancedResult.results[0]?.formatted
const components = advancedResult.results[0]?.components

if (components) {
  const city: string | undefined = components.city
  const country: string | undefined = components.country
  const postcode: string | undefined = components.postcode
}

Batch Geocoding with Progress Tracking

import type { 
  BatchQueries, 
  BatchGeocodingResult, 
  BatchGeocodingProgress,
  ProgressCallback 
} from '@geonot/typescript-sdk'

const queries: BatchQueries = [
  'London, UK',
  'Paris, France',
  { query: 'Tokyo, Japan', language: 'ja' },
  { query: 'New York, USA', limit: 1 }
]

// Type-safe progress callback
const progressCallback: ProgressCallback = (progress: BatchGeocodingProgress) => {
  console.log(`Progress: ${progress.percentage.toFixed(1)}%`)
  console.log(`Completed: ${progress.completed}/${progress.total}`)
  
  if (progress.errors.length > 0) {
    progress.errors.forEach(error => {
      console.error(`Error at index ${error.index}:`, error.error.message)
    })
  }
}

// Execute batch with full type safety
const batchResult: BatchGeocodingResult = await client.batchGeocode(queries, progressCallback)

// Process results with type safety
batchResult.results.forEach((result, index) => {
  if (result) {
    const bestMatch = result.results[0]
    if (bestMatch) {
      console.log(`Query ${index}: ${bestMatch.formatted}`)
      console.log(`Coordinates: ${bestMatch.geometry.lat}, ${bestMatch.geometry.lng}`)
    }
  } else {
    console.log(`Query ${index} failed`)
  }
})

// Handle errors with full type information
if (batchResult.errors.length > 0) {
  batchResult.errors.forEach(({ index, query, error }) => {
    console.error(`Failed query at index ${index}:`, query)
    console.error(`Error: ${error.message} (Code: ${error.code})`)
  })
}

Cache Management with Type Safety

import type { CacheStats } from '@geonot/typescript-sdk'

// Clear cache
client.clearCache()

// Get typed cache statistics
const stats: CacheStats | null = client.getCacheStats()
if (stats) {
  console.log(`Cache size: ${stats.size}/${stats.maxSize}`)
  console.log(`Hit rate: ${(stats.hitRate * 100).toFixed(1)}%`)
  console.log(`Hits: ${stats.hits}, Misses: ${stats.misses}`)
}

// Update cache settings with validation
client.updateOptions({ 
  cache: true,
  cacheSize: 200,
  cacheTtl: 600000 // 10 minutes
})

Statistics and Monitoring

import type { ClientStats, RateLimit } from '@geonot/typescript-sdk'

// Get comprehensive statistics with full typing
const stats: ClientStats = client.getStats()
console.log({
  totalRequests: stats.totalRequests,    // number
  errors: stats.errors,                  // number
  cacheHits: stats.cacheHits,           // number
  cacheMisses: stats.cacheMisses,       // number
  lastRequest: stats.lastRequest,        // string | null
  rateLimit: stats.rateLimit,           // RateLimit | null
  cache: stats.cache                    // CacheStats | null
})

// Get rate limit information with null safety
const rateLimit: RateLimit | null = client.getRateLimitInfo()
if (rateLimit) {
  const remaining: number = rateLimit.remaining
  const limit: number = rateLimit.limit
  const reset: number = rateLimit.reset
  
  if (remaining < 100) {
    console.warn(`Low quota: ${remaining}/${limit} requests remaining`)
    console.log(`Resets at: ${new Date(reset).toLocaleString()}`)
  }
}

// Health check with comprehensive typing
const health = client.getHealth()
console.log({
  configured: health.configured,        // boolean
  cacheEnabled: health.cacheEnabled,    // boolean
  rateLimit: health.rateLimit,         // RateLimit | null
  lastError: health.lastError,         // string | null
  uptime: health.uptime                // number
})

Error Handling with Type Safety

import type { OpenCageError } from '@geonot/typescript-sdk'
import { isValidationError, ValidationError } from '@geonot/typescript-sdk'

try {
  const result = await client.geocode('invalid query')
} catch (error) {
  // Type-safe error handling
  if (error instanceof Error) {
    const opencageError = error as OpenCageError
    
    console.error('Geocoding failed:', {
      message: opencageError.message,              // string
      code: opencageError.code,                    // number
      status: opencageError.status,                // { code: number; message: string }
      operation: opencageError.operation,          // 'forward' | 'reverse' | 'batch' | undefined
      context: opencageError.context,              // unknown
      timestamp: opencageError.timestamp,          // string | undefined
      rateLimitRemaining: opencageError.rateLimitRemaining // number | undefined
    })
    
    // Handle specific error types with type safety
    switch (opencageError.code) {
      case 402:
        console.error('Quota exceeded - upgrade your plan')
        break
      case 401:
        console.error('Invalid API key')
        break
      case 408:
        console.error('Request timeout - try again')
        break
      case 429:
        console.error('Rate limit exceeded')
        break
      default:
        console.error(`API error: ${opencageError.message}`)
    }
  }
  
  // Handle validation errors specifically
  if (isValidationError(error)) {
    const validationError = error as ValidationError
    console.error(`Validation error in field ${validationError.field}: ${validationError.message}`)
  }
}

API Key Validation

// Validate API key with type safety
try {
  const isValid: boolean = await client.validateApiKey()
  if (isValid) {
    console.log('✅ API key is valid')
  } else {
    console.error('❌ Invalid API key')
  }
} catch (error) {
  console.error('Could not validate API key:', (error as Error).message)
}

// Check configuration state
const isConfigured: boolean = client.isConfigured()
if (!isConfigured) {
  console.error('Client is not properly configured')
}

🛠️ Type-Safe Utilities

The SDK includes comprehensive utilities with full TypeScript support:

import { 
  formatCoordinates, 
  calculateDistance, 
  getBestResult, 
  isWithinBounds,
  parseCoordinates,
  createBounds,
  COUNTRY_CODES,
  LANGUAGE_CODES,
  type CountryCode,
  type LanguageCode
} from '@geonot/typescript-sdk'

// Format coordinates with type safety
const formatted: string = formatCoordinates(40.7128, -74.0060, 4) // "40.7128, -74.0060"

// Calculate distance with automatic type checking
const distance: number = calculateDistance(40.7128, -74.0060, 34.0522, -118.2437)
console.log(`Distance: ${distance.toFixed(0)} km`)

// Get best result with generic type support
interface ResultWithConfidence { confidence: number; formatted: string }
const results: ResultWithConfidence[] = [
  { confidence: 5, formatted: 'Result 1' },
  { confidence: 9, formatted: 'Result 2' }
]
const best: ResultWithConfidence | null = getBestResult(results)

// Bounds checking with tuple types
const bounds: [number, number, number, number] = [40, -75, 41, -73]
const inBounds: boolean = isWithinBounds(40.7128, -74.0060, bounds)

// Parse coordinates from strings
const coords = parseCoordinates('40.7128, -74.0060')
if (coords) {
  const { lat, lng } = coords // Both typed as number
}

// Create bounds with type safety
const boundingBox: [number, number, number, number] = createBounds(40.7128, -74.0060, 10)

// Use predefined constants with type safety
const countryCode: CountryCode = COUNTRY_CODES.UNITED_STATES
const languageCode: LanguageCode = LANGUAGE_CODES.ENGLISH

const query = {
  query: 'Berlin',
  country: countryCode,    // Typed as CountryCode
  language: languageCode   // Typed as LanguageCode
}

🎛️ Advanced TypeScript Configuration

Custom Type Extensions

// Extend types for your specific use case
interface ExtendedGeocodingQuery extends GeocodingQuery {
  customField?: string
}

interface MyAppResponse extends OpenCageResponse {
  appMetadata?: {
    timestamp: number
    userId: string
  }
}

// Create typed client wrapper
class MyAppGeocodingService {
  private client: OpenCageClient

  constructor(apiKey: string) {
    this.client = new OpenCageClient({ apiKey })
  }

  async geocodeWithMetadata(query: ExtendedGeocodingQuery): Promise<MyAppResponse> {
    const result = await this.client.geocode(query)
    
    return {
      ...result,
      appMetadata: {
        timestamp: Date.now(),
        userId: 'current-user-id'
      }
    } as MyAppResponse
  }
}

Generic Utilities

import type { GeocodingResult } from '@geonot/typescript-sdk'

// Create type-safe result processors
function processResults<T extends GeocodingResult>(
  results: T[],
  processor: (result: T) => string
): string[] {
  return results.map(processor)
}

// Use with full type safety
const addresses = processResults(response.results, (result) => {
  return `${result.formatted} (${result.confidence}/10)`
})

Strict Type Configuration

// tsconfig.json for maximum type safety
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true
  }
}

🔧 Browser and Runtime Compatibility

  • TypeScript: 4.7+
  • Node.js: 16+
  • Browsers:
    • Chrome 90+
    • Firefox 88+
    • Safari 14+
    • Edge 90+

📊 Bundle Analysis

  • Core SDK: ~12KB gzipped with full TypeScript definitions
  • Tree-shakable: Import only the functions you need
  • Zero dependencies: No external runtime dependencies
  • Type definitions: ~8KB for complete type coverage

🧪 Development with TypeScript

# Install with TypeScript support
npm install @geonot/typescript-sdk

# Development dependencies (optional)
npm install -D typescript @types/node

# Build your TypeScript project
npx tsc

# Lint with TypeScript-aware rules
npx eslint src --ext .ts

🤝 Contributing

We welcome contributions! Please ensure all code follows TypeScript best practices:

  1. Type Safety: All code must be fully typed with no any types
  2. Strict Mode: Must pass with strict: true in TypeScript
  3. Tests: Include comprehensive type tests
  4. Documentation: Update type definitions and examples

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

  • OpenCage Data for the excellent geocoding API
  • TypeScript community for the amazing tooling and ecosystem
  • Built with enterprise-grade TypeScript patterns and best practices

📞 Support


Made with 💙 and TypeScript by Rome Stone

"Type safety isn't just about preventing errors—it's about enabling fearless refactoring, better developer experience, and building software that scales."