GeoLocationService
Package: @nauth-toolkit/core
Type: Service
Service for IP geolocation using MaxMind GeoIP2 database files. Provides IP to country/city lookup, database management, and automatic reloading capabilities.
- NestJS
- Express
- Fastify
import { GeoLocationService } from '@nauth-toolkit/nestjs';
import { GeoLocationService } from '@nauth-toolkit/core';
// Access via nauth.geoLocationService after NAuth.create()
import { GeoLocationService } from '@nauth-toolkit/core';
// Access via nauth.geoLocationService after NAuth.create()
Overview
The GeoLocationService provides IP geolocation using MaxMind GeoIP2 database files (.mmdb). It supports both managed downloads (via MaxMind API) and external database management (via geoipupdate or other tools).
Only available when geoLocation.maxMind is configured. Auto-injected by framework when enabled.
@maxmind/geoip2-nodepeer dependency must be installed- MaxMind license key and account ID (for downloads) OR pre-existing .mmdb files
- See the Geolocation guide for setup instructions
Methods
getIpGeolocation()
Get geolocation information for an IP address. Looks up the IP in the loaded MaxMind databases and returns country, city, and coordinates if available.
async getIpGeolocation(ip: string): Promise<{
country?: string;
city?: string;
latitude?: number;
longitude?: number;
}>
Parameters
ip(string) - IP address to lookup (IPv4 or IPv6)
Returns
Object with optional geolocation fields:
country?: string- Two-letter country code (e.g., 'US', 'GB')city?: string- City name in English (e.g., 'London', 'New York')latitude?: number- Geographic latitudelongitude?: number- Geographic longitude
Behavior
- Private IPs (localhost, 192.168.x.x, 10.x.x.x, etc.) return
{}without lookup - City database is tried first (includes coordinates); falls back to country database
- If no databases are loaded, returns
{} - If IP not found in database, returns
{}
Errors
None. This method never throws errors - it returns empty object if geolocation is unavailable.
Example
- NestJS
- Express
- Fastify
import { Injectable } from '@nestjs/common';
import { GeoLocationService } from '@nauth-toolkit/nestjs';
@Injectable()
export class LocationService {
constructor(private readonly geoLocationService: GeoLocationService) {}
async checkUserLocation(ip: string) {
const geo = await this.geoLocationService.getIpGeolocation(ip);
if (geo.country) {
console.log(`User is from ${geo.city}, ${geo.country}`);
console.log(`Coordinates: ${geo.latitude}, ${geo.longitude}`);
} else {
console.log('Location unknown');
}
}
}
app.get('/check-location', async (req, res) => {
const ip = req.ip || 'unknown';
const geo = await nauth.geoLocationService.getIpGeolocation(ip);
res.json({
ip,
country: geo.country || 'Unknown',
city: geo.city || 'Unknown',
coordinates: geo.latitude && geo.longitude
? { lat: geo.latitude, lng: geo.longitude }
: null
});
});
fastify.get('/check-location', async (request, reply) => {
const ip = request.ip || 'unknown';
const geo = await nauth.geoLocationService.getIpGeolocation(ip);
return {
ip,
country: geo.country || 'Unknown',
city: geo.city || 'Unknown',
coordinates: geo.latitude && geo.longitude
? { lat: geo.latitude, lng: geo.longitude }
: null
};
});
reloadGeoLocationDatabaseFromDisk()
Reload MaxMind database files from disk without downloading. Useful when database files are managed externally (via geoipupdate, container volumes, CI/CD, etc.).
async reloadGeoLocationDatabaseFromDisk(): Promise<void>
Parameters
None
Returns
Promise that resolves when databases are reloaded
Behavior
- Attempts to load
GeoLite2-City.mmdbandGeoLite2-Country.mmdbfrom configureddbPath - Replaces in-memory database readers with newly loaded ones
- Logs warnings if no database files are found
- Safe to call repeatedly - if files haven't changed, it just reloads the same data
- Works with
skipDownloads: trueconfiguration
Errors
| Code | When |
|---|---|
VALIDATION_FAILED | MaxMind configuration not provided |
VALIDATION_FAILED | @maxmind/geoip2-node peer dependency not installed |
Example
- NestJS
- Express
- Fastify
import { Injectable } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';
import { GeoLocationService } from '@nauth-toolkit/nestjs';
@Injectable()
export class GeoReloadService {
constructor(private readonly geoLocationService: GeoLocationService) {}
// Reload databases daily (after geoipupdate runs)
@Cron('0 1 * * *') // Every day at 1 AM
async reloadDatabases() {
try {
await this.geoLocationService.reloadGeoLocationDatabaseFromDisk();
console.log('MaxMind databases reloaded successfully');
} catch (error) {
console.error('Failed to reload databases:', error);
}
}
}
// Create an admin endpoint to trigger reload
app.post('/admin/reload-geo-db', async (req, res) => {
try {
await nauth.geoLocationService.reloadGeoLocationDatabaseFromDisk();
res.json({ message: 'Databases reloaded successfully' });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Or use node-cron
const cron = require('node-cron');
cron.schedule('0 1 * * *', async () => {
await nauth.geoLocationService.reloadGeoLocationDatabaseFromDisk();
});
// Create an admin endpoint to trigger reload
fastify.post('/admin/reload-geo-db', async (request, reply) => {
try {
await nauth.geoLocationService.reloadGeoLocationDatabaseFromDisk();
return { message: 'Databases reloaded successfully' };
} catch (error) {
reply.status(500);
return { error: error.message };
}
});
// Or use fastify-cron
fastify.cron.createJob({
cronTime: '0 1 * * *',
onTick: async () => {
await nauth.geoLocationService.reloadGeoLocationDatabaseFromDisk();
}
});
updateGeoLocationDatabase()
Download the latest MaxMind database files and reload them into memory. Uses distributed locking to prevent concurrent downloads in multi-server deployments.
async updateGeoLocationDatabase(): Promise<void>
Parameters
None
Returns
Promise that resolves when databases are downloaded and reloaded
Behavior
- Downloads configured editions (default:
GeoLite2-City,GeoLite2-Country) from MaxMind - Uses distributed locking (lock key:
maxmind-db-update-lock, TTL: 5 minutes) - Automatically reloads in-memory database readers after download
- If another process holds the lock, skips download and returns immediately
- Requires
licenseKeyandaccountIdin configuration - Throws if
skipDownloads: true
Errors
| Code | When |
|---|---|
VALIDATION_FAILED | MaxMind configuration not provided |
VALIDATION_FAILED | @maxmind/geoip2-node peer dependency not installed |
VALIDATION_FAILED | skipDownloads: true is set in configuration |
VALIDATION_FAILED | licenseKey or accountId is missing from configuration |
Example
- NestJS
- Express
- Fastify
import { Injectable } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';
import { GeoLocationService } from '@nauth-toolkit/nestjs';
@Injectable()
export class GeoUpdateService {
constructor(private readonly geoLocationService: GeoLocationService) {}
// Update databases weekly
@Cron('0 0 * * 0') // Every Sunday at midnight
async updateMaxMindDatabases() {
try {
await this.geoLocationService.updateGeoLocationDatabase();
console.log('MaxMind databases updated successfully');
} catch (error) {
console.error('Failed to update databases:', error);
}
}
}
// Create an admin endpoint to trigger update
app.post('/admin/update-geo-db', async (req, res) => {
try {
await nauth.geoLocationService.updateGeoLocationDatabase();
res.json({ message: 'Databases updated successfully' });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Or use node-cron
const cron = require('node-cron');
cron.schedule('0 0 * * 0', async () => {
await nauth.geoLocationService.updateGeoLocationDatabase();
});
// Create an admin endpoint to trigger update
fastify.post('/admin/update-geo-db', async (request, reply) => {
try {
await nauth.geoLocationService.updateGeoLocationDatabase();
return { message: 'Databases updated successfully' };
} catch (error) {
reply.status(500);
return { error: error.message };
}
});
// Or use fastify-cron
fastify.cron.createJob({
cronTime: '0 0 * * 0',
onTick: async () => {
await nauth.geoLocationService.updateGeoLocationDatabase();
}
});
Configuration
The GeoLocationService is only available when geoLocation.maxMind is configured. See Geolocation Configuration for all options.
Basic Configuration
import { NAuthModuleConfig } from '@nauth-toolkit/nestjs';
export const authConfig: NAuthModuleConfig = {
// ... other config ...
geoLocation: {
maxMind: {
// For managed downloads
licenseKey: process.env.MAXMIND_LICENSE_KEY,
accountId: parseInt(process.env.MAXMIND_ACCOUNT_ID || '0', 10),
dbPath: '/app/data/maxmind',
// OR for external management
dbPath: '/app/data/maxmind',
skipDownloads: true,
},
},
};
Usage Patterns
Pattern 1: Auto-Download (Development)
Best for development and single-server deployments.
// Config
geoLocation: {
maxMind: {
licenseKey: process.env.MAXMIND_LICENSE_KEY,
accountId: parseInt(process.env.MAXMIND_ACCOUNT_ID || '0', 10),
autoDownloadOnStartup: true,
},
}
// No additional code needed - databases auto-download on startup
Pattern 2: Scheduled Updates (Production)
Best for production with nauth-managed downloads.
// Config
geoLocation: {
maxMind: {
licenseKey: process.env.MAXMIND_LICENSE_KEY,
accountId: parseInt(process.env.MAXMIND_ACCOUNT_ID || '0', 10),
dbPath: '/app/data/maxmind',
},
}
// Service
@Injectable()
export class GeoUpdateService {
constructor(private readonly geoLocationService: GeoLocationService) {}
@Cron('0 0 1 * *') // First day of each month
async updateDatabases() {
await this.geoLocationService.updateGeoLocationDatabase();
}
}
Pattern 3: External Management (Production)
Best for production with external database management (geoipupdate, CI/CD).
// Config
geoLocation: {
maxMind: {
dbPath: '/app/data/maxmind',
skipDownloads: true,
},
}
// Service
@Injectable()
export class GeoReloadService {
constructor(private readonly geoLocationService: GeoLocationService) {}
@Cron('0 1 * * *') // Daily at 1 AM (after geoipupdate)
async reloadDatabases() {
await this.geoLocationService.reloadGeoLocationDatabaseFromDisk();
}
}
Related
- Geolocation Feature Guide - Setup and configuration
- Configuration Reference - All configuration options
- ClientInfoService - Access geolocation in request context