nuxt-pg
nuxt-pg
PostgreSQL database module for Nuxt with automatic transaction management
nuxt-pg provides a clean, type-safe PostgreSQL integration for Nuxt with automatic transaction context tracking. Write cleaner code by eliminating transaction parameter passing throughout your application.
Features
- Automatic Transaction Context - No need to pass transaction objects through function calls
- Lazy Initialization - Database initializes on first
getPg()call, giving you full control over startup order - DevTools Integration - Switch between database connections in Nuxt DevTools during development
- Connection Hooks - Listen for connection switch events via Nitro hooks
- Type-Safe - Full TypeScript support with proper type inference
- Connection Pooling - Built-in connection pool management with
node-postgres - Flexible Pool Configuration - Customize pool settings globally or per-connection
- Request Isolation - Transactions are automatically scoped per-request
- Graceful Shutdown - Properly closes connections on server shutdown
Installation
npm install nuxt-pg pg
Quick Start
1. Add to your nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-pg'],
nuxtPg: {
connectionString: process.env.DATABASE_URL || '',
},
})
2. Use in your API routes
// server/api/users/[id].get.ts
export default defineEventHandler(async (event) => {
const db = getPg()
const id = getRouterParam(event, 'id')
const users = await db.query(
'SELECT * FROM users WHERE id = $1',
[id]
)
return users[0]
})
getPg() is auto-imported in all server code. The first call initializes the database connection; subsequent calls return the existing singleton.
Usage
Basic Queries
const db = getPg()
// Simple query
const users = await db.query('SELECT * FROM users')
// With parameters
const user = await db.query(
'SELECT * FROM users WHERE email = $1',
['user@example.com']
)
// With TypeScript types
interface User {
id: string
name: string
email: string
}
const users = await db.query<User>(
'SELECT * FROM users WHERE active = $1',
[true]
)
Transactions
Automatic (Recommended)
export default defineEventHandler(async (event) => {
const db = getPg()
return await db.transactional(async (txn) => {
await txn.query('INSERT INTO users (name) VALUES ($1)', ['Alice'])
await txn.query('INSERT INTO profiles (user_id) VALUES ($1)', [userId])
// Automatically commits on success, rolls back on error
return { success: true }
})
})
Manual Control
export default defineEventHandler(async (event) => {
const db = getPg()
await db.createTransaction()
try {
await db.query('INSERT INTO users ...')
await db.query('UPDATE profiles ...')
await db.commitTransaction()
return { success: true }
} catch (error) {
await db.rollbackTransaction()
throw error
}
})
Context-Aware Queries
Transactions are tracked per-request — no need to pass transaction objects through your call stack:
// user.repo.ts
export class UserRepository {
static async create(data: CreateUserData) {
// Automatically uses the active transaction if one exists
return getPg().query('INSERT INTO users ...', [...])
}
}
// handler.ts
export default defineEventHandler(async (event) => {
const db = getPg()
return await db.transactional(async () => {
// Both use the same transaction automatically
await UserRepository.create({ name: 'Alice' })
await ProfileRepository.create(userId, { bio: 'Hello' })
})
})
Configuration
Module Options
Configure nuxt-pg under the nuxtPg key in your nuxt.config.ts:
export default defineNuxtConfig({
modules: ['nuxt-pg'],
nuxtPg: {
enabled: true, // default: true
connectionString: '', // production connection string
pool: { // global pool settings
maxConnections: 30,
minConnections: 2,
idleTimeoutMs: 30000,
connectionTimeoutMs: 2000,
ssl: true,
},
dev: { // dev-only settings
connections: { // named connections for switching
local: {
connectionString: process.env.DATABASE_URL_DEV || '',
pool: { ssl: false },
},
staging: {
connectionString: process.env.DATABASE_URL_STAGING || '',
},
},
defaultConnection: 'local', // which connection to use initially
},
devtools: { // devtools panel settings
apiUrl: '', // remote API URL (see DevTools section)
},
},
})
When enabled is false, the module is entirely stripped — no aliases, runtime config, server imports, or hooks are registered. The only exception is the DevTools tab when devtools.apiUrl is set.
Pool Options
| Option | Type | Default | Description |
|---|---|---|---|
maxConnections |
number |
30 |
Maximum number of clients in the pool |
minConnections |
number |
2 |
Minimum number of clients to maintain |
idleTimeoutMs |
number |
30000 |
Time (ms) a client can be idle before being closed |
connectionTimeoutMs |
number |
2000 |
Time (ms) to wait for a connection before timing out |
ssl |
boolean |
true |
Enable SSL connection |
Pool settings defined per-connection in dev.connections are merged with (and override) the global pool settings.
DevTools Integration
In dev mode, nuxt-pg adds a NuxtPG tab to Nuxt DevTools that lets you switch between named database connections on the fly.
Setup
Define multiple connections under dev.connections:
nuxtPg: {
connectionString: process.env.DATABASE_URL || '',
dev: {
connections: {
local: {
connectionString: process.env.DATABASE_URL_DEV || '',
pool: { ssl: false },
},
staging: {
connectionString: process.env.DATABASE_URL_STAGING || '',
},
production: {
connectionString: process.env.DATABASE_URL || '',
},
},
defaultConnection: 'local',
},
}
The DevTools panel appears automatically. Click any connection to switch — the module waits for active transactions to complete, swaps the connection pool, and resumes.
Remote DevTools (Monorepo)
In a monorepo where multiple Nuxt apps share a single API project, you can show the connection switcher in all apps without initializing a database in each one:
// main-app/nuxt.config.ts, dashboard/nuxt.config.ts, etc.
export default defineNuxtConfig({
modules: ['nuxt-pg'],
nuxtPg: {
enabled: false,
devtools: {
apiUrl: 'http://localhost:3000', // your API project's dev URL
},
},
})
With enabled: false and devtools.apiUrl set, the module only registers the DevTools tab pointing at the remote API — nothing else is loaded.
Connection Switch Hooks
When a database connection is switched (via DevTools or programmatically), nuxt-pg emits Nitro hooks you can listen to:
| Hook | Payload | When |
|---|---|---|
nuxtpg:connection:before-switch |
{ from, to } |
Before the pool shutdown begins |
nuxtpg:connection:switched |
{ from, to } |
After a successful switch |
nuxtpg:connection:switch-error |
{ from, to, error } |
On switch failure |
Listening to Hooks
// server/plugins/on-db-switch.ts
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('nuxtpg:connection:switched', ({ from, to }) => {
console.log(`Database switched from "${from}" to "${to}"`)
// Invalidate caches, re-seed data, notify services, etc.
})
nitroApp.hooks.hook('nuxtpg:connection:switch-error', ({ from, to, error }) => {
console.error(`Failed to switch from "${from}" to "${to}":`, error.message)
})
})
Hook Payload Types
import type {
NuxtPgConnectionSwitchPayload,
NuxtPgConnectionSwitchErrorPayload,
NuxtPgHooks,
} from 'nuxt-pg'
API Reference
getPg()
Returns the database service singleton. Auto-imported in all server code.
On first call, reads runtime config, initializes the connection pool, and registers a shutdown hook. Subsequent calls return the existing instance.
| Method | Description |
|---|---|
query<T>(sql, params?) |
Execute a SQL query. Uses the active transaction if one exists. |
createTransaction(timeoutMs?) |
Start a new transaction in the current request context. |
commitTransaction() |
Commit the active transaction. |
rollbackTransaction() |
Rollback the active transaction. |
transactional<T>(fn, timeoutMs?) |
Execute a function within a transaction with automatic commit/rollback. |
isHealthy() |
Check if the connection pool is healthy. |
shutdown() |
Gracefully shutdown the database service. |
How It Works
nuxt-pg automatically enables Nitro's asyncContext to track database transactions across async boundaries. When you start a transaction, it's stored in the request context and automatically used by all subsequent queries in that request:
// Without nuxt-pg (manual transaction passing)
await userService.create(data, transaction)
await profileService.create(profileData, transaction)
// With nuxt-pg (automatic context tracking)
await userService.create(data)
await profileService.create(profileData)
Requirements
- Nuxt 3.0+
- Node.js 18+
- PostgreSQL 12+
pgas a peer dependency
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
Credits
Built with:
Related Packages
Module for adding healthcheck urls to a nuxt project
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.2.3 | ^3.0 | MIT | |
| 0.2.2 | ^3.0 | MIT | |
| 0.2.0 | ^3.0 | MIT | |
| 0.1.1 | ^3.0 | MIT | |
| 0.0.19 | ^3.0 | MIT | |
| 0.0.18 | ^3.0 | MIT | |
| 0.0.17 | ^3.0 | MIT | |
| 0.0.16 | ^3.0 | MIT | |
| 0.0.15 | ^3.0 | MIT | |
| 0.0.14 | ^3.0 | MIT | |
| 0.0.13 | ^3.0 | MIT | |
| 0.0.12 | ^3.0 | MIT | |
| 0.0.11 | ^3.0 | MIT | |
| 0.0.10 | ^3.0 | MIT | |
| 0.0.9 | ^3.0 | MIT | |
| 0.0.8 | ^3.0 | MIT | |
| 0.0.7 | ^3.0 | MIT | |
| 0.0.6 | ^3.0 | MIT | |
| 0.0.5 | ^3.0 | MIT | |
| 0.0.4 | ^3.0 | MIT | |
| 0.0.3 | ^3.0 | MIT | |
| 0.0.2 | ^3.0 | MIT | |
| 0.0.1 | ^3.0 | MIT |