@ipgeotrace/fastify
v0.1.0
Published
Fastify plugin for IPGeoTrace. Resolves the caller's location once per request and exposes it on request.geo.
Maintainers
Readme
@ipgeotrace/fastify
Fastify plugin for IPGeoTrace. Resolves the caller's location once per
request and hands it to you on request.geo. Built on @ipgeotrace/client — the
secret key stays server-side.
Sign up and grab your API key at ipgeotrace.com.
Install
npm add @ipgeotrace/fastify fastifyUsage
import Fastify from 'fastify';
import { ipgeotrace, getGeo } from '@ipgeotrace/fastify';
const app = Fastify({ trustProxy: true }); // so request.ip reads X-Forwarded-For behind a proxy
await app.register(ipgeotrace, { apiKey: process.env.IPGEOTRACE_API_KEY! });
app.get('/checkout', (request, reply) => {
const geo = getGeo(request);
const currency = geo.status === 'resolved' ? geo.value?.country?.currency ?? 'USD' : 'USD';
reply.send({ currency });
});The plugin is registered app-wide (wrapped with fastify-plugin), so request.geo is available in
every route.
request.geo (and getGeo(request)) is a GeoLookup whose status tells you exactly what
happened:
resolved—valuecarries the data.skipped— the caller's IP was missing, private, loopback, or link-local, so no API call was made.failed—errorcarries the reason (rate_limited,quota_exceeded, …).not_attempted— the plugin opted out for this request (shouldResolvereturned false).
getGeo() is always safe to call and returns not_attempted when the plugin never ran.
Skipping requests
Health checks and internal endpoints should not spend lookups. Opt them out with shouldResolve:
await app.register(ipgeotrace, {
apiKey: process.env.IPGEOTRACE_API_KEY!,
shouldResolve: (request) => !request.url.startsWith('/health') && !request.url.startsWith('/internal'),
});Private, loopback, and link-local addresses (including IPv4-mapped IPv6) are detected locally and skipped without an API call or quota usage.
Choosing the caller's IP
By default the plugin uses request.ip, which respects Fastify's trustProxy setting. For full
control, supply your own selector:
await app.register(ipgeotrace, {
apiKey: process.env.IPGEOTRACE_API_KEY!,
ipSelector: (request) => (request.headers['cf-connecting-ip'] as string) ?? request.ip,
});Configuration
Pass client options through clientOptions, or share a pre-built client across your app:
import { IpGeoTraceClient } from '@ipgeotrace/client';
// inline client options
await app.register(ipgeotrace, {
apiKey: process.env.IPGEOTRACE_API_KEY!,
clientOptions: { cache: true, cacheTtlMs: 6 * 60 * 60_000, timeoutMs: 3_000 },
});
// or bring your own client (also usable directly in services)
const client = new IpGeoTraceClient({ apiKey: process.env.IPGEOTRACE_API_KEY!, cache: true });
await app.register(ipgeotrace, { client });See the @ipgeotrace/client README for caching, retries, timeouts, and batch lookups.
