Why I Chose PostGIS Over an ORM for Bean Remote
Why I Chose PostGIS Over an ORM for Bean Remote
When I built Bean Remote - a mobile app that helps remote workers find great cafes to work from - I had to solve a core technical challenge:
How do you fetch a large set of spatial data (cafes) and display it instantly on a map, filtered by location or screen boundaries?
You might think any ORM like Prisma, Sequelize, or TypeORM could handle that. But after testing and research, I made a conscious choice to go directly with PostGIS - the spatial extension for PostgreSQL.
Here’s why I did it, what I learned, and how it shaped the performance of my app.
🧭 The Use Case: Geo-filtered Cafe Listings
Bean Remote uses:
- React Native + Expo on the frontend
- Node.js + Express on the backend
- PostgreSQL + PostGIS for data and geospatial queries
I wanted users to:
- See all nearby cafes within a certain radius
- Filter cafes within the current map screen bounds
- Eventually support search by travel time or availability
ORMs typically abstract away SQL complexity, but in this case… abstraction became a bottleneck.
🚀 Why PostGIS Is Built for This
1. Powerful and Fast Spatial Indexing
I store cafe and user locations as:
location GEOGRAPHY(Point, 4326)
And I apply a GIST index for fast lookups:
CREATE INDEX idx_cafes_location ON cafes USING GIST (location);
With PostGIS, I can now use ultra-efficient spatial queries like:
SELECT * FROM cafes
WHERE ST_DWithin(
location,
ST_SetSRID(ST_MakePoint(:lng, :lat), 4326),
:radius
);
This will instantly return all cafes within X meters of the user - using the index.
✅ Fast
✅ Accurate
✅ No post-filtering required
2. ORMs Fall Short on Native Spatial Operations
ORMs like Prisma or Sequelize do support raw SQL, but their native support for spatial features is limited or non-performant.
Example: Prisma
Prisma recently introduced spatial support via raw SQL extensions, but:
- No built-in support for
ST_DWithin
,ST_Intersects
, orST_MakeEnvelope
- No automatic index hints
- Joins and spatial operations often need to be written manually anyway
ORM layers often rely on application-level filtering, especially for complex geometry comparisons - a major performance bottleneck on mobile.
3. Real-World Benchmarks Back It Up
In a test run with 10,000+ location points:
Operation | ORM (Sequelize) | PostGIS |
---|---|---|
Nearby search (2km) | 200–300ms | ~15–30ms |
Bounding box query | 300ms+ | ~20ms |
Result accuracy | Filtered manually | Handled by DB |
Source: GIS StackExchange Discussion
Also see: PostGIS Index Performance Guide
✨ Simplicity and Power in SQL
I also use PostGIS for things like:
- Ordering results by distance (
ST_Distance
) - Screen-based bounding box filtering using
ST_MakeEnvelope(...)
- Filtering only verified cafes in the same query
SELECT * FROM cafes
WHERE is_verified = true
AND ST_Within(
location,
ST_MakeEnvelope(:west, :south, :east, :north, 4326)
);
This would be extremely difficult (or messy) to express in most ORMs.
🧱 My Cafe & User Tables (with Indexing)
CREATE TABLE cafes (
id SERIAL PRIMARY KEY,
name VARCHAR(255),
location GEOGRAPHY(Point, 4326),
...
);
CREATE INDEX idx_cafes_location ON cafes USING GIST (location);
CREATE TABLE users (
id SERIAL PRIMARY KEY,
location GEOGRAPHY(Point, 4326),
...
);
CREATE INDEX idx_users_location ON users USING GIST (location);
These indexes are the secret sauce - enabling real-time map updates as users pan or zoom.
⚖️ ORM vs PostGIS: What to Choose?
Feature | ORM | PostGIS |
---|---|---|
Simple CRUD | ✅ | ✅ |
Built-in spatial support | ❌ Limited | ✅ Full |
Index optimization | ❌ Manual | ✅ Native GIST, SP-GiST, BRIN |
Advanced geo queries | ❌ Hard to express | ✅ Easy + fast |
Ideal for real-time maps | ❌ No | ✅ Yes |
🧩 Final Thoughts
For most apps, ORMs are great. But for geolocation-heavy apps - like map-based mobile apps, delivery platforms, ride-sharing, or proximity search tools:
➡️ PostGIS gives you the control, speed, and spatial power you need.
You skip the abstraction, but you gain reliability, precision, and scalability.
Bean Remote wouldn’t be as fast or as clean without it.