sxgeo-js
v1.0.0
Published
Node.js reader and updater for Sypex Geo binary databases (SxGeo.dat / SxGeoCity.dat)
Downloads
137
Maintainers
Readme
sxgeo-js
Node.js reader and updater for Sypex Geo binary databases.
Reads the official SxGeo.dat and SxGeoCity.dat files and resolves an IPv4
address to country code, country ID, city data, region data, and full country
metadata. Also provides helper functions to download and update the database
files directly from sypexgeo.net.
Disclaimer: This package is an independent reader for the open Sypex Geo binary format. It is not affiliated with or endorsed by sypexgeo.net. The database files are provided by sypexgeo.net under their own terms — please review the Sypex Geo license before using the database in your project.
Installation
npm install sxgeo-jsDownload a Database
This package does not bundle a database file.
Download one from the official Sypex Geo website and place it somewhere in your project, or use the built-in helper (see Updater API):
| File | Description |
| ------------------ | -------------------------------------------- |
| SxGeo.dat | Country only (smaller, faster to load) |
| SxGeoCity.dat | City + region + country (full dataset) |
Recommended project layout:
your-project/
data/
SxGeoCity.dat
src/
index.jsQuick Start
ESM
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { SxGeoReader } from 'sxgeo-js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const dbPath = path.join(__dirname, 'data', 'SxGeoCity.dat');
const reader = new SxGeoReader(dbPath, SxGeoReader.MEMORY | SxGeoReader.BATCH);
console.log(reader.getCityFull('1.1.1.1'));CommonJS
const path = require('node:path');
const { SxGeoReader } = require('sxgeo-js');
const dbPath = path.join(__dirname, 'data', 'SxGeoCity.dat');
const reader = new SxGeoReader(dbPath, SxGeoReader.MEMORY | SxGeoReader.BATCH);
console.log(reader.getCityFull('1.1.1.1'));TypeScript
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { SxGeoReader } from 'sxgeo-js';
import type { CityFullResult } from 'sxgeo-js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const reader = new SxGeoReader(
path.join(__dirname, 'data', 'SxGeoCity.dat'),
SxGeoReader.MEMORY | SxGeoReader.BATCH,
);
const result: CityFullResult | null = reader.getCityFull('1.1.1.1');
console.log(result);Constructor
const reader = new SxGeoReader(databasePath: string, mode?: number);| Parameter | Type | Description |
| -------------- | -------- | -------------------------------------------------------- |
| databasePath | string | Absolute or relative path to SxGeo.dat / SxGeoCity.dat |
| mode | number | Optional bitmask of read modes (see table below) |
Mode flags
| Flag | Value | Description |
| --------------------- | ----- | -------------------------------------------------------- |
| SxGeoReader.FILE | 0 | Read from disk on every lookup (lowest memory use) |
| SxGeoReader.MEMORY | 1 | Pre-load data section into RAM |
| SxGeoReader.BATCH | 2 | Pre-load index buffers into RAM for faster repeated lookups |
Combine flags with bitwise OR:
// Best throughput, highest memory use
const reader = new SxGeoReader(dbPath, SxGeoReader.MEMORY | SxGeoReader.BATCH);
// Minimal memory, reads everything from disk
const reader = new SxGeoReader(dbPath, SxGeoReader.FILE);Reader API
get(ip)
Returns getCity(ip) when a city database is open, or getCountry(ip) when
a country database is open.
reader.get('8.8.8.8');
// City DB → { city: {...}, country: { id: 225, iso: 'US' } }
// Country DB → 'US'getCountry(ip)
Returns the ISO 3166-1 alpha-2 country code, or null for private/unknown IPs.
reader.getCountry('8.8.8.8');
// 'US'getCountryId(ip)
Returns the numeric Sypex Geo country ID, or null if not found.
reader.getCountryId('8.8.8.8');
// 225getCity(ip)
Returns a short city result (city + country ID and ISO), or null.
reader.getCity('1.1.1.1');{
"city": {
"id": 2174003,
"lat": -27.46794,
"lon": 153.02809,
"name_ru": "Брисбен",
"name_en": "Brisbane"
},
"country": {
"id": 16,
"iso": "AU"
}
}getCityFull(ip)
Returns city, region, and full country data, or null.
reader.getCityFull('1.1.1.1');{
"city": {
"id": 2174003,
"lat": -27.46794,
"lon": 153.02809,
"name_ru": "Брисбен",
"name_en": "Brisbane"
},
"region": {
"id": 2152274,
"name_ru": "Квинсленд",
"name_en": "State of Queensland",
"iso": "AU-QLD"
},
"country": {
"id": 16,
"iso": "AU",
"lat": -25,
"lon": 135,
"name_ru": "Австралия",
"name_en": "Australia"
}
}about()
Returns metadata about the open database file.
reader.about();{
"created": "2026.01.19",
"timestamp": 1768861974,
"charset": "utf-8",
"type": "SxGeo City EN",
"byteIndex": 224,
"mainIndex": 1775,
"blocksInIndexItem": 3376,
"ipBlocks": 5995209,
"blockSize": 6,
"city": { "maxLength": 127, "totalSize": 2687625 },
"region": { "maxLength": 175, "totalSize": 109649 },
"country": { "maxLength": 147, "totalSize": 9387 }
}close()
Releases the underlying file descriptor. Call this when the reader is no longer needed (e.g. at the end of a short-lived script).
reader.close();In long-running processes (web servers) you typically keep one reader open for the lifetime of the process, so calling
close()is optional.
Updater API
import {
downloadCityDatabase,
downloadCountryDatabase,
checkCityDatabaseUpdate,
checkCountryDatabaseUpdate,
DatabaseAlreadyExistsError,
} from 'sxgeo-js';downloadCityDatabase(destDir, options?)
Downloads SxGeoCity.dat from sypexgeo.net and extracts it into destDir.
// First-time download
const filePath = await downloadCityDatabase('./data');
console.log(`Saved to: ${filePath}`);
// Force re-download (overwrites existing file)
await downloadCityDatabase('./data', { force: true });
// Smart update — skips the download if the server has not changed
await downloadCityDatabase('./data', { skipIfCurrent: true });Returns the absolute path of the extracted .dat file.
Throws DatabaseAlreadyExistsError if the file already exists and neither
force nor skipIfCurrent is set.
downloadCountryDatabase(destDir, options?)
Downloads SxGeo.dat (country-only) and extracts it into destDir.
Accepts the same options as downloadCityDatabase.
const filePath = await downloadCountryDatabase('./data');
console.log(`Saved to: ${filePath}`);Download options
| Option | Type | Default | Description |
| ---------------- | --------- | ------- | ----------- |
| force | boolean | false | Overwrite the existing file without any check |
| skipIfCurrent | boolean | false | Check the server headers first; skip the download if nothing has changed |
When skipIfCurrent is true, the function sends a single HTTP HEAD request
(no body downloaded) and reads only the first 8 bytes of the local .dat file.
If both the server's Last-Modified date and Content-Length match the values
recorded at the previous download, the large ZIP file is not fetched.
checkCityDatabaseUpdate(destDir)
Checks whether a newer SxGeoCity.dat is available without downloading
anything. Sends one HTTP HEAD request and reads a tiny sidecar file
(see Sidecar metadata below).
import { checkCityDatabaseUpdate } from 'sxgeo-js';
const status = await checkCityDatabaseUpdate('./data');
console.log(status);{
"upToDate": true,
"localTimestamp": 1769893012,
"localCreated": "2026.01.31",
"localFileMtime": "2026-04-11T22:23:42.505Z",
"knownLastModified": "2026-03-31T21:44:42.000Z",
"knownRemoteSize": 22220636,
"serverLastModified": "2026-03-31T21:44:42.000Z",
"remoteSize": 22220636
}| Field | Description |
| ------------------- | ----------- |
| upToDate | true when the server has not changed since the last download |
| localTimestamp | Unix timestamp embedded in the .dat header — the date the geo data was compiled (may differ from the ZIP publish date) |
| localCreated | localTimestamp formatted as YYYY.MM.DD |
| localFileMtime | When the local .dat file was last written to disk (download date) |
| knownLastModified | Last-Modified recorded in the sidecar at the last download |
| knownRemoteSize | Content-Length recorded in the sidecar at the last download |
| serverLastModified| Last-Modified reported by the server right now |
| remoteSize | Content-Length reported by the server right now |
checkCountryDatabaseUpdate(destDir)
Same as checkCityDatabaseUpdate but for SxGeo.dat.
DatabaseAlreadyExistsError
Thrown by downloadCityDatabase / downloadCountryDatabase when the target
file exists and neither force nor skipIfCurrent is set.
import { downloadCityDatabase, DatabaseAlreadyExistsError } from 'sxgeo-js';
try {
await downloadCityDatabase('./data');
} catch (err) {
if (err instanceof DatabaseAlreadyExistsError) {
console.log(`Already present: ${err.filePath}`);
// Re-download only if the server has a newer version:
await downloadCityDatabase('./data', { skipIfCurrent: true });
} else {
throw err;
}
}Sidecar metadata
Every time a database is successfully downloaded, a small JSON file is written
alongside the .dat file:
data/
SxGeoCity.dat
SxGeoCity.dat.meta.json ← written automatically{
"downloadedAt": "2026-04-11T22:23:42.505Z",
"remoteLastModified": "2026-03-31T21:44:42.000Z",
"remoteSize": 22220636
}This file records the server's Last-Modified header and Content-Length at
the time of the download. Future freshness checks compare both values against
the live server headers — catching updates even when only the file content
changes but the publish date stays the same (or vice versa).
The sidecar file is a runtime artifact. Add
*.dat.meta.jsonto your.gitignore(the package itself already does this in its own repo).
Scheduled Updates
The free Sypex Geo database is updated periodically. The recommended pattern for a cron job or scheduled task:
// update-db.mjs — run with: node update-db.mjs
import { downloadCityDatabase } from 'sxgeo-js';
// Downloads only when the server has published a new version.
// A single HEAD request is sent first; the large ZIP is skipped if up to date.
const filePath = await downloadCityDatabase('./data', { skipIfCurrent: true });
console.log(`Database is current: ${filePath}`);To check without downloading at all (e.g. in a status dashboard):
import { checkCityDatabaseUpdate } from 'sxgeo-js';
const { upToDate, localCreated, serverLastModified } =
await checkCityDatabaseUpdate('./data');
if (!upToDate) {
console.log(
`Update available — server: ${serverLastModified?.toDateString()}, ` +
`local geo data compiled: ${localCreated}`,
);
}Notes
- Only IPv4 lookups are supported.
- Private, loopback, and reserved ranges (
10.x.x.x,127.x.x.x,0.x.x.xand addresses with a first octet ≥ the database's byte-index size) returnnull. SxGeoCity.datalready contains full country data, so a single file is enough for city-level lookups.- If you only need country codes,
SxGeo.datis significantly smaller.
Development
# Install dependencies
npm install
# Build (ESM + CJS + type declarations)
npm run build
# Run tests
npm testLicense
MIT — see LICENSE.
Database files are provided by sypexgeo.net under their own license. This package is an independent implementation that reads the open Sypex Geo binary format.
