🚀. Socket Launch Week Day 2:Introducing Manifest Alerts.Learn more
Sign In

@prisma-next/sql-contract-ts

Package Overview
Dependencies
Maintainers
3
Versions
914
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@prisma-next/sql-contract-ts

SQL-specific TypeScript contract authoring surface for Prisma Next

Source
npmnpm
Version
0.13.0-dev.23
Version published
Weekly downloads
14K
-2.67%
Maintainers
3
Weekly downloads
 
Created
Source

@prisma-next/sql-contract-ts

Status: Current SQL TypeScript contract authoring surface

This package owns the SQL TypeScript authoring API for Prisma Next.

Package Classification

  • Domain: sql
  • Layer: authoring
  • Plane: migration

Overview

This package is part of the SQL family namespace (packages/2-sql/2-authoring/contract-ts) and provides:

  • the SQL contract DSL centered on defineContract(...)
  • the base structural helpers exported from ./contract-builder: field.column(...), field.generated(...), field.namedType(...), plus model(...) and rel.*
  • an optional callback overload that exposes pack-composed helpers — namespaced helpers like field.id.uuidv7String(), field.text(), field.temporal.createdAt(), field.temporal.updatedAt(), plus pack-contributed entity-type helpers at top level alongside the built-in model / rel (e.g. enum({ name, values }))
  • lowering from authored model definitions into the canonical SQL Contract

Responsibilities

  • SQL contract authoring: Build SQL contracts programmatically with type safety
  • Pack-composed helper vocabulary: Merge family, target, and extension authoring contributions into the callback helper namespaces
  • Lowering pipeline: Turn authored model definitions into the canonical SQL contract artifacts consumed by the rest of the stack
  • Config helper: Provide typescriptContract(...) for prisma-next.config.ts
  • Schema validation: Contract JSON validation flows through the per-target descriptor SPI (descriptor.contractSerializer.deserializeContract(json))

Package Status

This is the current SQL TypeScript authoring implementation. Shared descriptor types live in @prisma-next/contract-authoring. Contract validation flows through the per-target descriptor's contractSerializer SPI (e.g. postgresTarget.contractSerializer.deserializeContract), or via the canonical façade (postgres<Contract>(...)).

Architecture

  • Base DSL: ./contract-builder exports the stable structural DSL (defineContract, field, model, rel)
  • Composed helper namespaces: defineContract(config, (helpers) => ...) synthesizes helpers.field.* and helpers.type.* from the selected family, target, and extension packs
  • SQL resolution and contract generation: internal resolution normalizes names, relations, indexes, and FK materialization before producing the canonical SQL contract artifacts
  • Shared descriptor layer: @prisma-next/contract-authoring provides the target-neutral descriptor types used by the DSL and by authoring-adjacent packs

Contributor-facing lowering notes and detailed warning semantics live in DEVELOPING.md.

flowchart LR
  builderInput[TypeScript contract input] --> sqlContractTs[@prisma-next/sql-contract-ts]
  sqlContractTs --> authoringCore[@prisma-next/contract-authoring]
  sqlContractTs --> sqlTypes[@prisma-next/sql-contract/types]
  sqlContractTs --> contract[SQL Contract]

Exports

  • ./contract-builder - SQL contract DSL (defineContract, field, model, rel)
  • ./config-types - typescriptContract(...) config helper

Usage

Direct Structural DSL

Direct imports expose the base structural helpers. Use this surface when you want to author with explicit column descriptors, explicit generators, or named storage types.

Built-in ID helpers from @prisma-next/ids already return the generated-field spec accepted by field.generated(...), so field.generated(uuidv4()) is a valid structural DSL call.

import { textColumn, timestamptzColumn } from '@prisma-next/adapter-postgres/column-types';
import sqlFamily from '@prisma-next/family-sql/pack';
import { uuidv4 } from '@prisma-next/ids';
import { defineContract, field, model, rel } from '@prisma-next/sql-contract-ts/contract-builder';
import postgresPack from '@prisma-next/target-postgres/pack';

const User = model('User', {
  fields: {
    id: field.generated(uuidv4()).id(),
    email: field.column(textColumn).unique(),
    createdAt: field.column(timestamptzColumn).defaultSql('now()'),
  },
})
  .relations({
    posts: rel.hasMany('Post', { by: 'userId' }),
  })
  .sql({
    table: 'app_user',
  });

const Post = model('Post', {
  fields: {
    id: field.generated(uuidv4()).id(),
    userId: field.column(textColumn),
    title: field.column(textColumn),
  },
})
  .relations({
    user: rel.belongsTo(User, { from: 'userId', to: 'id' }),
  })
  .sql({
    table: 'blog_post',
  });

export const contract = defineContract({
  family: sqlFamily,
  target: postgresPack,
  naming: { tables: 'snake_case', columns: 'snake_case' },
  models: {
    User,
    Post,
  },
});

Callback Helper Vocabulary

Pack-provided helper presets are available through the callback overload. This is the surface that exposes field.id.*, field.text(), field.temporal.createdAt(), field.temporal.updatedAt(), type.sql.String(...), and extension helpers such as type.pgvector.Vector(...).

import pgvector from '@prisma-next/extension-pgvector/pack';
import sqlFamily from '@prisma-next/family-sql/pack';
import { defineContract } from '@prisma-next/sql-contract-ts/contract-builder';
import postgresPack from '@prisma-next/target-postgres/pack';

export const contract = defineContract(
  {
    family: sqlFamily,
    target: postgresPack,
    extensionPacks: { pgvector },
  },
  ({ type, enum: enumEntity, field, model, rel }) => {
    const types = {
      ShortName: type.sql.String(35),
      Role: enumEntity({ name: 'role', values: ['USER', 'ADMIN'] as const }),
      Embedding1536: type.pgvector.Vector(1536),
    } as const;

    const User = model('User', {
      fields: {
        id: field.id.uuidv7String().sql({ id: { name: 'user_pkey' } }),
        shortName: field.namedType(types.ShortName),
        role: field.namedType(types.Role),
        embedding: field.namedType(types.Embedding1536).optional(),
        createdAt: field.temporal.createdAt(),
        updatedAt: field.temporal.updatedAt(),
      },
    });

    const Post = model('Post', {
      fields: {
        id: field.id.uuidv7String(),
        authorId: field.uuidString(),
        title: field.text(),
      },
    });

    return {
      types,
      models: {
        User: User.relations({
          posts: rel.hasMany(Post, { by: 'authorId' }),
        }).sql({
          table: 'user',
        }),
        Post: Post.relations({
          author: rel.belongsTo(User, { from: 'authorId', to: 'id' }),
        }).sql({
          table: 'post',
        }),
      },
    };
  },
);

Constraint Placement

Single-field constraints are usually most readable inline on the field, while compound constraints live in .attributes(...) or model-level .sql(...).

const Membership = model('Membership', {
  fields: {
    orgId: field.column(textColumn).column('org_id'),
    userId: field.column(textColumn).column('user_id'),
    role: field.column(textColumn),
  },
})
  .attributes(({ fields, constraints }) => ({
    id: constraints.id([fields.orgId, fields.userId], { name: 'membership_pkey' }),
    uniques: [
      constraints.unique([fields.orgId, fields.role], {
        name: 'membership_org_role_key',
      }),
    ],
  }))
  .sql({ table: 'membership' });

Helper Notes

  • Structural helpers: field.column(...), field.generated(...), field.namedType(...), plus model(...) and rel.*
  • Callback helper presets: field.id.uuidv4String(), field.id.uuidv7String(), field.id.nanoid({ size }), field.uuidString(), field.text(), field.timestamp(), field.temporal.createdAt(), field.temporal.updatedAt(), and type.* (Postgres also adds field.uuidNative(), field.id.uuidv4Native(), field.id.uuidv7Native() — these emit pg/uuid@1)
  • Timestamp helpers mirror PSL semantics: field.temporal.createdAt() lowers to a target storage now() default, while field.temporal.updatedAt() lowers to the target-owned timestampNow execution default for create and non-empty update mutations.
  • Keep field-local and FK-local storage overrides next to the authoring site with field.sql(...) and rel.belongsTo(...).sql({ fk })
  • Prefer typed local refs such as field.namedType(types.Role), User.refs.id, and User.ref('id') when those tokens are available
  • See API.md for generated-field spec semantics, validation rules, and typed-reference warning behavior

Foreign Key Defaults

Use the root-level foreignKeyDefaults option when a contract wants non-default FK materialization:

const contract = defineContract({
  family: sqlFamily,
  target: postgresPack,
  foreignKeyDefaults: { constraint: true, index: false },
  models: {
    // ...
  },
});

Per-FK overrides still live next to the FK authoring site, either via constraints.foreignKey(...) inside model .sql(...) or via rel.belongsTo(...).sql({ fk: ... }). See ADR 161.

Validating Contracts

Contract JSON validation flows through the per-target descriptor's contractSerializer SPI; this package focuses on authoring and lowering. End-user app code typically goes through the canonical façade (postgres<Contract>(...)), which threads the descriptor SPI internally.

import postgres from '@prisma-next/postgres/runtime';
import contractJson from './contract.json' with { type: 'json' };
import type { Contract, TypeMaps } from './contract.d';

const db = postgres<Contract, TypeMaps>({
  contractJson,
  url: process.env['DATABASE_URL']!,
});

For advanced consumers that need direct access to the SPI:

import postgresTarget from '@prisma-next/target-postgres/control';
import type { Contract } from './contract.d';

const contract = postgresTarget.contractSerializer.deserializeContract(
  contractJson,
) as Contract;

Config Helper

Use typescriptContract from this package when wiring TS-authored contracts in prisma-next.config.ts.

import { defineConfig } from '@prisma-next/cli/config-types';
import { typescriptContract } from '@prisma-next/sql-contract-ts/config-types';
import { contract } from './src/prisma/contract';

export default defineConfig({
  contract: typescriptContract(contract, 'src/prisma/contract.json'),
});

Optional third argument (options bag) for typescriptContract / typescriptContractFromPath, and defaultControlPolicy on emptyContract:

typescriptContract(contract, 'src/prisma/contract.json', { defaultControlPolicy: 'external' });

The specifier value applies only when the loaded contract omits defaultControlPolicy (a value authored on the contract module wins).

Dependencies

  • @prisma-next/config - ContractConfig types used by typescriptContract(...)
  • @prisma-next/contract-authoring - Shared descriptor types
  • @prisma-next/framework-components - Pack refs, authoring contributions, and codec lookup types
  • @prisma-next/sql-contract - SQL contract types and validation target

Testing

Unit tests for the authoring DSL live in this package. Broader integration tests that span authoring, emission, CLI, and runtime packages live in @prisma-next/integration-tests.

Migration Notes

  • Direct imports give you the structural DSL
  • The callback overload gives you pack-composed helper vocabularies
  • Import authoring helpers directly from @prisma-next/sql-contract-ts
  • Reach contract validation through the per-target descriptor's contractSerializer.deserializeContract(...) (or via the canonical façade)

See Also

  • @prisma-next/contract-authoring - Shared target-neutral authoring descriptor types
  • @prisma-next/sql-contract-psl - PSL parser-output to SQL contract interpreter
  • @prisma-next/sql-contract-psl/provider - SQL PSL-first prismaContract() helper

FAQs

Package last updated on 11 Jun 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts