A lightweight, type-safe Node.js library for interacting with PostGIS-enabled PostgreSQL databases. Export GeoJSON, serve MVT vector tiles, run spatial queries, and more — with zero runtime dependencies. Designed as a high-performance open source javascript library and nodejs utility for developers.
| Feature | Details |
|---|---|
| 🔷 TypeScript First | Full type definitions, IntelliSense, and compile-time safety |
| 📦 Dual ESM + CJS | Tree-shakable ESM + CommonJS fallback — works everywhere |
| ⚡ Zero Runtime Deps | Bring your own pg client, nothing else required |
| 🗺️ Spatial Queries | bbox, centroid, intersections, nearest, coordinate transforms |
| 📄 GeoJSON Export | Full FeatureCollection with bounds, ID, and precision control |
| 🗜️ Geobuf Export | Binary-compact protobuf encoding via ST_AsGeobuf |
| 🧱 MVT Tiles | Server-side Mapbox Vector Tiles via ST_AsMVT |
| 🧪 Fully Tested | 50+ unit tests with mocked pg.Client — no database needed |
npm install postgis pgyarn add postgis pgpnpm add postgis pgRequirements: Node.js ≥ 18, PostgreSQL ≥ 12, PostGIS ≥ 3.0
import { Client } from 'pg';
import Postgis from 'postgis';
const client = new Client({ connectionString: process.env.DATABASE_URL });
await client.connect();
const postgis = new Postgis(client);
// List spatial tables
const tables = await postgis.list_tables();
// Export as GeoJSON
const fc = await postgis.geojson('parcels', { precision: 6, columns: 'name, area' });
console.log(fc.type); // "FeatureCollection"
// Find 5 nearest hospitals
const nearby = await postgis.nearest('hospitals', '73.5,14.9,4326', { limit: 5 });
await client.end();CommonJS:
const Postgis = require('postgis');
const postgis = new Postgis(client);new Postgis(client: PostgisClient)Accepts any pg.Client, pg.Pool client, or any object with a query(sql) method.
Lists all user-accessible tables with PostGIS geometry metadata.
const tables = await postgis.list_tables({ filter: "table_type = 'BASE TABLE'" });Lists all columns of a table using PostgreSQL system catalogs.
const cols = await postgis.list_columns('parcels');
// [{ field_name: 'geom', field_type: 'geometry' }, ...]Flexible SELECT with filter, group, sort, and limit.
const rows = await postgis.query_table('parcels', {
columns: 'id, name',
filter: "status = 'active'",
sort: 'name ASC',
limit: 50,
});Returns the spatial bounding box (ST_Extent) of all geometries.
const [{ bbox }] = await postgis.bbox('parcels', { srid: 4326 });Returns the centroid (x, y) of each geometry.
const pts = await postgis.centroid('parcels', { force_on_surface: true });Cross-table spatial intersection using ST_DWithin.
const hits = await postgis.intersect_feature('roads', 'parcels', { distance: '50' });Features within distance of a point.
// point format: "longitude,latitude,SRID"
const nearby = await postgis.intersect_point('shops', '73.5,14.9,4326', {
distance: '500',
limit: 20,
});Exports a GeoJSON FeatureCollection.
const fc = await postgis.geojson('parcels', {
bounds: '72.8,18.9,73.2,19.2', // xmin,ymin,xmax,ymax
precision: 6,
columns: 'name, area',
});
// { type: 'FeatureCollection', features: [...] }Exports features as a Geobuf binary Buffer.
const buf = await postgis.geobuf('parcels');
res.setHeader('Content-Type', 'application/x-protobuf');
res.send(buf);Generates a Mapbox Vector Tile for tile coordinate z/x/y.
const [{ mvt }] = await postgis.mvt('parcels', 0, 0, 0);
res.setHeader('Content-Type', 'application/vnd.mapbox-vector-tile');
res.send(mvt);KNN nearest-neighbor search ordered by <-> distance operator.
const closest = await postgis.nearest('hospitals', '73.5,14.9,4326', { limit: 5 });
// Each row includes a `distance` columnTransforms a point between coordinate systems.
const [{ x, y }] = await postgis.transform_point('73.5,14.9,4326', { srid: 3857 });| Variable | Description |
|---|---|
DATABASE_URL |
PostgreSQL connection string |
POSTGIS_DEBUG |
Set to 'true' to log all SQL queries to stderr |
Methods accepting a point argument use "x,y,srid" format:
73.70534,14.94202,4326 → longitude,latitude,EPSG:4326
app.get('/tiles/:z/:x/:y.mvt', async (req, res) => {
const client = await pool.connect();
try {
const postgis = new Postgis(client);
const [row] = await postgis.mvt('parcels', +req.params.x, +req.params.y, +req.params.z);
if (!row?.mvt) return res.status(204).end();
res
.setHeader('Content-Type', 'application/vnd.mapbox-vector-tile')
.setHeader('Cache-Control', 'public, max-age=3600')
.send(row.mvt);
} finally {
client.release();
}
});const client = await pool.connect();
const postgis = new Postgis(client);
try {
return await postgis.geojson('my_layer');
} finally {
client.release();
}- Spatial indexes are essential for large tables. Add a GiST index:
CREATE INDEX ON my_layer USING GIST(geom);
- MVT queries internally call
ST_TileEnvelope— PostGIS 3.0+ required. - Bounds filtering avoids full-table scans when a
boundsargument is provided. nearest()uses the index-accelerated<->KNN operator — much faster thanORDER BY ST_Distance.
Warning
SQL values (table names, column names, filters) are interpolated directly into SQL strings — not parameterized. Never pass raw user input as table, filter, or columns arguments.
See SECURITY.md for the full security policy and reporting process.
See the examples/ directory:
| File | Description |
|---|---|
basic-usage.js |
List tables, columns, query, bbox |
geojson-export.js |
Export GeoJSON and Geobuf to files |
mvt-server.js |
Express MVT tile server with Mapbox GL JS viewer |
Encountering issues like permission denied, missing st_asmvt function, or timeouts?
Check out our comprehensive Troubleshooting Guide for quick solutions to the most common PostGIS and Node.js database hurdles.
Does it work with pg.Pool?
Yes — acquire a client from the pool, pass it to new Postgis(client), and release it after.
CommonJS or ESM?
Both. Import with import Postgis from 'postgis' (ESM) or const Postgis = require('postgis') (CJS).
Any runtime dependencies?
None. pg is a peer dependency — you install and manage it yourself.
Full FAQ: jsuyog2.github.io/postgis/guide/faq
We welcome contributions! Please read CONTRIBUTING.md for guidelines on:
- Setting up the dev environment
- Running tests (
npm test) - Submitting pull requests
See CHANGELOG.md for version history.