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-pg-toolkit

v1.3.0

Published

Joins (INNER/LEFT/RIGHT/FULL OUTER/CROSS) and locking (pessimistic/optimistic) helpers for Prisma

Readme

prisma-pg-toolkit

Joins (INNER / LEFT / RIGHT / FULL OUTER / CROSS), UNION/UNION ALL, and locking (pessimistic / optimistic) helpers for Prisma + PostgreSQL.

Prisma's client only performs LEFT JOIN-style relation loading internally, has no UNION support, and no built-in row locking. This library fills those gaps with safe, config-driven APIs — no raw SQL required from consumers.

Postgres only. Join syntax (FULL OUTER JOIN, RIGHT JOIN) and locking (FOR UPDATE) differ across databases — this library targets Postgres specifically.

Install

npm install prisma-pg-toolkit

Requires @prisma/client v5+ as a peer dependency.

Setup

import { PrismaClient } from '@prisma/client';
import { withToolKit } from 'prisma-pg-toolkit';

const base = new PrismaClient();
const prisma = withToolKit(base);

Joins

const result = await prisma.$join({
  from: 'User',
  select: ['User.email', 'Post.title'],
  join: {
    type: 'LEFT', // 'INNER' | 'LEFT' | 'RIGHT' | 'FULL OUTER' | 'CROSS'
    table: 'Post',
    on: { fromColumn: 'id', toColumn: 'userId' },
  },
  where: { column: 'published', op: '=', value: true }, // optional
});

CROSS joins don't need an on clause.

Union / Union All

Combines the results of two or more queries with the same column shape. all: false (default) dedupes matching rows; all: true keeps every row, including duplicates, and is faster since no dedupe pass is needed.

const result = await prisma.$union({
  all: false, // true = UNION ALL, false = UNION (dedupes)
  queries: [
    { from: 'User', select: ['email as label'] },
    { from: 'Post', select: ['title as label'], where: { column: 'published', op: '=', value: true } },
  ],
});

Each sub-query's select list must resolve to the same number of columns as the others, with compatible types — this is a Postgres requirement for UNION, not specific to this library. At least 2 queries are required.

Pessimistic locking

Locks a row (SELECT ... FOR UPDATE) inside a transaction. Other callers requesting the same row wait until the transaction commits or rolls back.

await prisma.$lock.pessimistic(
  { table: 'User', id: 1, mode: 'FOR UPDATE' }, // or 'FOR SHARE'
  async (tx, row) => {
    return tx.user.update({ where: { id: row.id }, data: { name: 'Updated' } });
  }
);

Optimistic locking

Requires a version: Int column on the model. Update succeeds only if expectedVersion still matches the current row; otherwise throws OptimisticLockError, and the caller should re-fetch and retry.

import { OptimisticLockError } from 'prisma-pg-toolkit';

try {
  await prisma.$lock.optimistic({
    model: 'user', // matches your Prisma model accessor, lowercase
    id: 1,
    expectedVersion: 3,
    data: { name: 'New Name' },
  });
} catch (err) {
  if (err instanceof OptimisticLockError) {
    // row changed since you read it — re-fetch and retry
  }
}

Security

Table and column names are validated against a strict identifier pattern before being spliced into SQL — only letters, digits, and underscores, matching Postgres' identifier rules. Values are always passed as parameterized bindings via Prisma's tagged-template $queryRaw, never string-concatenated.

Testing

Automated tests (Vitest) covering joins, unions, pessimistic locking, optimistic locking, and identifier security live in src/vitest. Manual runnable example scripts are in src/testing.

License

MIT EOF