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-js-defaults

v2.1.0

Published

Zero-dependency Prisma extension to generate default values using custom JavaScript functions.

Readme

prisma-js-defaults

A zero-dependency, context-aware Prisma extension that allows you to use custom JavaScript/TypeScript functions to generate default values for your database fields dynamically at runtime.

What's new in v2.1.0? (Stability & Deep Cloning Update)

Version 2.1.0 brings massive improvements to runtime safety, memory management, and complete support for complex Prisma data types and nested update trees.

Smarter & Hardened deepClone

  • Prisma Ecosystem Types: Fixed bugs where specialized Prisma fields would lose their instances during query arguments cloning. Now fully supports Prisma.Decimal, Prisma.DbNull, Prisma.JsonNull, and Prisma.AnyNull without breaking their internal structures.
  • Native Type Support: Added seamless deep cloning for JavaScript Date and Node.js Buffer objects.
  • Circular Reference Protection: Upgraded the cloning engine with a WeakMap registry. If your query payload contains circular references, the extension safely handles them instead of throwing a RangeError: Maximum call stack size exceeded crash.

Traversal for Top-Level update Operations

  • Nested Writes inside Updates: The extension now intercepts top-level update queries. While it intentionally skips applying defaults to fields being directly updated (preserving your data integrity), it now recursively traverses nested relation trees inside the update payload.
  • This means nested create, createMany, connectOrCreate, or upsert queries triggered inside a parent update will now correctly receive their dynamic JS defaults.

Why this exists?

Prisma currently doesn't allow using custom JavaScript/TypeScript functions directly inside the @default() attribute in your schema.prisma file. This package solves that by introducing a custom /// @defaultJs comment decorator and a lightweight runtime extension.

Installation

npm install prisma-js-defaults

(Note: Requires @prisma/client v4.0.0 or higher. The examples below use Prisma v7 syntax and Driver Adapters).

Usage

1. Update your schema.prisma

Add the /// @defaultJs(yourFunctionName) comment directly above the field you want to generate dynamically.

TypeScript Tip: Add a native Prisma default like @default("") to satisfy the TypeScript compiler. Your database won't use it because prisma-js-defaults will inject the real value at runtime before the query executes.

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
}

model Article {
  id Int @id @default(autoincrement())
  title String
  
  // Use @default("") so TypeScript doesn't complain during creation
  /// @defaultJs(generateSlug)
  slug String @default("")
}

2. Generate the configuration

Run the CLI tool to parse your schema and generate the internal configuration file (js-defaults.json):

npx prisma-js-defaults

(This command will automatically run npx prisma generate for you afterwards.)

3. Setup the Prisma Extension

In your backend code, wrap your Prisma Client with the withJsDefaults extension. You need to pass the generated config as the first argument, and your functions as the second. Here is an example:

import { Pool } from 'pg';
import { PrismaPg } from '@prisma/adapter-pg';
import { PrismaClient } from '@prisma/client';
import { withJsDefaults } from 'prisma-js-defaults';

// 1. Import the generated configuration
import { jsDefaultsConfig } from '../prisma/generated/js-defaults';

// 2. Initialize your Driver Adapter
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const adapter = new PrismaPg(pool);

// 3. Apply the extension to your Prisma Client
const prisma = new PrismaClient({ adapter }).$extends(
    withJsDefaults(jsDefaultsConfig, {
        // The generator receives the current data object.
        // You can use it to create contextual defaults!
        generateSlug: (data) => {
            if (data?.title) {
                return data.title.toLowerCase().replace(/\s+/g, '-');
            }
            return "fallback-slug-" + Date.now();
        }
    })
);

async function main() {
    // The slug will be generated automatically based on the title!
    const article = await prisma.article.create({
        data: {
            title: "Hello Awesome World"
        }
    });

    console.log(article.slug);
    // Output: hello-awesome-world
}

main()
    .catch(console.error)
    .finally(() => prisma.$disconnect());

Supported Operations

prisma-js-defaults deeply traverses your queries and applies defaults to:

  • create
  • createMany
  • createManyAndReturn
  • upsert (applies to the create branch only)
  • Nested relational writes (create, createMany, connectOrCreate, upsert within includes)
  • Nested relation trees inside update operations (applies only to nested creations like create, createMany, connectOrCreate, or upsert.create).

Note: Top-level fields inside update actions are never modified, ensuring existing data isn't accidentally overwritten by default values.

Interactive Transactions: Fully supported out of the box. The extension seamlessly handles queries executed inside $transaction(async (tx) => { ... }) blocks without any additional configuration.

Advanced: Async Functions

Your generator functions can be completely asynchronous! This is incredibly powerful if your default value depends on an external API, database lookup, or heavy cryptographic hashing.

const prisma = new PrismaClient().$extends(
    withJsDefaults(jsDefaultsConfig, {
        generateSlug: async (data) => {
            // Fetch some external data or hash before inserting!
            const uniqueHash = await fetchSomeExternalApi();
            return `${data.title}-${uniqueHash}`;
        }
    })
);

Important Architecture Notes

1. Extension Order (Middleware Pipeline)

Prisma executes extensions in the order they are chained. If you are using multiple extensions that modify args.data on queries, the order matters:

  • Place withJsDefaults last if you want it to run after other extensions have formatted your data.
  • Place withJsDefaults first if you want subsequent extensions to validate or log the defaults generated by your functions.

2. @defaultJs() vs native @default()

If a field in your schema has both a Prisma native default and a JS default (e.g., id String @default(uuid()) /// @defaultJs(myGenerator)), the @defaultJs will always win. This is because prisma-js-defaults injects the value at the runtime query level, skipping the database-level default generation entirely.

License

MIT