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.