Skip to content

Getting started ​

Install ​

sh
npm i --save-dev pg-dolly
sh
pnpm add --save-dev pg-dolly
sh
yarn add --dev pg-dolly

TIP

pg-dolly is ESM only, for Node.js 24 or later. Its one dependency is pg, for its own admin connection: tests connect with whatever client the project uses.

A server ​

pg-dolly needs a Postgres server, in DATABASE_URL, as a user that can create databases:

sh
export DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres

A local server, a devcontainer sidecar or a CI service all work. Without one, it fails rather than skipping the tests: leaving out integration tests should be a choice, not something that happens quietly.

Test databases don't need to survive a restart, so a server only for tests can run with fsync=off, synchronous_commit=off and full_page_writes=off, and its data on a tmpfs.

A schema ​

The schema is SQL: ideally a dump of what your migrations build, committed as schema.sql. pg-dolly-schema keeps that file for any migration tool, and fails CI when it's stale. See The schema for other ways.

Connect once, clone per test ​

Once per test run, connect and get the template. The first run builds it, and later runs (and other processes) reuse it until the SQL changes:

ts
import { 
readFile
} from 'node:fs/promises';
import {
PgDolly
} from 'pg-dolly';
const
dolly
= await
PgDolly
.
connect
();
const
template
= await
dolly
.
template
(await
readFile
('schema.sql', 'utf8'));

For each test, clone it, and connect to the clone's URL with any client:

ts
const 
database
= await
template
.
clone
();
const
client
= new
pg
.
Client
({
connectionString
:
database
.
url
});
await
client
.
connect
();
await
client
.
query
("INSERT INTO item (id, name) VALUES (1, 'dolly')");
await
client
.
end
();

When a test fails, keep its database. Its name is printed, so you can connect to it and look:

ts
database
.
keep
();

At the end of the run, close. Every clone not kept is dropped:

ts
await 
dolly
.
close
();

Close every connection to the clones first: dropping a database ends any left open.

Next steps ​