Skip to content

data-table-postgres ​

The data-table-postgres package provides a PostgreSQL adapter for the data-table query toolkit. It uses the pg driver.

Installation ​

bash
npm install remix pg
npm install -D @types/pg

Or install the standalone package:

bash
npm install @remix-run/data-table-postgres pg
npm install -D @types/pg

Import ​

ts
import { createPostgresDatabaseAdapter } from 'remix/data-table-postgres'

Or from the standalone package:

ts
import { createPostgresDatabaseAdapter } from '@remix-run/data-table-postgres'

API ​

createPostgresDatabaseAdapter(client, options?) ​

Creates a DatabaseAdapter for use with createDatabase().

ts
import { Pool } from 'pg'
import { createPostgresDatabaseAdapter } from 'remix/data-table-postgres'
import { createDatabase } from 'remix/data-table'

let pool = new Pool({ connectionString: process.env.DATABASE_URL })
let adapter = createPostgresDatabaseAdapter(pool)
let db = createDatabase(adapter)

Parameters ​

ParameterTypeDescription
clientPool | PoolClient (from pg)A pg connection pool or an individual pool client.
optionsPostgresDatabaseAdapterOptionsOptional adapter configuration.

PostgresDatabaseAdapterOptions ​

OptionTypeDescription
capabilitiesAdapterCapabilityOverridesOverride detected adapter capabilities.

Capabilities ​

The PostgreSQL adapter reports the following capabilities by default:

CapabilityDefaultDescription
returningtruePostgreSQL supports RETURNING clauses.
savepointstruePostgreSQL supports savepoints for nested transactions.
upserttruePostgreSQL supports ON CONFLICT upsert.
transactionalDdltruePostgreSQL supports DDL inside transactions.
migrationLocktruePostgreSQL supports advisory locks for migration safety.

Connection Pooling ​

Always use a Pool in production. The pool manages connections automatically and handles cleanup.

ts
import { Pool } from 'pg'

let pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  max: 20,              // Maximum pool size
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
})

You can also pass an individual PoolClient for use within a manually managed connection:

ts
let client = await pool.connect()
try {
  let adapter = createPostgresDatabaseAdapter(client)
  let db = createDatabase(adapter)
  // ... use db
} finally {
  client.release()
}

PostgreSQL-Specific Features ​

  • Full RETURNING support: All write operations (create, update, delete) can return the affected rows.
  • ILIKE: Native case-insensitive pattern matching is fully supported.
  • Advisory locks: The migration runner uses PostgreSQL advisory locks to prevent concurrent migrations in multi-process deployments.
  • UUID columns: Native UUID type is supported. Use c.uuid() for UUID primary keys.
  • JSON/JSONB: The c.json() column type maps to PostgreSQL's JSON types.
  • Enum types: The c.enum() column builder maps to PostgreSQL CHECK constraints by default.

Example ​

ts
import { Pool } from 'pg'
import { createPostgresDatabaseAdapter } from 'remix/data-table-postgres'
import { createDatabase, table, column as c } from 'remix/data-table'

let pool = new Pool({ connectionString: process.env.DATABASE_URL })
let adapter = createPostgresDatabaseAdapter(pool)
let db = createDatabase(adapter)

let users = table({
  name: 'users',
  columns: {
    id: c.uuid().primaryKey().defaultSql('gen_random_uuid()'),
    email: c.varchar(255).notNull().unique('users_email_uq'),
    name: c.text().notNull(),
    createdAt: c.timestamp({ withTimezone: true }).notNull().defaultNow(),
    updatedAt: c.timestamp({ withTimezone: true }).notNull().defaultNow(),
  },
  timestamps: true,
})

let user = await db.create(users, {
  email: 'alice@example.com',
  name: 'Alice',
}, { returnRow: true })

Released under the MIT License.