PostGIS extension for Prisma — typed spatial queries, automatic WKB↔GeoJSON conversion, and geometry-aware findMany with mixed scalar + geo filters.
bun add @stratapkg/spatialgenerator spatial {
provider = "stratapkg-spatial"
output = "./generated/spatial"
}model City {
id Int @id @default(autoincrement())
name String
/// @spatial srid=4326
location Unsupported("geometry(Point,4326)")?
/// @spatial srid=4326
boundary Unsupported("geometry(Polygon,4326)")?
}bunx prisma generateimport { PrismaClient } from './generated/client'
import { withSpatial } from '@stratapkg/spatial'
import { spatialConfig } from './generated/spatial'
const prisma = new PrismaClient({ adapter })
.$extends(withSpatial(spatialConfig))await prisma.city.findMany({
where: {
population: { gte: 100_000 },
location: {
$near: { lat: 59.9, lon: 30.3 },
$maxDistance: 5000, // metres
},
},
orderBy: {
location: { $near: { lat: 59.9, lon: 30.3 } },
},
take: 10,
})await prisma.city.findMany({
where: {
boundary: { $intersects: someGeoJsonPolygon },
},
})await prisma.city.findMany({
where: {
location: { $within: someGeoJsonPolygon },
},
})Geometry columns are returned as GeoJSON automatically — no manual WKB parsing needed.
The generator reads /// doc comments on Unsupported fields and emits:
generated/spatial/index.ts— config map with SRID and geometry type per fieldgenerated/spatial/types.d.ts—GeometryPoint<SRID>,GeometryPolygon<SRID>etc.
The runtime extension intercepts findMany/findFirst/findUnique, extracts geo operators from where/orderBy, and builds a single CTE query:
WITH scalar_filter AS (
SELECT id FROM "City" WHERE population >= 100000
)
SELECT c.*, ST_AsGeoJSON(c.location) as location
FROM "City" c
INNER JOIN scalar_filter sf ON c.id = sf.id
WHERE ST_DWithin(
c.location::geography,
ST_SetSRID(ST_Point(30.3, 59.9), 4326)::geography,
5000
)
ORDER BY c.location <-> ST_SetSRID(ST_Point(30.3, 59.9), 4326)
LIMIT 10One query. No N+1.
- Prisma >= 7.0
- PostgreSQL with PostGIS extension enabled
- Any Prisma adapter (adapter-agnostic)
MIT