# Author in PSL (/docs/orm/next/contract-authoring/psl-syntax)

> For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL.

Write the Prisma Next contract as a Prisma schema file, using the schema language you already know plus the Prisma Next additions.

Location: ORM > Next > Contract authoring > Author in PSL

PSL is the preferred way to author the Prisma Next [data contract](https://www.prisma.io/docs/orm/next/contract-authoring/the-data-contract). You write a Prisma schema file, usually `prisma/contract.prisma`, and [`prisma-next contract emit`](https://www.prisma.io/docs/cli/next/contract-emit) turns it into `contract.json` and `contract.d.ts`.

If you know the Prisma schema language, most of a contract file reads exactly as you expect. This page covers the shared basics briefly and the Prisma Next additions in detail: named types, typed enums, value objects, base models with variants, and extension types.

## A complete contract [#a-complete-contract]

  

#### PostgreSQL

```prisma title="prisma/contract.prisma" 
// use prisma-next

types {
  Uuid = String @db.Uuid
}

type Address {
  street  String
  city    String
  zip     String?
  country String
}

enum Priority {
  @@type("pg/text@1")
  Low    = "low"
  High   = "high"
  Urgent = "urgent"
}

model User {
  id        Uuid     @id @default(uuid())
  email     String
  createdAt DateTime @default(now())
  address   Address?
  posts     Post[]

  @@map("user")
}

model Post {
  id        Uuid     @id @default(uuid())
  title     String
  userId    Uuid
  priority  Priority @default(Low)
  createdAt DateTime @default(now())

  user User @relation(fields: [userId], references: [id])

  @@map("post")
}
```

#### MongoDB

```prisma title="prisma/contract.prisma" 
// use prisma-next

type Address {
  street  String
  city    String
  zip     String?
  country String
}

enum UserRole {
  @@type("mongo/string@1")
  Admin  = "admin"
  Author = "author"
  Reader = "reader"
}

model User {
  id      ObjectId @id @map("_id")
  name    String
  email   String
  bio     String?
  role    UserRole
  address Address?
  posts   Post[]

  @@map("users")
}

model Post {
  id        ObjectId @id @map("_id")
  title     String
  content   String
  authorId  ObjectId
  createdAt DateTime

  author User @relation(fields: [authorId], references: [id])

  @@index([authorId])
  @@map("posts")
}
```

Run `npx prisma-next contract emit` after any change to refresh the artifacts.

## Point the config at the schema [#point-the-config-at-the-schema]

The config's `contract` path names the source of truth. A `.prisma` extension selects PSL authoring:

  

#### PostgreSQL

```typescript title="prisma-next.config.ts" 
import { defineConfig } from "@prisma-next/postgres/config";

export default defineConfig({
  contract: "./prisma/contract.prisma",
});
```

#### MongoDB

```typescript title="prisma-next.config.ts" 
import { defineConfig } from "@prisma-next/mongo/config";

export default defineConfig({
  contract: "./prisma/contract.prisma",
});
```

Scaffolded projects (`npx create-prisma@next`) already contain this wiring and a starter schema.

## Models and fields [#models-and-fields]

Models declare fields with a type, an optional `?` marker, and attributes:

* `@id` marks the primary key; `@@id([a, b])` declares a composite key.
* `@unique` adds a unique constraint; `@@index([...])` declares a secondary index.
* `@default(...)` sets a default. Database function defaults such as `@default(now())` become column defaults in the database. Generated defaults such as `@default(uuid())` are applied by Prisma Next before each write instead, so they work the same on every database and appear in the contract's `execution` section rather than as DDL.
* `@map("column_name")` sets a field's physical name; `@@map("table_name")` sets the table or collection name when it differs from the model name.

How IDs map differs by database:

#### PostgreSQL

```prisma
model User {
  id Uuid @id @default(uuid())
}
```

The primary key is an ordinary column; pick its type and default yourself.

#### MongoDB

```prisma
model User {
  id ObjectId @id @map("_id")
}
```

The primary key is MongoDB's `_id`: type it `ObjectId` and map it to the physical `_id` key.

## Named types [#named-types]

The `types` block declares reusable type aliases. An alias binds a base type and its storage details under one name:

```prisma
types {
  Uuid = String @db.Uuid
}
```

Fields then use `Uuid` like any built-in type. The alias keeps the storage decision (`uuid` columns rather than `text`) in one place.

## Enums [#enums]

An enum in Prisma Next declares its storage codec with `@@type` and, optionally, the stored value for each member:

```prisma
enum Priority {
  @@type("pg/text@1")
  Low    = "low"
  High   = "high"
  Urgent = "urgent"
}
```

`@@type("pg/text@1")` stores the values as `text` on PostgreSQL. When a member has no explicit value, the member name itself is stored. In the database, the emitted contract enforces the allowed values with a `CHECK` constraint on each column that uses the enum.

## Value objects [#value-objects]

A `type` block declares a value object: a structured value that belongs to its parent record and has no identity or table of its own.

```prisma
type Address {
  street  String
  city    String
  zip     String?
  country String
}

model User {
  id      Uuid     @id @default(uuid())
  address Address?
}
```

Storage follows the database's nature: on PostgreSQL a value object field lives in a single `jsonb` column, and on MongoDB it is an embedded document. Either way, `contract.d.ts` types it as a structured object rather than untyped JSON. On MongoDB, whether to embed or reference is the central modeling decision; [MongoDB data modeling](https://www.prisma.io/docs/orm/next/data-modeling/mongodb#embed-or-reference) covers it.

## Relations [#relations]

Relations use the `@relation` syntax you know from Prisma ORM. The side that holds the foreign key declares the scalar field and the mapping; the other side declares a list:

```prisma
model Post {
  userId Uuid
  user   User @relation(fields: [userId], references: [id])
}

model User {
  posts Post[]
}
```

Many-to-many relations go through an explicit join model with a composite primary key. The list fields on both sides then resolve through it:

```prisma
model Post {
  tags Tag[]
}

model Tag {
  posts Post[]
}

model PostTag {
  postId Uuid
  tagId  Uuid

  post Post @relation(fields: [postId], references: [id])
  tag  Tag  @relation(fields: [tagId], references: [id])

  @@id([postId, tagId])
  @@map("post_tag")
}
```

The emitted contract records the relation as `N:M` with the join table's columns, so queries can traverse `post.tags` directly.

## Base models and variants [#base-models-and-variants]

A base model declares a discriminator field, and each variant names its base and its discriminator value:

```prisma
model Task {
  id     Uuid   @id @default(uuid())
  title  String
  type   String

  @@discriminator(type)
  @@map("task")
}

model Bug {
  severity     String
  stepsToRepro String?

  @@base(Task, "bug")
  @@map("bug")
}
```

Rows with `type = "bug"` are `Bug` records. On PostgreSQL, the variant's `@@map` picks its storage layout: with its own `@@map`, as here, the variant's fields live in their own table sharing the base model's primary key; without one, they live in the base table as nullable columns. [Relational data modeling](https://www.prisma.io/docs/orm/next/data-modeling/relational-databases#polymorphic-relations) covers choosing between the two. On MongoDB, variants add their fields to documents in the base model's collection, so a variant declares `@@base` but no `@@map` of its own.

## Extension types [#extension-types]

Extension packs contribute types through constructor expressions in the `types` block. Compose the pack in the config, then use its types:

```typescript title="prisma-next.config.ts"
import pgvector from "@prisma-next/extension-pgvector/control";
import { defineConfig } from "@prisma-next/postgres/config";

export default defineConfig({
  contract: "./prisma/contract.prisma",
  extensions: [pgvector],
});
```

```prisma title="prisma/contract.prisma"
types {
  Embedding1536 = pgvector.Vector(1536)
}

model Post {
  id        Uuid           @id @default(uuid())
  embedding Embedding1536?
}
```

Emission validates extension types against the composed packs: a type from a pack that is not listed in the config fails the emit with a diagnostic. Re-run `contract emit` after changing the extension list.

## Starting from an existing database [#starting-from-an-existing-database]

If the database already exists, don't write the schema by hand. [`prisma-next contract infer`](https://www.prisma.io/docs/cli/next/contract-infer) reads the live schema and writes a starter `contract.prisma` for you to review and edit.

The syntax above declares relations; for which shape to choose and which side owns the foreign key, see [relational data modeling](https://www.prisma.io/docs/orm/next/data-modeling/relational-databases) and [MongoDB data modeling](https://www.prisma.io/docs/orm/next/data-modeling/mongodb).

## Prompt your coding agent [#prompt-your-coding-agent]

Projects scaffolded with `create-prisma@next` install [Prisma Next skills](https://www.prisma.io/docs/ai/tools/skills#available-skills-for-prisma-next) for your coding agent; the `prisma-next-contract` skill covers this page. Ask your agent to:

* "Using the prisma-next-contract skill, add a Status enum stored as text and use it on the Order model."
* "Add a one-to-many between User and Post with the foreign key on Post."
* "Give the Post model a composite unique constraint on userId and title."

## Next steps [#next-steps]

* Emit and inspect [the artifacts](https://www.prisma.io/docs/orm/next/contract-authoring/the-contract-artifact) the schema produces.
* If you prefer defining models in code, see [authoring in TypeScript](https://www.prisma.io/docs/orm/next/contract-authoring/typescript-schema-builder).
* Apply the contract to a database with [`prisma-next db init`](https://www.prisma.io/docs/cli/next/db-init) or plan changes with [`prisma-next migration plan`](https://www.prisma.io/docs/cli/next/migration-plan).

## Related pages

- [`Author in TypeScript`](https://www.prisma.io/docs/orm/next/contract-authoring/typescript-schema-builder): Define the Prisma Next contract with a typed builder API instead of a schema file. Same models, same artifacts, no separate language.
- [`Capabilities`](https://www.prisma.io/docs/orm/next/contract-authoring/capabilities): Capabilities record what your database stack supports, so Prisma Next can reject unsupported features early with a clear error.
- [`The data contract`](https://www.prisma.io/docs/orm/next/contract-authoring/the-data-contract): The data contract is the single description of your data model and its storage layout. Everything in Prisma Next is typed, planned, and verified against it.
- [`The emitted artifacts`](https://www.prisma.io/docs/orm/next/contract-authoring/the-contract-artifact): contract.json and contract.d.ts are the deterministic artifacts every other part of Prisma Next consumes. Here is what is inside them.