@ynm/location-resolver
v1.2.4
Published
Resolve Vietnam province from location strings (local lookup + optional Google Geocoding)
Readme
Location Resolver
A Node.js library that resolves country codes and Vietnamese provinces/cities from free-form address strings or Facebook-like identity objects. It prefers local lookup (provinces + districts), and falls back to a Geo API — by default the internal YNM Geo Resolver, with optional Google Geocoding API.
npm (npmjs.com): scoped package @ynm/location-resolver — published under the private npm org @ynm. Only members / tokens with permissions on the org can npm install it (this is a private package and requires authentication).
Requirements
- Node.js ≥ 18
- One of the following geocoding backends for the network fallback:
- YNM Geo Resolver (default) — reachable HTTP endpoint, e.g. the in-cluster service
http://geo-resolver.sl-production.svc.cluster.local:3030or a custom URL viaynmGeoEndpoint. - Google Maps Geocoding — requires a valid API key, enabled by setting
geoAPI: 'googleGeoAPI'.
- YNM Geo Resolver (default) — reachable HTTP endpoint, e.g. the in-cluster service
Installation
Login to npm using an account added to the @ynm org (or use an automation token with read access to private packages):
npm login
npm install @ynm/location-resolver
# or
yarn npm login # if using Yarn 2+
yarn add @ynm/location-resolverFor CI / non-interactive machines: create an Access Token (Granular or Classic, with read access to private packages) on npmjs, set the NPM_TOKEN environment variable, and run for example:
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc
npm install @ynm/location-resolver(Do not commit .npmrc with your token; in CI environments, usually you create a temporary file or use npm config set, and then remove it after the install step.)
From a local clone (development):
npm install /path/to/location-resolverBundled data
The data/ directory:
| File | Purpose |
|------|---------|
| province.json | Province/city name → internal province_id |
| districts.js | District/ward list → parent province |
| countries.json | Country names (English / aliases) → ISO alpha-2 code |
You can edit these files if your internal IDs or place names differ from your upstream system.
Quick start
const { LocationResolver } = require('@ynm/location-resolver');
// Default: local lookup, fallback to YNM Geo Resolver
const resolver = new LocationResolver({
// ynmGeoEndpoint: 'http://geo-resolver.sl-production.svc.cluster.local:3030', // optional override
});
// Free-form address string
const info = await resolver.getProvinceInfo('Quận 1, Hồ Chí Minh, Vietnam');
// { country_code: 'VN', province_name: 'Hồ Chí Minh', province_id: 5 }
// Facebook-like location / hometown object
const fb = await resolver.getProvinceInfoForFBIdentity({
location: { country: 'Vietnam', city: 'Đà Nẵng', name: '' },
});Switching to Google Geocoding instead of the YNM endpoint:
const resolver = new LocationResolver({
geoAPI: 'googleGeoAPI',
googleApiKey: process.env.GOOGLE_MAPS_API_KEY,
hooks: {
onGoogleRequest() { /* e.g. quota accounting */ },
onGoogleSuccess() { /* optional */ },
},
});API
new LocationResolver(options?)
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| geoAPI | 'YNMGeoAPI' \| 'googleGeoAPI' | 'YNMGeoAPI' | Selects which network backend is invoked when local lookup fails. |
| ynmGeoEndpoint | string | http://geo-resolver.sl-production.svc.cluster.local:3030 | Base URL of the YNM Geo Resolver. The library calls GET {ynmGeoEndpoint}/detect?location=.... |
| googleApiKey | string | '' | Google Maps Geocoding API key (only used when geoAPI === 'googleGeoAPI'). |
| hooks | object | {} | onGoogleRequest() / onGoogleSuccess() — invoked before / after a Google call. |
The selected backend is bound to this.getProvinceByAPI at construction time, so resolveLocation always calls the right one.
getProvinceInfo(str)
Parses a string:
- If the entire string is a known country name →
{ country_code, province_id: null }. - If the string contains a country other than Vietnam →
{ country_code, province_id: null }. - If Vietnam or country is unknown but Vietnamese place resolution is needed → calls
resolveLocation(local, then Geo API).
getProvinceInfoForFBIdentity(identity)
Reads identity.location or identity.hometown as { country, city, name } (address often in name). Non-Vietnam countries return province_id: 0; Vietnam builds a string and runs resolveLocation.
resolveLocation(str)
Use when you need Vietnamese province inference (or geocoding to another country): local first, then the configured Geo API. Result includes country_code, province_name, province_id (0 if the API returns Vietnam but the province cannot be mapped).
resolveProvinceByLocal(str) (synchronous)
Splits on commas and tries to match province name or district; no network I/O.
getProvinceByYNMGeoAPI(location)
Calls GET {ynmGeoEndpoint}/detect?location=<encoded> and expects:
{ "status": "OK", "results": { "country_code": "VN", "province_name": "Hồ Chí Minh" } }Returns { country_code, province_name } or null on HTTP error / NOT_FOUND.
getProvinceByGoogleMap(location)
Calls the Google Geocoding JSON API and normalizes the result to the same shape as the YNM backend: { country_code, province_name } (province_name may be null if Google does not return administrative_area_level_1).
Breaking change vs. v1.0.x — this method previously returned
{ countryCode, province }. The keys are nowcountry_codeandprovince_namefor parity with the YNM Geo API.
Additional exports
The module also exports PROVINCE_MAPPING and COUNTRIES_MAPPING (loaded from data/) for direct use.
Resolution flow (summary)
- Normalize Vietnamese text (strip diacritics, lowercase, remove spaces; tokens like
city,thanhphoare stripped for matching). - For comma-separated addresses: try the last segment as a province name; if no match, try district on the last or second-to-last segment.
- If local resolution fails → call the configured Geo API (
YNMGeoAPIby default,googleGeoAPIwhen explicitly enabled) → if the result is in Vietnam, map the returnedprovince_nameback toprovince_idviaprovince.json.
Production logging
When process.env.NODE_ENV === 'production', the library disables console.debug to keep production logs clean. console.log and console.error are unaffected.
Testing
This repository includes a comprehensive, data-driven automation test suite built with Jest that exercises:
- Local lookup by province and district.
- Foreign address handling.
- Both fallback backends — Google (
geoAPI: 'googleGeoAPI') and YNM (ynmGeoEndpoint) — including HTTP errors andNOT_FOUNDresponses. - Edge cases (
null, empty, whitespace-only inputs) and constructor wiring.
Most cases are centralized in __tests__/test-cases.json, making it easy to add or remove records without modifying the test runner.
To run the tests:
npm run testTo run the tests and generate a graphical code coverage report:
npm run test:coverage
# Open coverage/lcov-report/index.html to view the reportLicense
UNLICENSED — see package.json. Change the license if you open-source or distribute the package.
