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-orm-extension-typed-json

v8.0.0-rc.5

Published

Typed JSON and typed text columns for Prisma 8 with no validator dependency. The prisma-json-types-generator idea, as a Prisma 8 extension.

Readme

prisma-orm-extension-typed-json

CI npm

Typed JSON and typed text columns for Prisma 8, with no validator and no runtime cost: a Prisma 8 extension implementing the typed/json@1 and typed/text@1 codecs.

An extension is a package that adds a database capability Prisma 8 does not have out of the box, while keeping the Prisma 8 experience: typed schema declarations, generated TypeScript, migration support, and query helpers that feel native to your app. This one adds what prisma-json-types-generator gave Prisma 4 to 7: a jsonb column whose TypeScript type is whatever you say it is, and a text column narrowed to a literal union. The type is compile-time only. Values pass through unchanged, rows written before the type existed keep reading, and nothing is installed or validated.

// Your existing PrismaJson namespace file works as-is.
declare global {
  namespace PrismaJson {
    interface Settings { theme: 'light' | 'dark'; tags: string[] }
  }
}

Account: model('Account', {
  fields: {
    id: field.id.uuidv7String(),
    settings: field.column(typedJson<PrismaJson.Settings>('PrismaJson.Settings')),
    status: field.column(typedText<'draft' | 'published'>("'draft' | 'published'")),
  },
}),

The string is the TypeScript type expression, printed verbatim into the emitted contract.d.ts. The type parameter is the same type for the no-emit path. Keep the two in agreement; the package keeps the string safe (single line, balanced brackets, no stray ;) and fails at the call site otherwise.

The type applies on both sides of the client: create and update inputs for the column are checked against it, and rows come back narrowed to it. What never happens is a runtime check, so data that bypasses TypeScript (an old row, an as never, a raw SQL write) is stored and returned as-is.

If you want the column validated at runtime as well, that is a different codec: prisma-orm-extension-zod-json validates on write and read from a zod schema.

1. Install the package

npm install prisma-orm-extension-typed-json @prisma/[email protected] @prisma/[email protected]
npm install -D prisma@next

No validator library is pulled in. @prisma/orm-postgres is the Prisma 8 facade your app talks to, @prisma/cli-engine provides definePrismaConfig for prisma.config.ts (it has to be a direct dependency for that import to resolve), and prisma is the unified CLI. This package's version always matches the Prisma release it targets (see Versioning below), so keep it aligned with @prisma/orm-postgres.

2. Register it in the config

Prisma 8 uses this registration when it emits your contract and plans migrations:

// prisma.config.ts
import { definePrismaConfig } from '@prisma/cli-engine';
import { defineConfig as ormConfig } from '@prisma/orm-postgres/config';
import { typedExtensionDescriptor } from 'prisma-orm-extension-typed-json/control';

export default definePrismaConfig({
  orm: ormConfig({
    contract: './src/prisma/contract.ts',
    extensions: [typedExtensionDescriptor],
    db: { connection: process.env.DATABASE_URL! },
  }),
});

3. Register it on the client

Prisma 8 uses this registration when your app runs queries. Pass your emitted Contract type explicitly so the client is fully typed, not unknown:

// src/prisma/db.ts
import postgres from '@prisma/orm-postgres/runtime';
import { typedRuntimeDescriptor } from 'prisma-orm-extension-typed-json/runtime';
import type { Contract } from './contract.d';
import contractJson from './contract.json' with { type: 'json' };

export const db = postgres<Contract>({ contractJson, url, extensions: [typedRuntimeDescriptor] });

4. Use it in your contract

With the TypeScript builder, add the pack next to models and declare columns with typedJson and typedText:

import { defineContract } from '@prisma/orm-postgres/contract-builder';
import { typedJson, typedText } from 'prisma-orm-extension-typed-json/column-types';
import typedPack from 'prisma-orm-extension-typed-json/pack';

export const contract = defineContract({}, ({ field, model }) => ({
  extensions: { typedJson: typedPack },
  models: {
    Account: model('Account', {
      fields: {
        id: field.id.uuidv7String(),
        settings: field.column(typedJson<PrismaJson.Settings>('PrismaJson.Settings')),
        status: field.column(typedText<'draft' | 'published'>("'draft' | 'published'")),
      },
    }),
  },
}));

With PSL, the same columns need no TypeScript at all:

types {
  Settings = typed.Json("PrismaJson.Settings")
  Status   = typed.Text("'draft' | 'published'")
}

model Account {
  id       String   @id @default(uuid())
  settings Settings
  status   Status
}

Both doors emit the same contract.d.ts:

readonly settings: PrismaJson.Settings;
readonly status: 'draft' | 'published';

5. Apply and query

npx prisma contract emit
npx prisma db init

contract emit stores the type expression in contract.json and prints it into contract.d.ts. db init (or db update on an existing database) creates a plain jsonb or text column. From there the client returns the narrowed types and stores whatever you write, and a missing registration surfaces as a clear error naming what is absent, not silent misbehaviour.

For all of this inside a runnable app, see prisma-orm-extension-typed-json-example: a small preferences service with a typed settings column, a literal-union status, a discriminated-union payload, an HTTP boundary that validates, and the psql experiment that shows where the type stops.

Migrating from prisma-json-types-generator

Prisma 8 has no generated client to patch and no generator hook, so the generator itself cannot run there. The idea survives unchanged:

| prisma-json-types-generator | this package | | --- | --- | | /// [Settings] on a Json field | typedJson<PrismaJson.Settings>('PrismaJson.Settings'), or typed.Json("PrismaJson.Settings") in PSL | | /// !['draft' \| 'published'] on a String field | typedText<'draft' \| 'published'>("'draft' \| 'published'"), or typed.Text(...) in PSL | | declare global { namespace PrismaJson { ... } } | unchanged, keep the file | | types only, no runtime validation | the same |

Two differences to know about. The type reference is a string now, because it has to travel through contract.json; the PrismaJson. prefix is a convention you keep, not something the package requires. And the storage type is explicit: typedJson is jsonb. A column that was native json (not jsonb) shows up as a type change in your first migration plan.

What this is not

It does not validate. A value the type forbids is stored and read back exactly as written, which is the point: it matches how prisma-json-types-generator behaved, and it means switching a column to a typed one never changes what existing rows return. When you want the database boundary enforced, reach for a validator codec instead.

Versioning and status

Versions mirror the Prisma release this package targets, the same convention Prisma's own extensions use: installing [email protected] gets you the build for Prisma 8.0.0-rc.5. A fix released between Prisma versions appends a counter (8.0.0-rc.5.1), which semver orders after its base and before the next Prisma release.

Early; tracks the Prisma v8 release-candidate line, which is still moving.