Skip to content
🐘

PgShift

Start with Postgres. Shift only when you must.

Most teams reach for Redis, Kafka, and Elasticsearch before they actually need them. The result is fragile distributed infrastructure that costs more to operate than the product earns.

PostgreSQL already handles full-text search, job queues, pub/sub messaging, query result caching, and vector search natively. PgShift gives you a clean, consistent API on top of those capabilities, and tells you exactly when it is time to move on.

Search

Full-text search with typo tolerance via tsvector and pg_trgm. No Elasticsearch cluster required.

Cache

Pre-compute expensive queries via materialized views. Instant reads, non-blocking refresh.

Queue

At-least-once job processing via SKIP LOCKED. Retries, dead letter queue, and priority built in.

More modules available

PgShift also includes realtime messaging, pub/sub, and other Postgres-powered infrastructure primitives. Explore the documentation to discover all available modules and migration strategies.

Terminal window
npm install @pgshift/search
import { createClient } from '@pgshift/search'
const db = createClient({ url: process.env.DATABASE_URL })
await db.search('products').index({
fields: ['name', 'description'],
weights: { name: 'A', description: 'B' },
fuzzy: true,
})
await db.search('products').upsert('1', {
name: 'Nike Air Max 90',
description: 'Classic sneaker.',
category: 'shoes',
})
const results = await db.search('products').query('air maxx', {
fuzzy: true,
filters: { category: 'shoes' },
})

PgShift tracks latency for every operation. When a module consistently hits the limits of what Postgres can handle efficiently, it emits a migration hint, telling you exactly when it is time to move to Elasticsearch, Redis, Pinecone, or Kafka.

const db = createClient({
url: process.env.DATABASE_URL,
onMigrationHint(hint) {
console.warn(`Consider migrating ${hint.module} to ${hint.suggestedAdapter}.`)
console.warn(hint.reason)
},
})

When the time comes, the migration is infrastructure work, not application code. Your db.search().query(), db.cache().get(), db.queue().push(), and db.vector().query() calls stay exactly the same.