geoip-country

A memory-light, synchronous Node.js library for looking up a country (plus capital, continent, currency, and languages) from an IP address.

Library
npm
v5.0.202609050122
93stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
66/100Good
Development Activity80
Maintenance60
Community52
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
51/100Fair
Architecture62
Code Quality45
Innovation55
Learning Curve40

geoip-country is a fork of geoip-lite that drops city- and coordinate-level data to keep only country lookups, cutting the resident database from over 120 MB down to around 7 MB and shortening both startup and per-lookup time. It loads MaxMind’s GeoLite2-Country CSV data, converted into a compact fixed-width binary format, directly into memory when the module is required, then answers lookups synchronously with no callbacks and no network calls involved.

Lookups use a two-stage search: a configurable precomputed index (geoip_fast_lookup, tunable from 4 to 18) narrows the search range, followed by a binary search over fixed-size records read straight off the in-memory buffer. This lets consumers explicitly trade a documented amount of extra memory for lower per-lookup latency. Country codes are then joined against the countries-list/annexare/Countries dataset to attach capital, continent, currency, and spoken-language fields alongside the raw ISO 3166-1 alpha-2 code.

The package ships a built-in updater (npm run updatedb) that pulls fresh GeoLite2-Country CSVs from MaxMind (a license key is required per MaxMind’s EULA) and repacks them into the binary format the library reads, with a GitHub Actions workflow re-running this on a schedule so published versions carry a recent database snapshot. Because GeoLite2 data licensing carries usage restrictions (no FCRA use, no household/individual identification), the library also supports pointing GEOIP_DATADIR/IP_LOCATION_DB at alternative, more permissively licensed databases from the author’s companion ip-location-db project.

The upstream README notes the repository is no longer actively maintained and points users toward the author’s newer, from-scratch successor ip-location-api for a faster and more customizable replacement — worth knowing before adopting this package for a new project, though the existing release remains functional and widely used.

What You Get

  • A synchronous lookup(ip) API supporting IPv4, IPv6, and IPv4-mapped IPv6 addresses with no callbacks
  • A pre-packed, ~7 MB binary GeoLite2-Country dataset loaded into memory at require time
  • A tunable fast-lookup index (geoip_fast_lookup) to trade memory for lookup speed
  • A built-in updateDatabase/npm run updatedb script to refresh the bundled MaxMind data
  • File-watcher support (startWatchingDataUpdate) to hot-reload data files after an external update
  • Enriched results (capital, continent name, currency, languages) joined from the countries-list dataset

Common Use Cases

  • Redirecting or localizing users by country at the edge or in middleware without an external API round-trip
  • Tagging analytics events or logs with a visitor’s country for reporting and segmentation
  • Country-based feature flagging or content restriction (e.g. showing region-specific pricing or compliance notices)
  • Lightweight fraud/abuse signals where a coarse country match is sufficient and low latency matters
  • Replacing a heavier city-level GeoIP database when only country granularity is actually needed

Under The Hood

Architecture geoip.js loads two flat binary buffer files (geoip-country.dat for IPv4, geoip-country6.dat for IPv6) into memory once via preload(), with each record a fixed width (10 bytes for IPv4, 18 for IPv6) sorted by IP range start. lookup4/lookup6 perform a two-stage search: a coarse jump using a precomputed middleIps index sized by the fast_lookup exponent (default 12, configurable 4-18) bounds a range, then a binary search reads fixed offsets directly off the Buffer with no parsing step. Country/continent/capital/currency/language enrichment is a second, static in-memory join (countries-list) applied post-lookup in setCountryInfo(). There are no classes or dependency injection — state lives in module-level cache4/cache6 objects mutated by preload()/clear()/reload(), making the module effectively a singleton; changing the core indexing scheme would require touching lookup4, lookup6, and setMiddleLines together since they share tuned offsets.

Tech Stack Plain CommonJS Node.js with no build step and no TypeScript. Runtime dependencies are async, countries-list, ip-address, and yauzl (zip extraction for the updater); dev dependencies include dayjs, geoip-lite (used only for benchmark comparison), and nodeunit-x. The data pipeline (scripts/updatedb.js) downloads a GeoLite2-Country CSV zip from MaxMind’s licensed API, extracts it with yauzl, and repacks it into the library’s binary format. CI is GitHub Actions running a Node 10-20 test matrix on every PR, plus a separate scheduled workflow that re-runs the updater against MaxMind and auto-publishes a new npm version, which explains the timestamp-style version numbers.

Code Quality Tests in test/tests.js use nodeunit-x and cover IPv4, IPv6, and IPv4-mapped-IPv6 lookups, but assertions are thin (a handful of test.ok/test.equal checks plus console-logged manual inspection); test/benchmark.js and two memory-usage scripts exist for manual profiling rather than automated checks. There is no TypeScript or type annotations, minimal input validation beyond net.isIP(), and no ESLint/Prettier configuration in the repo. CI running npm test across six Node versions on every pull request is the primary quality gate.

What Makes It Unique The explicit, documented geoip_fast_lookup knob — letting a consumer trade a known amount of extra memory (roughly 192 B up to 3 MB) for measurably lower per-lookup latency — is a deliberate tuning surface most GeoIP libraries don’t expose. Combined with deliberately dropping city/coordinate data to shrink the resident dataset by roughly an order of magnitude compared to geoip-lite while keeping a dependency-light, synchronous API, it’s a focused, well-benchmarked optimization of an existing idea rather than a wholly new capability.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search