npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@prisma/dev

v0.25.0

Published

A local Prisma Postgres server for development and testing

Readme

@prisma/dev

A local Prisma Postgres server for development and testing.

It runs a real PostgreSQL database in your own Node process — no Docker, no separate service to install — and exposes it over both a plain TCP connection string and the prisma+postgres:// HTTP protocol that deployed Prisma Postgres uses.

Not for production. This is a development and testing tool.

There are two ways to use it:

  • @prisma/dev/vite — a Vite plugin that runs a database for the lifetime of your dev server.
  • @prisma/dev — start and stop a server from your own code.

Install

npm install --save-dev @prisma/dev

Vite plugin

Add the plugin and your app gets a database whenever vite dev runs.

import { prismaDev } from "@prisma/dev/vite";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [prismaDev({ name: "my-app" })],
});

That sets two environment variables before any of your code runs:

| Variable | Value | | --------------------- | ---------------------------------------------------------------------------------- | | DATABASE_URL | TCP connection string for the main database | | SHADOW_DATABASE_URL | TCP connection string for the shadow database, which prisma migrate dev requires |

Set name per project. It decides where the database files are persisted, so two projects left on the default name would share data.

Production builds are untouched. See When the plugin does nothing.

Applying migrations and seeding

A fresh database has no schema. You could migrate from application code, but then migration logic ships in your production artifact for the sake of local development. onDatabaseReady keeps it in the dev-only config instead.

import { execFile } from "node:child_process";
import { promisify } from "node:util";

import { prismaDev } from "@prisma/dev/vite";
import { defineConfig } from "vite";

const execFileAsync = promisify(execFile);

export default defineConfig({
  plugins: [
    prismaDev({
      name: "my-app",
      async onDatabaseReady({ env }) {
        await execFileAsync("npx", ["prisma", "migrate", "deploy"], { env: { ...process.env, ...env } });
      },
    }),
  ],
});

The hook runs before any application or framework code executes, so nothing can observe an unprepared database. vite dev waits for it, so a slow migration delays startup.

The plugin does this work in Vite's configureServer hook. That is the earliest hook which only runs for a dev server that is actually going to serve. Anything earlier also runs for config resolutions that serve nothing — a bare resolveConfig call, or the child compiler a framework builds from your config file — and starting a database for those is wrong. See Frameworks that build a child compiler.

One hook is registered, but it can do as much as you need. Sequence with plain await:

async onDatabaseReady(database) {
  await migrate(database);
  await seed(database);
}

It receives:

| Property | Description | | ------------------- | ---------------------------------------------------------------------- | | databaseUrl | TCP connection string for the main database | | shadowDatabaseUrl | TCP connection string for the shadow database | | prismaPostgresUrl | The prisma+postgres:// connection string. Carries an API key | | env | The variables this plugin manages, mapped to the local database | | name | The server name | | owned | false when the plugin attached to a database another process started |

Pass database.env when spawning a subprocess

Spread env explicitly, as the example above does.

It names the local database directly, so a migration cannot pick up a variable this plugin does not manage. That matters if you disabled one with env: { databaseUrl: false }: the ambient process.env still holds your own value, while database.env holds the local one.

The hook runs once per database

Not once per dev server. Two dev servers sharing one database in one process — see Frameworks that build a child compiler — run it once. A config restart that does not change the plugin's own options keeps the running database, so it does not re-run the hook either.

It does run when the plugin attached to a database that prisma dev was already running. Make it idempotent — prisma migrate deploy already is.

If the hook fails

The plugin shuts the database down and Vite fails to start. It will not serve your app against a half-prepared database.

Throwaway databases

By default the database is persisted under name and survives dev server restarts. Set persistenceMode: "stateless" to get a clean, in-memory database on every start instead.

prismaDev({
  name: "my-app",
  test: { name: "my-tests", persistenceMode: "stateless" },
});

The clearest use is integration tests, where every run should start from a known state. See Databases for Vitest runs.

For interactive vite dev it is worth understanding what you give up:

  • Editing vite.config.ts or an .env file restarts the dev server, which discards the data. Anything you seeded or entered by hand is gone.
  • onDatabaseReady gets an empty database on every start, so it has to do the full migrate and seed each time.
  • The database is invisible to prisma dev ls, stop, and rm, and cannot be shared with a running prisma dev.
  • Ports are chosen from whatever is free rather than the defaults below, unless you set them explicitly. That matters if you want to psql in at a predictable address.

Sharing a database with prisma dev

If a prisma dev server is already running under the same name, the plugin uses it instead of starting its own, and leaves it running when Vite exits. database.owned is false in that case.

This applies to the default stateful mode only. A stateless plugin always starts its own throwaway database, so asking for one never hands you a persistent database by surprise.

So this works, with both terminals on one database:

# terminal 1
npx prisma dev --name my-app

# terminal 2 — vite.config.ts uses prismaDev({ name: "my-app" })
npx vite dev

Frameworks that build a child compiler

Some frameworks compile part of your app with a second, internal Vite server built from the same config file. React Router does this, and so the plugin has to cope with being instantiated twice in one process.

The plugin does. Two acquisitions of one name converge on one database: the second joins the first rather than restarting it, the database stays up until the last of them lets go, and onDatabaseReady runs once. Nothing needs configuring for this.

Two consequences are worth knowing:

  • A child compiler runs without a database. React Router builds its child compiler before any dev server is configured, and drops the hook the plugin uses. Nothing in a child compiler queries a database, but code that runs there cannot expect DATABASE_URL to be set.
  • Commands that only resolve the config start nothing. react-router typegen and react-router build load vite.config.ts without serving anything, so no database starts and no migration runs. A typecheck is a typecheck.

Reading a database's address from another process

process.env reaches the Vite process and its children. It does not reach a psql session, a migration you run by hand, or anything else started from a second terminal.

Look the server up by name instead:

import { getPrismaDevServerConnection } from "@prisma/dev";

const connection = await getPrismaDevServerConnection({ name: "my-app" });

console.log(connection?.databaseUrl); // postgres://...

It resolves to null when no server of that name is running. Only stateful servers are visible — a stateless one writes no state and can only be addressed through the process that started it.

This is why hardcoding a connection string into an .env file is not necessary, and why pinning ports is not either. See Ports and stable addresses.

Connection strings never reach the browser

The plugin writes to process.env and nothing else. It deliberately does not use Vite's define or import.meta.env.

Those inline values into browser assets. Both the TCP connection string and the prisma+postgres:// URL carry credentials, so inlining either would hand database access to every visitor of your app.

Server-side code, SSR loaders, and Prisma Client all read process.env, which is the surface that actually needs these values.

The database's lifetime

The database runs inside the Vite process, so it cannot outlive it. There is no background daemon and nothing to clean up by hand.

Quitting Vite normally, or with Ctrl-C, shuts the database down cleanly. A kill -9 takes the database down with the process; the next start recovers on its own, though it may pause briefly to do so.

Editing vite.config.ts or an .env file restarts the Vite dev server. The database is only restarted along with it if the edit changed this plugin's own options, in which case the new options have to win. Otherwise the running database is carried over untouched. Either way your data is persisted, unless you opted into a throwaway database. Editing application code does not touch the database.

When the plugin does nothing

No database is started when:

  • the command is vite build
  • the command is vite preview
  • the config was loaded by Vitest, which reads the same vite.config.ts
  • the config was only resolved, and no dev server was configured — see Frameworks that build a child compiler

See Databases for Vitest runs to allow one under Vitest.

Databases for Vitest runs

Vitest reads the same vite.config.ts, so by default the plugin starts nothing there. A plain vitest run should not boot a database as a config side effect.

Opt in with the test option:

prismaDev({
  name: "my-app",
  test: { name: "my-app-test", persistenceMode: "stateless" },
});

Each entry under test replaces the corresponding option above it. Everything you do not name is inherited. Here the test run gets its own throwaway database and inherits the env mapping, while vite dev keeps its persistent my-app one.

The port options are inherited too, but this database is stateless, so it takes whatever ports are free rather than the ones it inherited — see Throwaway databases. Set the ports under test if a run needs a predictable address. Note that pinning them collides with a vite dev on the same ports if both run at once.

test: {} opts in with the surrounding options unchanged, so vite dev and vitest run share one database.

Setting test has no effect outside Vitest, so there is one config file and no branching in it.

Keeping your own value for a variable

The plugin owns every variable it writes. If your environment already points one somewhere else, the local database wins and the plugin logs a warning naming what it displaced.

This is deliberate. Many frameworks load .env into process.env before the plugin runs, so deferring to the existing value would leave a project whose .env already names DATABASE_URL with a running database that nothing connects to.

To keep your own value, disable that variable:

prismaDev({
  env: { databaseUrl: false },
  name: "my-app",
});

The plugin then never writes DATABASE_URL, and reports no conflict. database.env in onDatabaseReady still carries the local connection string.

A variable that already holds exactly the value the plugin was going to write is not warned about. There is no conflict to report.

Using the prisma+postgres:// URL

Off by default, because it embeds an API key. Opt in by naming a variable:

prismaDev({
  env: { prismaPostgresUrl: "PRISMA_DATABASE_URL" },
  name: "my-app",
});

Options

| Option | Default | Description | | ----------------------- | ----------------------- | -------------------------------------------------------------- | | name | "default" | Server name, which decides where data is persisted | | onDatabaseReady | — | Async hook to migrate and seed before Vite starts | | persistenceMode | "stateful" | "stateless" for a clean in-memory database on every start | | env.databaseUrl | "DATABASE_URL" | Variable for the main TCP connection string. false to skip | | env.shadowDatabaseUrl | "SHADOW_DATABASE_URL" | Variable for the shadow TCP connection string. false to skip | | env.prismaPostgresUrl | false | Variable for the prisma+postgres:// URL. Name it to opt in | | test | false | Options for Vitest runs. An object opts in, false stays off | | port | 51213 | HTTP server port | | databasePort | 51214 | Main database port | | shadowDatabasePort | 51215 | Shadow database port | | streamsPort | 51216 | Colocated Prisma Streams server port | | debug | false | Log the server's debug output |

Ports and stable addresses

A named stateful server records the ports it settled on and reuses them the next time it starts. So name alone is enough for a stable address.

To find out which ports a server actually landed on, look it up by name. That is also the recommended way to reach the database from outside the Vite process.

Passing a port explicitly changes the failure mode, deliberately:

  • A default port that is taken moves. The plugin falls back to another free port rather than refusing to start.
  • A port you named is bound or the start fails. It is never silently moved. A caller who names a port has something outside this process depending on it, and pointing that something at a port nothing is listening on is worse than a clear error.

A stateless database records nothing, so it takes any free port on every start unless you name one.

Programmatic server

For test harnesses, scripts, and integrations outside Vite:

import { startPrismaDevServer } from "@prisma/dev";

const server = await startPrismaDevServer({ name: "my-tests" });

console.log(server.database.prismaORMConnectionString); // postgres://...
console.log(server.ppg.url); // prisma+postgres://...

await server.close();

The resolved server exposes:

| Property | Description | | ---------------- | ------------------------------------------------------------------------------- | | database | connectionString, prismaORMConnectionString, and a psql terminalCommand | | shadowDatabase | The same shape, for prisma migrate | | ppg.url | The prisma+postgres:// connection string, API key included | | http.url | Base URL of the HTTP server | | name | The server name | | close() | Shuts everything down |

Persistence

startPrismaDevServer() defaults to persistenceMode: "stateless", which discards all data when the server closes. That suits tests.

Pass persistenceMode: "stateful" to persist data under the given name, matching prisma dev and the Vite plugin's default.

const server = await startPrismaDevServer({ name: "my-app", persistenceMode: "stateful" });

Ports

Defaults are 51213 for HTTP, 51214 for the database, 51215 for the shadow database, and 51216 for the Prisma Streams server. Override any of them with port, databasePort, shadowDatabasePort, and streamsPort.

A default that is taken falls back to another free port. A port you pass explicitly is bound or the call rejects — with PortNotAvailableError when nothing can bind it, and PortBelongsToAnotherServerError when another Prisma Dev server already holds it.

Starting a second server under a name that is already running rejects with ServerAlreadyRunningError.

Looking up a running server

import { getPrismaDevServerConnection } from "@prisma/dev";

const connection = await getPrismaDevServerConnection({ name: "my-app" });

Resolves the databaseUrl, shadowDatabaseUrl, and prismaPostgresUrl of a running stateful server by name, or null when none is running. Use it from a process that did not start the database and therefore cannot read its process.env.

Experimental

server.experimental exposes change-data-capture events, query insights, and the colocated Prisma Streams endpoint. These are experimental and may change without notice.

Requirements

The Vite plugin supports Vite 5, 6, 7, and 8. vite is an optional peer dependency, so installing @prisma/dev without Vite is fine — the plugin module references Vite for types only.

License

ISC