Skip to main content
Server SDKs

Node.js examples

Provider configuration, cancellation, and structured error handling.

Add providers​

import { GeocodingClient } from '@affordable-geo-apis/sdk';

const client = new GeocodingClient({
baseUrl: process.env.GEOCODING_BASE_URL!,
apiKey: process.env.GEOCODING_API_KEY!,
confidenceThreshold: 0.8,
fallbackBudget: { maxRequests: 1000 },
fallbackCircuitBreaker: { failureThreshold: 3, cooldownMs: 30_000 },
fallbacks: [
{ provider: 'google', apiKey: process.env.GOOGLE_MAPS_API_KEY! },
{ provider: 'here', apiKey: process.env.HERE_API_KEY! },
],
});

const response = await client.geocode({
street: '10 Downing Street', city: 'London', country: 'GB',
});

console.log(response.provider, response.fallbackReason, response.attempts);
if (response.provider !== 'primary') {
console.log(response.providerResponse);
}

GB skips primary and routes to backups. The raw selected body is available as Record<string, unknown>; check provider and field types before consuming it. The first nonempty backup wins. Review fallback policy before choosing an order or threshold.

Bound the complete call​

const response = await client.reverseGeocode(
{ lat: 40.7074, lon: -74.0113 },
{ signal: AbortSignal.timeout(15_000) },
);

This deadline covers the entire provider chain. Constructor timeoutMs limits each provider request separately.

For caller-controlled cancellation:

const controller = new AbortController();
const pending = client.geocode(
{ street: '11 Wall Street', city: 'New York' },
{ signal: controller.signal },
);
controller.abort();
try {
await pending;
} catch (error) {
if (!controller.signal.aborted) throw error;
}

The signal's abort reason propagates and stops the chain.

Handle provider errors​

import { GeocodingError } from '@affordable-geo-apis/sdk';

try {
const response = await client.geocode({ street: '11 Wall Street', city: 'New York' });
if (response.results.length === 0) console.log('No match');
} catch (error) {
if (!(error instanceof GeocodingError)) throw error;
console.error({
message: error.message,
provider: error.provider,
code: error.code,
status: error.status,
attempts: error.attempts.map(attempt => ({
provider: attempt.provider,
outcome: attempt.outcome,
skipReason: attempt.skipReason,
errorCode: attempt.error?.code,
})),
});
}

HTTP 4xx and malformed primary responses do not call backups. A successful recovery exposes the original primary error in response.attempts. See error handling.

Filter primary sources​

const response = await client.geocode({
street: '11 Wall Street', city: 'New York', state: 'NY',
exclude_sources: ['osm'],
});

This setting affects primary results only. Configured backups can still run if filtering leaves no acceptable primary candidates.

Reuse and transport​

Reuse one client across calls to share backup budget and breaker state. No close method is required. Pass fetch in the constructor to use a custom transport or mock. It must honor standard fetch behavior and AbortSignal. Functions are bound to globalThis; a function already bound to another receiver retains that receiver.

Address autocomplete​

const response = await client.autocomplete({q: '11 Wall', country: 'US'});
if (response.suggestions.length) {
const address = await client.resolveAutocomplete(response.suggestions[0]);
console.log(address.label, address.lat, address.lon);
}

Keep the returned suggestion object, including its provider and Google session token, until selection. See the autocomplete guide.

For a Google-backed typing session, reuse one sessionToken across searches and pass the selected suggestion unchanged to resolution. Generate a new token for the next session. The token is SDK-only; it is not a primary HTTP field. Resolution is a separate provider call, outside fallback budgets/circuit breakers, and returns one address without an envelope or attempt history.