Skip to main content

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.

import { GeoLocationService } from '@nauth-toolkit/nestjs';

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).

note

Only available when geoLocation.maxMind is configured. Auto-injected by framework when enabled.

Requirements
  • @maxmind/geoip2-node peer 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 latitude
  • longitude?: 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

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');
}
}
}

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.mmdb and GeoLite2-Country.mmdb from configured dbPath
  • 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 in disk-only mode, where it is the only way to pick up externally managed files

Errors

CodeWhen
VALIDATION_FAILEDMaxMind configuration not provided
VALIDATION_FAILED@maxmind/geoip2-node peer dependency not installed

Example

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);
}
}
}

updateGeoLocationDatabase()​

Download the latest MaxMind database files and reload them into memory. Downloads are serialized across instances with a distributed lock, so containers that start in parallel take turns instead of all calling MaxMind at once.

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 the configured download source
  • Uses distributed locking (lock key: maxmind-db-update-lock, TTL: 5 minutes)
  • If another instance holds the lock, waits for it (up to 2 minutes) rather than giving up
  • Reuses .mmdb files on disk that are less than 24 hours old instead of re-downloading
  • Automatically reloads in-memory database readers after download
  • Concurrent calls in the same process share a single run
  • Throws if no download source is configured
Clustered deployments

The lock serializes instances rather than silencing them, which keeps both storage layouts correct:

  • Shared volume (EFS, NFS, mounted PVC) — the first instance downloads; the rest find fresh files and load them without hitting MaxMind.
  • Container-local path (the default, os.tmpdir()) — each instance downloads its own copy when its turn comes, so no container is left without geolocation data.

Errors

CodeWhen
VALIDATION_FAILEDMaxMind configuration not provided
VALIDATION_FAILED@maxmind/geoip2-node peer dependency not installed
VALIDATION_FAILEDNo download source is configured

Example

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);
}
}
}

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: {
dbPath: '/app/data/maxmind',

// For managed downloads. Omit `download` entirely for external management.
download: {
from: 'maxmind',
licenseKey: process.env.MAXMIND_LICENSE_KEY,
accountId: Number(process.env.MAXMIND_ACCOUNT_ID),
},
},
},
};

Usage Patterns​

Pattern 1: Auto-Download (Development)​

Best for development and single-server deployments.

// Config
geoLocation: {
maxMind: {
download: {
from: 'maxmind',
licenseKey: process.env.MAXMIND_LICENSE_KEY,
accountId: Number(process.env.MAXMIND_ACCOUNT_ID),
},
},
}

// No additional code needed - databases download during startup, which waits for them

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',
// No `download` block: the toolkit only loads what is already there.
},
}

// 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();
}
}