Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/prisma/prisma-next/llms.txt

Use this file to discover all available pages before exploring further.

The PostGIS extension pack adds the geometry column type and seven spatial query operations to Prisma Next for PostgreSQL databases. You store points, linestrings, polygons, and their multi-geometry variants as GeoJSON-shaped values, attach an optional SRID (e.g. 4326 for WGS84 lng/lat), and query them with distance, containment, and intersection operations — all without learning PostGIS-specific client APIs.

What the extension provides

  • Geometry column type — stores Points, LineStrings, Polygons, and their Multi-* variants. Accepts an optional SRID parameter (Geometry(4326) for WGS84 lng/lat).
  • GeoJSON-shaped runtime values — read and write geometries as plain { type, coordinates, srid? } objects. Constructors from @prisma-next/extension-postgis/geojson handle coordinate ordering and SRID metadata.
  • Seven query operationsdistance, distanceSphere, dwithin, contains, within, intersects, intersectsBbox — available in .where(), .select(), and .orderBy().
  • Automatic CREATE EXTENSION — the control descriptor declares postgis as a database dependency, so prisma-next db init installs it before the first migration runs.
  • Capability flag — declares postgis.geometry to gate the codec and all seven operations.

Prerequisites

The PostGIS extension must be available on your PostgreSQL server. Most managed providers (RDS, Cloud SQL, Supabase, Neon) include it. For local development, use the postgis/postgis Docker image or imresamu/postgis on Apple Silicon hosts.

Setup

1

Install the package

pnpm add @prisma-next/extension-postgis
2

Register the extension in prisma-next.config.ts

prisma-next.config.ts
import { defineConfig } from '@prisma-next/cli/config-types';
import postgresAdapter from '@prisma-next/adapter-postgres/control';
import sql from '@prisma-next/family-sql/control';
import postgres from '@prisma-next/target-postgres/control';
import postgis from '@prisma-next/extension-postgis/control';

export default defineConfig({
  family: sql,
  target: postgres,
  adapter: postgresAdapter,
  extensionPacks: [postgis],
});
3

Declare geometry columns in your schema

Use postgis.Geometry(srid) in PSL or the TypeScript contract builder.
// use prisma-next

types {
  WgsGeometry = postgis.Geometry(4326)
}

model Cafe {
  id       String      @id @default(uuid())
  name     String
  location WgsGeometry
  @@map("cafe")
}
postgis.Geometry(srid) and geometry({ srid }) are the SRID-constrained forms — they emit geometry(Geometry, 4326) in DDL. Use postgis.Geometry() or geometryColumn for schemas that mix SRIDs at the row level.
4

Emit the contract and initialize the database

pnpm prisma-next contract emit
pnpm prisma-next db init
db init runs CREATE EXTENSION postgis before any application migration.
5

Wire the extension into the runtime

src/prisma/db.ts
import postgres from '@prisma-next/postgres/runtime';
import postgis from '@prisma-next/extension-postgis/runtime';
import type { Contract } from './contract.d';
import contractJson from './contract.json' with { type: 'json' };

export const db = postgres<Contract>({
  contractJson,
  extensions: [postgis],
  url: process.env['DATABASE_URL']!,
});

Building geometry values

Use the constructors from @prisma-next/extension-postgis/geojson to build geometry values. They handle coordinate ordering ([lng, lat]) and SRID metadata.
import { bboxPolygon, point, polygon } from '@prisma-next/extension-postgis/geojson';

// Point — longitude first, latitude second.
const sightglass = point(-122.4106, 37.7765, 4326);

// Polygon — outer ring, optionally followed by holes. First point must equal last.
const soma = polygon(
  [
    [-122.418, 37.77],
    [-122.4, 37.77],
    [-122.4, 37.785],
    [-122.418, 37.785],
    [-122.418, 37.77],
  ],
  4326,
);

// Axis-aligned bbox polygon — handy for map-viewport queries.
const viewport = bboxPolygon([-122.425, 37.775, -122.4, 37.8], 4326);
For LineString, MultiPoint, MultiLineString, and MultiPolygon, construct the object directly:
import type { Geometry } from '@prisma-next/extension-postgis/codec-types';

const route: Geometry = {
  type: 'LineString',
  coordinates: [[-122.4194, 37.7749], [-122.4106, 37.7765]],
  srid: 4326,
};

Querying

All seven operations are available inside .where(), .select(), and .orderBy() callbacks via the fns argument.
import { point } from '@prisma-next/extension-postgis/geojson';
import { db } from './prisma/db';

const ferryBuilding = point(-122.3937, 37.7955, 4326);

// Five nearest cafes with their distance in metres.
const closest = await db.runtime().execute(
  db.sql.cafe
    .select('id', 'name')
    .select('meters', (f, fns) => fns.distanceSphere(f.location, ferryBuilding))
    .orderBy((f, fns) => fns.distanceSphere(f.location, ferryBuilding), { direction: 'asc' })
    .limit(5)
    .build(),
);

// Point-in-polygon — which neighborhood contains this point?
const neighborhood = await db.runtime().execute(
  db.sql.neighborhood
    .select('id', 'name')
    .where((f, fns) => fns.contains(f.boundary, sightglass))
    .build(),
);

// Index-friendly bounding-box filter for map viewport queries.
const inView = await db.runtime().execute(
  db.sql.cafe
    .select('id', 'name')
    .where((f, fns) => fns.intersectsBbox(f.location, viewport))
    .build(),
);
User-supplied geometries (the second argument to each operation) are bound as parameters — not interpolated. The codec encodes them as EWKT on the wire.

Operations reference

MethodSQLReturnsUse when
distance(other)ST_Distance(self, other)float8Cartesian distance in the geometry’s native units (degrees for SRID 4326).
distanceSphere(other)ST_DistanceSphere(self, other)float8Sphere-accurate metres between two lng/lat geometries.
dwithin(other, distance)ST_DWithin(self, other, distance)booleanIndex-friendly “within X distance” check. Distance is in the geometry’s native units.
contains(other)ST_Contains(self, other)booleanPoint-in-polygon and polygon-contains-polygon checks.
within(other)ST_Within(self, other)booleanInverse of containsA within B is equivalent to B contains A.
intersects(other)ST_Intersects(self, other)booleanAny kind of overlap between two geometries.
intersectsBbox(other)self && otherbooleanCheap 2-D bounding-box overlap — fast viewport filtering.

Picking the right distance operation

For SRID 4326 (WGS84) lng/lat data, distance returns degrees, which is rarely what you want. Use distanceSphere to get human-friendly metres. dwithin interprets its distance argument in whatever units the inputs use — pair it with a projected SRS if you need metres directly.

SRID and units

  • Declare an SRID at the column level (postgis.Geometry(4326) or geometry({ srid: 4326 })) to constrain the column and have the runtime preserve the SRID through writes.
  • The point, polygon, and bboxPolygon constructors attach the SRID to the value. The codec emits it as SRID=4326;… EWKT on the wire.
  • distance and dwithin are not unit-aware. Prefer distanceSphere for WGS84 metre-based comparisons.

Wire format

  • JS → SQL: geometries are emitted as EWKT (SRID=4326;POINT(-122.39 37.79)) and cast to ::geometry.
  • SQL → JS: node-postgres returns geometry columns as hex-encoded EWKB. The codec parses Point, LineString, Polygon, MultiPoint, MultiLineString, and MultiPolygon.
Z and M coordinates are not supported in this release. If a geometry column carries them, decoding throws so the mismatch is visible rather than silent.

Types

import type { CodecTypes, Geometry } from '@prisma-next/extension-postgis/codec-types';
import type { OperationTypes, QueryOperationTypes } from '@prisma-next/extension-postgis/operation-types';

// CodecTypes['pg/geometry@1']['output'] is Geometry (the GeoJSON union type)
// Geometry<4326> is the SRID-branded form rendered into contract.d.ts

Capability flag

The extension declares postgis.geometry. Features that require PostGIS geometry support can declare a requires: ['postgis.geometry'] constraint in their capability gate.

Build docs developers (and LLMs) love