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.