@ipgeotrace/client
v0.1.0
Published
Isomorphic IPGeoTrace client for Node, edge, Deno, and Bun. Single and batch IP geolocation with caching and retries.
Maintainers
Readme
@ipgeotrace/client
Isomorphic IPGeoTrace client for Node 18+, edge runtimes, Deno, and
Bun. Resolve a single IP or a batch of up to 100, with optional caching and automatic retries.
Zero runtime dependencies, fetch-based.
Sign up and grab your API key at ipgeotrace.com.
This is the server-side, secret-key client — you supply the IP. For the browser (resolve the
current visitor, no secret key), use @ipgeotrace/browser.
Install
npm add @ipgeotrace/clientQuick start
import { IpGeoTraceClient } from '@ipgeotrace/client';
const client = new IpGeoTraceClient({ apiKey: process.env.IPGEOTRACE_API_KEY! });
const result = await client.resolve('8.8.8.8');
if (result.ok) {
console.log(`${result.value.city?.name}, ${result.value.country?.name}`);
} else {
console.log(`failed: ${result.error.code}`);
}The IP is always supplied by you: a signup form, a webhook payload, a stored audit log. Invalid IPs
are caught locally (invalid_ip) with no wasted round trip.
Results, not exceptions
Every call returns Result<T> — { ok: true, value } | { ok: false, error }. Check ok and
TypeScript narrows value (or error) for you. The only thrown thing is caller cancellation via
an AbortSignal:
const controller = new AbortController();
const result = await client.resolve('8.8.8.8', controller.signal);Batch lookups
const batch = await client.resolveBatch(logins.map((l) => l.ip));
if (batch.ok) {
for (const item of batch.value.results) {
report.add(item.ip, item.found ? item.country?.name : item.error);
}
}Results come back in request order, one item per address. A single bad address fails as its own
item (found: false, error) without failing the batch. With caching on, cached IPs are served
locally and only the misses are sent; if every IP is a hit, no request is made and
batch.value.fromCache is true.
Configuration
const client = new IpGeoTraceClient({
apiKey: '...',
environment: 'production', // or 'sandbox'; ignored when baseUrl is set
baseUrl: undefined, // absolute override for self-hosted / staging
timeoutMs: 10_000,
maxRetries: 2, // retries 429 (rate_limited) and 503, honoring Retry-After
cache: true, // or supply your own GeoCache (Redis, etc.)
cacheTtlMs: 5 * 60_000,
fetch: globalThis.fetch, // override on Node < 18 or to inject a proxy agent
});Only successful lookups are cached, never errors. A throwing cache never breaks a lookup — the client falls back to calling the API.
Errors
On failure, error carries the reason:
interface GeoError {
code: GeoErrorCode; // GeoErrorCodes.*
message: string;
statusCode?: number;
retryAfterSeconds?: number;
}Codes: unauthorized, invalid_ip, bad_request, not_found, rate_limited, quota_exceeded,
license_inactive, forbidden, service_unavailable, network_error, timeout,
invalid_response, unknown.
