npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

postgis

v1.2.0

Published

A lightweight, type-safe Node.js library for interacting with PostGIS-enabled PostgreSQL databases. Supports GeoJSON, Geobuf, MVT tiles, spatial queries, and more.

Readme

postgis

npm version npm downloads Build Status Coverage License: MIT TypeScript

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.

📖 Full Documentation →


Features

| 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 |


Installation

npm install postgis pg
yarn add postgis pg
pnpm add postgis pg

Requirements: Node.js ≥ 18, PostgreSQL ≥ 12, PostGIS ≥ 3.0


Quick Start

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);

API Documentation

Constructor

new Postgis(client: PostgisClient)

Accepts any pg.Client, pg.Pool client, or any object with a query(sql) method.


list_tables(options?)

Lists all user-accessible tables with PostGIS geometry metadata.

const tables = await postgis.list_tables({ filter: "table_type = 'BASE TABLE'" });

list_columns(table)

Lists all columns of a table using PostgreSQL system catalogs.

const cols = await postgis.list_columns('parcels');
// [{ field_name: 'geom', field_type: 'geometry' }, ...]

query_table(table, options?)

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,
});

bbox(table, options?)

Returns the spatial bounding box (ST_Extent) of all geometries.

const [{ bbox }] = await postgis.bbox('parcels', { srid: 4326 });

centroid(table, options?)

Returns the centroid (x, y) of each geometry.

const pts = await postgis.centroid('parcels', { force_on_surface: true });

intersect_feature(table_from, table_to, options?)

Cross-table spatial intersection using ST_DWithin.

const hits = await postgis.intersect_feature('roads', 'parcels', { distance: '50' });

intersect_point(table, point, options?)

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,
});

geojson(table, options?)

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: [...] }

geobuf(table, options?)

Exports features as a Geobuf binary Buffer.

const buf = await postgis.geobuf('parcels');
res.setHeader('Content-Type', 'application/x-protobuf');
res.send(buf);

mvt(table, x, y, z, options?)

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);

nearest(table, point, options?)

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` column

transform_point(point, options?)

Transforms a point between coordinate systems.

const [{ x, y }] = await postgis.transform_point('73.5,14.9,4326', { srid: 3857 });

Configuration

Environment Variables

| Variable | Description | |---|---| | DATABASE_URL | PostgreSQL connection string | | POSTGIS_DEBUG | Set to 'true' to log all SQL queries to stderr |

Point Format

Methods accepting a point argument use "x,y,srid" format:

73.70534,14.94202,4326  →  longitude,latitude,EPSG:4326

Advanced Usage

MVT Tile Server (Express)

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();
  }
});

Using with pg.Pool

const client = await pool.connect();
const postgis = new Postgis(client);
try {
  return await postgis.geojson('my_layer');
} finally {
  client.release();
}

Performance Notes

  • 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 bounds argument is provided.
  • nearest() uses the index-accelerated <-> KNN operator — much faster than ORDER BY ST_Distance.

Security

[!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.


Examples

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 |


FAQ

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


Contributing

We welcome contributions! Please read CONTRIBUTING.md for guidelines on:

  • Setting up the dev environment
  • Running tests (npm test)
  • Submitting pull requests

Changelog

See CHANGELOG.md for version history.

License

MIT © Suyog Dinesh Jadhav