bd-geo-address
v0.1.4
Published
Bangladesh geo address library — division/district/upazila/thana lookup, BBS geocode resolution, Bangla name support, postal codes, geolocation. Works on Node.js, React, Next.js, Vue, Angular, NestJS and all major JS frameworks.
Maintainers
Readme
bd-geo-address
Bangladesh address library — division/district/upazila/thana lookup, BBS geocode resolution, Bangla name support, postal codes, geolocation. Zero dependencies. Works on Node.js, React, React Native, Next.js, Vue, Angular, NestJS, Deno and Bun.
Compatibility
React Native
Fully compatible — no setup required:
- Zero Node.js APIs in library code (
fs,path,processare only in build scripts, not shipped) - Zero DOM/browser APIs (
window,document,navigator— none used) - Zero runtime dependencies
- All functions synchronous — no async, no Promises, no side effects
- JSON data import works natively in Metro bundler
- Ships both CJS (
index.js) and ESM (index.mjs) — Metro picks the right one automatically
npm install bd-geo-addressNo additional Metro config or babel plugin needed.
TypeScript
Full TypeScript support out of the box:
strict: truethroughout — noany, no@ts-ignore- Ships
.d.tsand.d.mtsdeclaration files for both CJS and ESM consumers - Declaration maps (
.d.ts.map) for go-to-definition in VS Code and JetBrains IDEs - All public types explicitly exported — see TypeScript Types
Installation
npm install bd-geo-address
# or
yarn add bd-geo-address
# or
pnpm add bd-geo-addressQuick Start
Name-Based API
import { districtsOf, upazilasOf, isUpazila, isThana, DivisionName } from "bd-geo-address"
// All districts in Dhaka division
districtsOf(DivisionName.Dhaka)
// → ['Dhaka', 'Gazipur', 'Kishoreganj', 'Manikganj', 'Munshiganj', 'Narayanganj', ...]
// Upazilas of Tangail district
upazilasOf("Tangail")
// → [{ upazila: 'Tangail Sadar', upazilaBn: 'টাঙ্গাইল সদর', district: 'Tangail', ... }, ...]
// Correctly distinguishes upazilas from metropolitan thanas
isUpazila("Savar") // → true
isUpazila("Gulshan") // → false (Gulshan is a metropolitan thana)
isThana("Gulshan") // → trueBBS Code API
import { resolveCode, toAddressString, toShortAddress } from "bd-geo-address"
// App stores BBS geocode in the database, resolves at runtime
const address = resolveCode("30262530")
address?.formatted.en
// → 'Kafrul, Dhaka North City Corporation, Dhaka, Dhaka Division'
address?.formatted.bn
// → 'কাফরুল, ঢাকা উত্তর সিটি কর্পোরেশন, ঢাকা, ঢাকা বিভাগ'
toShortAddress("30262530") // → 'Kafrul, Dhaka'CommonJS Usage
const { allDivision, districtsOf, resolveCode, DivisionName } = require("bd-geo-address")
console.log(allDivision())
// → ['Dhaka', 'Chattogram', 'Mymensingh', 'Khulna', 'Rajshahi', 'Rangpur', 'Sylhet', 'Barisal']
console.log(resolveCode("30262530").formatted.short)
// → 'Kafrul, Dhaka'API Reference
Division
import {
allDivision, allDivisionBn,
divisionalDataOf, divisionalDataOfBn,
isValidDivision, isValidDivisionBn,
} from "bd-geo-address"
allDivision()
// → ['Dhaka', 'Chattogram', 'Mymensingh', 'Khulna', 'Rajshahi', 'Rangpur', 'Sylhet', 'Barisal']
allDivisionBn()
// → ['ঢাকা', 'চট্টগ্রাম', 'ময়মনসিংহ', 'খুলনা', 'রাজশাহী', 'রংপুর', 'সিলেট', 'বরিশাল']
divisionalDataOf("Sylhet")
// → { districts: ['Sylhet', 'Sunamganj', 'Habiganj', 'Moulvibazar'], upazilas: [...] }
divisionalDataOfBn("ঢাকা")
// → { districts: [...], upazilas: [...] }
isValidDivision("Dhaka") // → true
isValidDivision("InvalidName") // → false
isValidDivisionBn("ঢাকা") // → trueDistrict
import {
allDistricts, allDistrictsBn,
districtsOf, districtsOfBn,
isValidDistrict, isValidDistrictBn,
getDivisionOfDistrict, getDivisionOfDistrictBn,
} from "bd-geo-address"
allDistricts() // → all 64 district names in English
allDistrictsBn() // → all 64 district names in Bangla
districtsOf("Dhaka") // → ['Dhaka', 'Gazipur', 'Kishoreganj', ...]
districtsOfBn("ঢাকা") // → ['Dhaka', 'Gazipur', ...]
isValidDistrict("Tangail") // → true
isValidDistrict("Savar") // → false (upazila, not a district)
getDivisionOfDistrict("Tangail") // → 'Dhaka'
getDivisionOfDistrict("Cox's Bazar") // → 'Chattogram'
getDivisionOfDistrictBn("টাঙ্গাইল") // → 'Dhaka'Upazila
import {
allUpazila, allUpazilaNames, allUpazilaNamesBn,
upazilasOf, upazilasOfBn,
upazilaNamesOf, upazilaNamesOfBn,
upazilasOfDivision, upazilasOfDivisionBn,
upazilaNamesOfDivision, upazilaNamesOfDivisionBn,
isUpazila, isUpazilabn,
getUpazila, getUpazilabn,
getDistrictOfUpazila, getDistrictOfUpazilabn,
upazilaData,
} from "bd-geo-address"
allUpazila() // → all 595 upazila names
upazilasOf("Tangail")
// → [{ upazila: 'Tangail Sadar', upazilaBn: 'টাঙ্গাইল সদর', district: 'Tangail', ... }, ...]
upazilaNamesOf("Tangail") // → ['Tangail Sadar', 'Basail', 'Bhuapur', ...]
upazilaNamesOfBn("টাঙ্গাইল") // → Bangla upazila names for Tangail
upazilasOfDivision("Sylhet") // → all upazilas in Sylhet division
isUpazila("Savar") // → true
isUpazila("Gulshan") // → false (metropolitan thana)
isUpazilabn("সাভার") // → true
getUpazila("Savar")
// → { upazila: 'Savar', upazilaBn: 'সাভার', district: 'Dhaka', division: 'Dhaka', ... }
// Disambiguate names that appear in multiple divisions:
getUpazila("Mohammadpur", "Khulna")
// → { upazila: 'Mohammadpur', district: 'Magura', division: 'Khulna', ... }
getDistrictOfUpazila("Savar") // → 'Dhaka'
getDistrictOfUpazilabn("সাভার") // → 'Dhaka'
upazilaData // raw Upazila[] constant — all 595 objectsThana
The 26 metropolitan police thanas across Dhaka (15), Chattogram (6), Khulna (3), and Rajshahi (2).
import {
allThana, allThanaNames,
thanasOf, thanasOfBn,
thanaNamesOf, thanaNamesOfBn,
isThana, isThanabn,
getThana, getThanabn,
thanaData,
} from "bd-geo-address"
allThana() // → all 26 Thana objects
allThanaNames() // → ['Adabor', 'Badda', 'Banani', ..., 'Matihar']
thanasOf("Dhaka") // → 15 Dhaka thana objects
thanaNamesOf("Dhaka") // → ['Adabor', 'Badda', 'Banani', ...]
thanaNamesOfBn("ঢাকা") // → Bangla thana names
// isThana without district = true if it is a thana in any city
isThana("Gulshan") // → true
isThana("Kotwali", "Dhaka") // → true (disambiguate)
isThana("Kotwali", "Chattogram") // → true
isThanabn("গুলশান") // → true
getThana("Gulshan")
// → { thana: 'Gulshan', thanaBn: 'গুলশান', district: 'Dhaka', division: 'Dhaka', type: 'thana' }
getThana("Kotwali", "Chattogram")
// → { thana: 'Kotwali', district: 'Chattogram', ... }
thanaData // raw Thana[] constant — all 26 objects| City | Thanas | |------------|--------| | Dhaka | 15 | | Chattogram | 6 | | Khulna | 3 | | Rajshahi | 2 |
Search
import { searchLocations } from "bd-geo-address"
searchLocations("pur")
// → matches any division, district, upazila, or thana containing "pur"
// → [{ name: 'Rangpur', type: 'division' }, { name: 'Mirpur', type: 'thana', district: 'Dhaka' }, ...]
searchLocations("সিলেট")
// → Bangla search works the same way
// Optional second parameter filters by level
searchLocations("pur", "upazila") // only upazilas
searchLocations("pur", "district") // only districts
searchLocations("pur", "thana") // only thanas
searchLocations("pur", "division") // only divisionsBBS Code Resolution
import { resolveCode, resolveCodes } from "bd-geo-address"
// Division-level code (2 digits)
resolveCode("30")
// {
// fhirCode: '30',
// division: { name: 'Dhaka', ... },
// // district is undefined for division-level codes
// formatted: { en: 'Dhaka Division', short: 'Dhaka Division', ... }
// }
// Upazila / thana / union code (8+ digits)
resolveCode("30262530")
// {
// fhirCode: '30262530', // BBS geocode
// thana: { code: '30262530', name: 'Kafrul', level: 'thana', ... },
// city_corp:{ name: 'Dhaka North City Corporation', ... },
// district: { name: 'Dhaka', ... },
// division: { name: 'Dhaka', ... },
// formatted: {
// en: 'Kafrul, Dhaka North City Corporation, Dhaka, Dhaka Division',
// bn: 'কাফরুল, ঢাকা উত্তর সিটি কর্পোরেশন, ঢাকা, ঢাকা বিভাগ',
// short: 'Kafrul, Dhaka',
// shortBn: 'কাফরুল, ঢাকা'
// },
// postal: { postalCode: '1216', postOfficeName: 'Kafrul', ... }
// }
resolveCodes(["30262530", "10040009", "INVALID"])
// → [BDAddress, BDAddress, null]Address Formatting
import { toAddressString, toShortAddress, toCustomAddress } from "bd-geo-address"
toAddressString("30262530")
// → 'Kafrul, Dhaka North City Corporation, Dhaka, Dhaka Division'
toAddressString("30262530", "bn")
// → 'কাফরুল, ঢাকা উত্তর সিটি কর্পোরেশন, ঢাকা, ঢাকা বিভাগ'
toShortAddress("30262530") // → 'Kafrul, Dhaka'
toShortAddress("30262530", "bn") // → 'কাফরুল, ঢাকা'
toCustomAddress("30262530", ["thana", "district", "division"])
// → 'Kafrul, Dhaka, Dhaka Division'
toCustomAddress("30262530", ["thana", "district"], "bn")
// → 'কাফরুল, ঢাকা'Lookup by BBS Code
import { getByCode, findByName, findByNameBn, search, getAllByLevel } from "bd-geo-address"
getByCode("30") // → { code: '30', name: 'Dhaka', level: 'division', ... }
getByCode("INVALID") // → null
findByName("Dhaka") // → [division, district, ...]
findByName("Dhaka", "district") // → [{ name: 'Dhaka', level: 'district', ... }]
findByNameBn("ঢাকা") // → same but by Bangla name
search("Dhaka") // → English first, then Bangla fallback
getAllByLevel("division") // → all 8 division BDLocation objects
getAllByLevel("district") // → all 64 district BDLocation objects
getAllByLevel("upazila") // → all upazila BDLocation objectsName → BBS Code (Dropdown Use Case)
getCodeByName is designed for cascade-dropdown flows (division → district → upazila → thana/union). Once the user makes their final selection, pass the selected name + level + the ancestor names already selected to get the single BBS code to store in your database.
import { getCodeByName } from "bd-geo-address"
// Unique name — no ancestors needed
getCodeByName("Savar", "upazila")
// → "302645"
// Ambiguous name — same upazila name exists in multiple districts
getCodeByName("Mohammadpur", "upazila") // → null (ambiguous)
getCodeByName("Mohammadpur", "upazila", { district: "Magura" }) // → BBS code for Magura's Mohammadpur
getCodeByName("Mohammadpur", "upazila", { division: "Khulna" }) // → same, via division
// Works for all levels
getCodeByName("Dhaka", "division") // → "30"
getCodeByName("Barguna", "district") // → district BBS code
getCodeByName("Kafrul", "thana", { district: "Dhaka" }) // → "30262530"
getCodeByName("Amtali Paurasava", "paurasava") // → "1004000920"
getCodeByName("SomeUnion", "union", { district: "X", upazila: "Y" })
// Returns null when not found or still ambiguous after filteringReact example — saving the selected BBS code:
import { getCodeByName } from "bd-geo-address"
const handleUpazilaChange = (upazilaName: string) => {
setUpazila(upazilaName)
const code = getCodeByName(upazilaName, "upazila", { district, division })
setBBSCode(code) // store this in your database
}Hierarchy
import { getParent, getChildren, getAllDescendants, getHierarchy } from "bd-geo-address"
getParent("1004") // → { code: '10', name: 'Barishal', level: 'division', ... }
getParent("10") // → null (divisions have no parent)
getChildren("10") // → all direct children of Barishal division
getAllDescendants("10") // → all locations under Barishal at any depth
getAllDescendants("10", "district") // → only districts
getAllDescendants("10", "upazila") // → only upazilas
getHierarchy("30262530")
// → BDAddress without the `formatted` fieldValidation
import { isValidCode, isValidName, isValidNameBn, isChildOf, getLevel } from "bd-geo-address"
isValidCode("30262530") // → true
isValidCode("INVALID") // → false
isValidName("Dhaka") // → true
isValidName("Dhaka", "division") // → true
isValidName("Dhaka", "upazila") // → false
isValidNameBn("ঢাকা", "division") // → true
isChildOf("10040009", "10") // → true (in Barishal division)
isChildOf("10040009", "1004") // → true (in Barguna district)
isChildOf("10040009", "30") // → false (not in Dhaka)
getLevel("10") // → 'division'
getLevel("1004") // → 'district'
getLevel("10040009") // → 'upazila'
getLevel("INVALID") // → nullPostal Codes
import {
getByPostalCode, getPostalCode, getPostalCodeBn,
getPostalCodesByArea, getFhirCodeFromPostal, resolvePostalCode,
} from "bd-geo-address"
getByPostalCode("8710")
// → { postalCode: '8710', postOfficeName: 'Amtali', upazilaName: 'Amtali', districtName: 'Barguna', ... }
getPostalCode("Amtali") // → '8710'
getFhirCodeFromPostal("8710") // → '10040009' (Amtali upazila BBS code)
getPostalCodesByArea("Barguna", "district") // → all postal entries in Barguna district
getPostalCodesByArea("Barisal", "division") // → all postal entries in Barisal division
resolvePostalCode("8710")
// → full BDAddress for Amtali (same as resolveCode('10040009'))Geolocation
Coordinate data requires running
npm run build:coordswith HDX shapefiles.
import { getCoordinates, getCoordinatesByName, getNearestLocation, getLocationsWithinRadius } from "bd-geo-address"
// By BBS code
getCoordinates("30") // → { lat: 23.8103, lng: 90.4125 }
// By name — level and ancestors optional, same disambiguation rules as getCodeByName
getCoordinatesByName("Savar", "upazila")
// → { lat: 23.8576, lng: 90.2659 }
getCoordinatesByName("Mohammadpur", "upazila", { district: "Magura" })
// → { lat, lng } for Mohammadpur in Magura district
getCoordinatesByName("Dhaka", "division") // → Dhaka division centroid
getCoordinatesByName("Dhaka") // → first Dhaka entry with coordinates
getNearestLocation(23.7, 90.4) // → nearest BDLocation to the point
getNearestLocation(23.7, 90.4, "upazila") // → nearest upazila
getLocationsWithinRadius(23.7, 90.4, 50) // → all locations within 50 kmTypeScript Types
import type {
DivisionName, // enum
DistrictName, // string literal union of all 64 districts
LocationLevel, // 'division' | 'district' | 'city_corp' | 'municipality' | 'upazila' | 'thana' | 'union' | 'paurasava'
Upazila,
Thana,
SearchResult,
BDLocation,
BDAddress,
BDPostalEntry,
} from "bd-geo-address"BDAddress — resolved BBS geocode result:
interface BDAddress {
fhirCode: string
division: BDLocation
district?: BDLocation // absent for division-level codes
city_corp?: BDLocation
municipality?: BDLocation
upazila?: BDLocation
thana?: BDLocation
union?: BDLocation
paurasava?: BDLocation
formatted: { en: string; bn: string; short: string; shortBn: string }
coordinates?: { lat: number; lng: number }
postal?: { postalCode: string; postOfficeName: string; postOfficeNameBn: string }
}DivisionName enum — use for IDE autocomplete:
import { DivisionName } from "bd-geo-address"
DivisionName.Dhaka // → 'Dhaka'
DivisionName.Chattogram // → 'Chattogram'
// Dhaka, Chattogram, Mymensingh, Khulna, Rajshahi, Rangpur, Sylhet, BarisalFramework Examples
React — Cascade Location Picker
import { useState } from "react"
import { allDivision, districtsOf, upazilasOf } from "bd-geo-address"
export function LocationPicker({ onChange }) {
const [division, setDivision] = useState("")
const [district, setDistrict] = useState("")
const districts = division ? districtsOf(division) : []
const upazilas = district ? upazilasOf(district) : []
return (
<div>
<select onChange={e => { setDivision(e.target.value); setDistrict("") }}>
<option value="">Division</option>
{allDivision().map(d => <option key={d} value={d}>{d}</option>)}
</select>
<select onChange={e => setDistrict(e.target.value)} disabled={!division}>
<option value="">District</option>
{districts.map(d => <option key={d} value={d}>{d}</option>)}
</select>
<select onChange={e => onChange(e.target.value)} disabled={!district}>
<option value="">Upazila</option>
{upazilas.map(u => (
<option key={u.upazila} value={u.upazila}>{u.upazilaBn} ({u.upazila})</option>
))}
</select>
</div>
)
}Next.js — Server Component
import { resolveCode } from "bd-geo-address"
export default async function UserPage({ params }) {
const user = await fetchUser(params.id)
const address = resolveCode(user.locationCode)
return (
<div>
<p>{address?.formatted.en}</p>
<p>{address?.formatted.bn}</p>
</div>
)
}Vue 3
<template>
<select v-model="selectedDivision">
<option v-for="d in divisions" :key="d" :value="d">{{ d }}</option>
</select>
<select v-model="selectedDistrict">
<option v-for="d in districts" :key="d" :value="d">{{ d }}</option>
</select>
</template>
<script setup lang="ts">
import { ref, computed } from "vue"
import { allDivision, districtsOf } from "bd-geo-address"
const selectedDivision = ref("")
const divisions = allDivision()
const districts = computed(() =>
selectedDivision.value ? districtsOf(selectedDivision.value) : []
)
</script>Node.js / Express
import express from "express"
import { isValidCode, resolveCode, upazilasOf } from "bd-geo-address"
const app = express()
app.get("/address/:code", (req, res) => {
if (!isValidCode(req.params.code)) return res.status(400).json({ error: "Invalid geocode" })
res.json(resolveCode(req.params.code))
})
app.get("/upazilas/:district", (req, res) => {
res.json(upazilasOf(req.params.district))
})NestJS
import { Injectable, BadRequestException } from "@nestjs/common"
import { resolveCode, isValidCode, BDAddress } from "bd-geo-address"
@Injectable()
export class LocationService {
resolve(bbsCode: string): BDAddress {
if (!isValidCode(bbsCode)) throw new BadRequestException("Invalid BD BBS geocode")
return resolveCode(bbsCode)!
}
}React Native
Works out of the box — no config needed.
import { useState } from "react"
import { View, Text } from "react-native"
import { Picker } from "@react-native-picker/picker"
import { allDivision, districtsOf, upazilasOf, getCodeByName } from "bd-geo-address"
export function LocationPicker({ onCode }: { onCode: (code: string) => void }) {
const [division, setDivision] = useState("")
const [district, setDistrict] = useState("")
const [upazila, setUpazila] = useState("")
const districts = division ? districtsOf(division) : []
const upazilas = district ? upazilasOf(district) : []
const handleUpazila = (name: string) => {
setUpazila(name)
const code = getCodeByName(name, "upazila", { district, division })
if (code) onCode(code) // BBS code ready to store in DB
}
return (
<View>
<Picker selectedValue={division} onValueChange={v => { setDivision(v); setDistrict(""); setUpazila("") }}>
<Picker.Item label="Select Division" value="" />
{allDivision().map(d => <Picker.Item key={d} label={d} value={d} />)}
</Picker>
<Picker selectedValue={district} onValueChange={v => { setDistrict(v); setUpazila("") }} enabled={!!division}>
<Picker.Item label="Select District" value="" />
{districts.map(d => <Picker.Item key={d} label={d} value={d} />)}
</Picker>
<Picker selectedValue={upazila} onValueChange={handleUpazila} enabled={!!district}>
<Picker.Item label="Select Upazila" value="" />
{upazilas.map(u => <Picker.Item key={u.upazila} label={`${u.upazilaBn} (${u.upazila})`} value={u.upazila} />)}
</Picker>
</View>
)
}Database Dumps
For PHP, Python, Java, Go, Ruby or any non-JS project, database dumps are included in the package.
| File | Format |
|------|--------|
| db/mysql/bangladesh-address.sql | MySQL 8.0+ |
| db/postgresql/bangladesh-address.sql | PostgreSQL 13+ |
| db/sqlite/bangladesh-address.db | SQLite 3 binary |
| db/mongodb/bangladesh-address.json | MongoDB insertMany-ready |
Schema:
divisions— 8 rows (code, name, name_bn, lat, lng)districts— 64 rows (code, name, name_bn, division_code, lat, lng)locations— 1519 rows (all other BBS locations: city corps, upazilas, unions, etc.)thanas— 26 metropolitan thanaspostal_codes— 1343 postal code entries
MySQL example:
mysql -u root -p mydb < node_modules/bd-geo-address/db/mysql/bangladesh-address.sqlPostgreSQL example:
psql -U postgres mydb < node_modules/bd-geo-address/db/postgresql/bangladesh-address.sqlSQLite example:
cp node_modules/bd-geo-address/db/sqlite/bangladesh-address.db ./my-app.dbMongoDB example:
const data = require("bd-geo-address/db/mongodb/bangladesh-address.json")
await db.collection("divisions").insertMany(data.divisions)
await db.collection("locations").insertMany(data.locations)Data Coverage
| Level | Count | |-------|-------| | Divisions | 8 | | Districts | 64 | | City Corporations | 12 | | Upazilas | 595 | | Metropolitan Thanas | 26 | | Unions | 848 | | Postal codes | 1343 |
Data Sources & Credits
- BBS geocodes — Bangladesh Bureau of Statistics (BBS)
- Postal codes — saaiful/postcode-bd
- Coordinates (optional) — HDX Bangladesh Admin Boundaries
- Bangla names — HDX shapefiles + manual curation
Contributing
See CONTRIBUTING.md for guidelines on fixing data, adding tests, or improving the API.
