# Writing data (/docs/orm/fundamentals/writing-data) > 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. Create, update, delete, and upsert records with Prisma ORM, one at a time or in bulk. Location: ORM > Fundamentals > Writing data This page shows how to write data with Prisma ORM: [creating](#create-one-record), [updating](#update-one-record), [deleting](#delete-one-record), and [upserting](#upsert-a-record) single records, and [writing many records at once](#write-many-records). Every example imports `db`. In a new project run `npm create prisma@latest -- my-app`, and in an existing project run `npx prisma orm init`. Either way you get `src/prisma/db.ts`, which exports `db`, and the import is `./prisma/db` from a file in `src/`. This page uses `db.orm`, which holds your models. The rest of `db` is `db.sql` for the SQL query builder, `db.raw.sql` for raw SQL, and `db.transaction` for running several writes together. On PostgreSQL the path to a model is `db.orm..`, so the `User` model is `db.orm.public.User`. `public` is the PostgreSQL schema that holds the model's table, and the path always includes the schema name. The schema is `public` unless you put the model inside a `namespace` block, as [Example schema](#example-schema) shows. On MongoDB there is no schema segment, and the path is `db.orm.`, where the collection name is the model's `@@map(...)` value, or the model name when there is no `@@map`. The MongoDB `User` model in the example schema maps to `users`, so it is `db.orm.users`. ## Example schema [#example-schema] Examples use this contract. In Prisma ORM 8 the schema file is `contract.prisma` instead of `schema.prisma`; the docs call it your contract. `@default(cuid(2))` means Prisma ORM generates the `id` for you, so you never pass one: **Expand for sample schema** #### PostgreSQL ```prisma model User { id String @id @default(cuid(2)) email String @unique name String? createdAt DateTime @default(now()) posts Post[] } model Post { id String @id @default(cuid(2)) title String content String? published Boolean authorId String author User @relation(fields: [authorId], references: [id]) createdAt DateTime @default(now()) } ``` #### MongoDB ```prisma model User { id ObjectId @id @map("_id") email String @unique name String? createdAt Date posts Post[] @@map("users") } model Post { id ObjectId @id @map("_id") title String content String? published Bool tags String[] author User @relation(fields: [authorId], references: [id]) authorId ObjectId createdAt Date @@map("posts") } ``` To put models in another PostgreSQL schema, wrap them in a `namespace` block: ```prisma namespace billing { model Invoice { id String @id @default(cuid(2)) amount Int } } ``` The model is then only at `db.orm.billing.Invoice`, and the block name is the PostgreSQL schema name. ## Create one record [#create-one-record] Use `.create(...)` to insert one record, and pass the fields directly. Prisma ORM returns the inserted record, including generated values such as IDs and database defaults: #### PostgreSQL ```typescript import { db } from "./prisma/db"; const user = await db.orm.public.User.create({ email: "jane@prisma.io", name: "Jane", }); // user.id and user.createdAt are filled in ``` #### MongoDB ```typescript import { db } from "./prisma/db"; const user = await db.orm.users.create({ email: "jane@prisma.io", name: "Jane", createdAt: new Date(), }); // user._id is filled in by the server ``` The returned record is complete, so you can use the generated values right away (PostgreSQL shown; on MongoDB the key is `_id`): ```js no-copy { id: 'cuid20000000000000000003', email: 'jane@prisma.io', name: 'Jane', createdAt: 2026-07-06T09:09:56.119Z } ``` To get back only some fields, chain `.select(...)` before `.create(...)`: the record is still inserted in full, but you only get back the fields you listed: ```typescript const account = await db.orm.public.User .select("id", "email") .create({ email: "jane@prisma.io", name: "Jane" }); ``` ```js no-copy { id: 'cuid20000000000000000003', email: 'jane@prisma.io' } ``` `.select(...)` works the same before `update`, `delete`, `upsert`, `createAll`, `updateAll`, and `deleteAll`. The `AndCount` methods give you back a number, so `.select(...)` has no effect on them. When a write breaks a unique constraint, the call throws, and errors have no shared class: a PostgreSQL database error carries `sqlState` (a five-character SQL state code), a MongoDB driver error carries a numeric `code`, and a Prisma ORM error carries a string `code` such as `RUNTIME.ITERATOR_CONSUMED`. On PostgreSQL, `23505` is the SQL state code for a unique violation. The [error reference](https://www.prisma.io/docs/orm/reference/error-reference) lists Prisma ORM's own codes: ```typescript try { await db.orm.public.User.create({ email: "jane@prisma.io", name: "Jane" }); } catch (error) { if ((error as { sqlState?: string }).sqlState === "23505") { // that email is already taken } } ``` On MongoDB the driver throws its own error, and a duplicate key gives you an error whose `error.code` is `11000`. > [!NOTE] > MongoDB contracts do not support `@default`. For a timestamp, type the field `temporal.createdAt()` or `temporal.updatedAt()` and a client created by `mongo()` fills it in, as [Field types](https://www.prisma.io/docs/orm/data-modeling/mongodb#field-types) shows. Pass any other value yourself. > > `@map("_id")` renames the id field to `_id` everywhere, so the returned document has an `_id` key and not an `id` key. ## Create a record and its related records [#create-a-record-and-its-related-records] To write related records in the same call, pass a callback for the relation field. The callback's argument, named `p` below, is the relation builder, which holds the methods that link or insert related records: ```typescript const user = await db.orm.public.User.create({ email: "jane@prisma.io", name: "Jane", posts: (p) => p.create([{ title: "First post", content: null, published: false }]), }); ``` You do not pass `authorId` on the nested posts, because Prisma ORM fills it in from the user it just inserted. `connect` links a record that already exists, and works in `.update(...)` too: ```typescript await db.orm.public.User .where({ email: "jane@prisma.io" }) .update({ posts: (p) => p.connect([{ id: existingPostId }]) }); ``` `p.disconnect(...)` unlinks a related record, but it applies on `.update(...)` only, not on `.create(...)`. `connectOrCreate`, the relation `set`, and nested updates, upserts, and deletes do not exist: see [Not available](https://www.prisma.io/docs/orm/coming-from-prisma-orm-7#not-available-yet). For the full picture of relations, see [Relations and joins](https://www.prisma.io/docs/orm/fundamentals/relations-and-joins). ## Update one record [#update-one-record] Use `.where(...)` to pick the record, then `.update(...)` with the fields to change, which updates **one** matching record and returns it: #### PostgreSQL ```typescript const updatedUser = await db.orm.public.User .where({ email: "jane@prisma.io" }) .update({ name: "Jane Doe" }); ``` #### MongoDB ```typescript const updatedUser = await db.orm.users .where({ email: "jane@prisma.io" }) .update({ name: "Jane Doe" }); ``` ```js no-copy { id: 'cuid20000000000000000003', email: 'jane@prisma.io', name: 'Jane Doe', createdAt: 2026-07-06T09:09:56.119Z } ``` When nothing matches the filter, `.update(...)` returns `null` rather than throwing. When the filter matches more than one record, `.update(...)` still changes only one of the matching records, with no guaranteed order. Use [`updateAll` or `updateAndCount`](#write-many-records) if you mean all of them. On MongoDB you can also pass a callback instead of an object, and change a field with an operation rather than a value. The callback's argument, named `p` here, gives you one entry per field, and each field carries the operations you can apply to it: ```typescript await db.orm.posts .where({ title: "Draft thoughts" }) .update((p) => [p.content.set("Now filled in"), p.published.set(true)]); ``` The callback returns an array, so you can apply several operations in one update. `set` and `unset` work on any field, while `inc` and `mul` are on number fields only. `push`, `pull`, `addToSet`, and `pop` are for array fields, and you call them the same way: `p.tags.push("news")`. These field operations are MongoDB only: PostgreSQL has no callback form of `update`, so on PostgreSQL you pass an object. See [Field update operations](https://www.prisma.io/docs/orm/reference/orm-client#field-update-operations) in the reference. There is no `increment` on PostgreSQL, so to add to a number in place, write the update as raw SQL. For a `views Int` column added to `Post`, `db.raw.sql` writes a raw statement, `.affectedCount()` says you want the row count back as `{ affectedRows }`, `.build()` finishes it, and `db.runtime().execute(...)` runs it; [Advanced queries](https://www.prisma.io/docs/orm/fundamentals/advanced-queries) explains raw SQL: ```typescript const query = db.raw.sql`UPDATE post SET views = views + 1 WHERE id = ${postId}` .affectedCount() .build(); const { affectedRows } = await db.runtime().execute(query); ``` ## Delete one record [#delete-one-record] Use `.where(...)` then `.delete()`, which deletes **one** matching record and returns it: #### PostgreSQL ```typescript const deletedUser = await db.orm.public.User .where({ email: "jane@prisma.io" }) .delete(); ``` #### MongoDB ```typescript const deletedUser = await db.orm.users .where({ email: "jane@prisma.io" }) .delete(); ``` `.delete()` returns `null` when nothing matches, and deletes only one record when several match. See [Update one record](#update-one-record). To delete every match, use [`deleteAll` or `deleteAndCount`](#write-many-records). ## Upsert a record [#upsert-a-record] Use `.upsert(...)` to update a record if it exists and create it otherwise, and pass the two branches separately: #### PostgreSQL ```typescript await db.orm.public.User.upsert({ create: { email: "eve@prisma.io", name: "Eve" }, update: { name: "Eve Exists" }, conflictOn: { email: "eve@prisma.io" }, }); ``` #### MongoDB ```typescript await db.orm.users.where({ email: "eve@prisma.io" }).upsert({ create: { email: "eve@prisma.io", name: "Eve", createdAt: new Date() }, update: { name: "Eve Exists" }, }); ``` `conflictOn` repeats the unique field and its value from `create`; Prisma ORM uses it to look for an existing row. For a unique constraint over several columns, pass them all in one object: `conflictOn: { tenantId, email }`. Without `conflictOn` on PostgreSQL, the upsert looks for a row by primary key, which a new record does not have, so the insert runs and fails on the unique constraint. Always pass `conflictOn`. On MongoDB, there is no `conflictOn`, so put the match in `.where(...)` before `.upsert(...)`. ## Write many records [#write-many-records] Use the `All` and `AndCount` methods when you intend to write every record you pass, or change every record the filter matches: ```typescript const user = await db.orm.public.User.first({ email: "jane@prisma.io" }); if (!user) throw new Error("no such user"); // Insert many records const newPosts = await db.orm.public.Post.createAll([ { title: "One", content: null, published: false, authorId: user.id }, { title: "Two", content: null, published: false, authorId: user.id }, ]); // Insert many, get back only the number inserted const insertedCount = await db.orm.public.Post.createAndCount([ { title: "Three", content: null, published: false, authorId: user.id }, ]); // Update every match, get back only the number updated const updatedCount = await db.orm.public.Post.where({ published: false }).updateAndCount({ published: true }); // Delete every match, get back the deleted records const deletedPosts = await db.orm.public.Post.where({ published: false }).deleteAll(); // Delete every match, get back only the number deleted const deletedCount = await db.orm.public.Post.where((p) => p.title.ilike("draft%")).deleteAndCount(); ``` `createAll` gives you back the inserted records, with their generated IDs: ```js no-copy [{ id: 'cuid20000000000000000101', title: 'One', published: false, /* ... */ }, { id: 'cuid20000000000000000102', title: 'Two', published: false, /* ... */ }] ``` The `AndCount` methods return a plain number, so if three posts match, `updatedCount` is `3`, not an object. To insert many records and skip the ones that would break a unique constraint, pass `{ onConflict: "skip" }` as the second argument to `createAll` or `createAndCount`. That skips a record that breaks any unique constraint on the table. To skip only the records that clash on one constraint, add `conflictOn` to the same object with the names of that constraint's fields, as in `createAll(users, { onConflict: "skip", conflictOn: ["email"] })`. A record that clashes on any other unique constraint is not skipped, so the database rejects the insert and the call throws a unique-constraint error. Here `conflictOn` takes field names, while in [`.upsert(...)`](#upsert-a-record) it takes the fields and their values. This replaces Prisma ORM 7's `skipDuplicates`, and it works on PostgreSQL and SQLite. On MongoDB, `createAll` and `createAndCount` take only the records, with no second argument. `createAll` returns only the records the database wrote, and `createAndCount` counts only those. [`createAll()`](https://www.prisma.io/docs/orm/reference/orm-client#createall) in the reference covers the details. `.where((p) => p.title.ilike("draft%"))` is the callback form of a filter, for conditions an object cannot express. Its argument gives you one entry per field, and each field carries the comparisons you can apply to it. [Reading data](https://www.prisma.io/docs/orm/fundamentals/reading-data) covers the filters you can write. The bulk methods work the same on MongoDB, on the collection: `db.orm.posts`. Pass `createdAt` in every object you give `createAll`, as with `create`. ## Return rows or counts [#return-rows-or-counts] Each write comes in three forms, and you pick by what you need back: | Form | What it writes | What you get back | | ---------------------------------------------------- | ------------------------------------- | ----------------------------- | | `create`, `update`, `delete` | one record | that record | | `createAll`, `updateAll`, `deleteAll` | every record you pass, or every match | those records | | `createAndCount`, `updateAndCount`, `deleteAndCount` | every record you pass, or every match | the number of records written | `update` and `delete` return `null` when nothing matches. Use the `AndCount` forms when the number is all you need, because they do not send the records back. The `All` forms return a result you can use two ways. Nothing is sent to the database until you `await` the result or loop over it, so an `updateAll` you never await changes nothing. `await` it for an array of records: ```typescript const publishedPosts = await db.orm.public.Post .where({ published: false }) .updateAll({ published: true }); ``` ```js no-copy [{ id: 'cuid20000000000000000101', title: 'One', published: true, /* ... */ }, { id: 'cuid20000000000000000102', title: 'Two', published: true, /* ... */ }] ``` Or [iterate it with `for await`](https://www.prisma.io/docs/orm/fundamentals/reading-data#iterate-a-large-result) to handle records as they arrive: ```typescript const updated = db.orm.public.Post.where({ published: false }).updateAll({ published: true }); for await (const post of updated) { console.log(post.id); } ``` Pick one: `await` it or loop it with `for await`. Mixing the two throws an error whose `error.code` is `RUNTIME.ITERATOR_CONSUMED`. ## Common mistakes [#common-mistakes] ### Updating or deleting more than one record [#updating-or-deleting-more-than-one-record] You filtered on a non-unique field and expected every match to change: ```typescript await db.orm.public.Post.where({ published: false }).update({ published: true }); ``` `.update(...)` and `.delete()` only change one record, even when the filter matches many. See [Update one record](#update-one-record). When you intend to affect every match, say so with the bulk methods: ```typescript const updatedCount = await db.orm.public.Post .where({ published: false }) .updateAndCount({ published: true }); ``` Use `updateAll` or `deleteAll` when you also need the changed records back, and `updateAndCount` or `deleteAndCount` when the number is enough. `update` and `delete` never change more than one record, so they stay safe for the one-record case. ### Wrapping create fields in a data object [#wrapping-create-fields-in-a-data-object] You wrote the Prisma ORM 7 shape, which fails type-checking because there is no field named `data` on your models: ```diff - await db.orm.public.User.create({ data: { email, name } }); + await db.orm.public.User.create({ email, name }); ``` You pass the record's own fields, and you get the record back. ### Updating or deleting without a filter [#updating-or-deleting-without-a-filter] You called `.update(...)` or `.delete()` straight on the model: ```typescript await db.orm.public.User.delete(); ``` Both need a `.where(...)` first, and the call does not type-check without one. If you truly mean every record, pass an empty filter, which adds no condition and so matches everything. `.delete()` needs a `.where(...)` for the same reason, and `.where({})` satisfies it there too: ```typescript await db.orm.public.User.where({}).deleteAll(); ``` ### Running related writes back to back [#running-related-writes-back-to-back] You created a user, then created their first post as a second await: ```typescript const user = await db.orm.public.User.create({ email, name }); const post = await db.orm.public.Post.create({ title, published: false, authorId: user.id }); ``` If the second write fails, the first has already committed, and you're left with half the operation. When writes must succeed together, run them in a [transaction](https://www.prisma.io/docs/orm/fundamentals/transactions), and inside the callback, query through `tx` instead of `db`: ```typescript await db.transaction(async (tx) => { const user = await tx.orm.public.User.create({ email, name }); await tx.orm.public.Post.create({ title, published: false, authorId: user.id }); }); ``` ### Passing an array of queries to a transaction [#passing-an-array-of-queries-to-a-transaction] Prisma ORM 7 supported `$transaction([query1, query2])`, but Prisma ORM 8 does not: there is no `$transaction`, and queries don't queue up in arrays. Put the calls inside one `db.transaction(...)` callback instead, and the [Transactions page](https://www.prisma.io/docs/orm/fundamentals/transactions) shows the pattern. ## Prompt your coding agent [#prompt-your-coding-agent] Projects created with `npm create prisma@latest` include the [Prisma ORM skills](https://www.prisma.io/docs/ai/tools/skills#available-skills-for-prisma-8) for your coding agent, and in an existing project you run `npx prisma skills sync` to add them. Skills are instruction files that tell the agent how Prisma ORM 8 works, and the `prisma-8` skill covers everything on this page, so try prompts that map to each section: * "Using the prisma-8 skill, add a signup function that creates a User and returns only its id and email." * "Write an upsert that creates a user by email or updates their name if they exist." * "This cleanup script must delete every draft older than 30 days. Use the bulk delete method and log how many records were removed." * "Review my mutations for places where .update() should be updateAll or updateAndCount." ## Next [#next] * [Run several writes atomically](https://www.prisma.io/docs/orm/fundamentals/transactions) with `db.transaction(...)`. * [Read data](https://www.prisma.io/docs/orm/fundamentals/reading-data) to filter, sort, paginate, and select fields from your models. * [Use the SQL builder](https://www.prisma.io/docs/orm/fundamentals/advanced-queries#postgresql-sql-query-builder) for inserts and updates with explicit `RETURNING` clauses. ## Related pages - [`Advanced queries`](https://www.prisma.io/docs/orm/fundamentals/advanced-queries): Use the SQL query builder on PostgreSQL and the pipeline builder on MongoDB for queries the ORM API can't express. - [`Reading data`](https://www.prisma.io/docs/orm/fundamentals/reading-data): Fetch one record or many with Prisma ORM, then filter, select, sort, paginate, and iterate the results. - [`Relations and joins`](https://www.prisma.io/docs/orm/fundamentals/relations-and-joins): Read related records in one query with .include(), and understand how one-to-one, one-to-many, and many-to-many relationships work. - [`Transactions`](https://www.prisma.io/docs/orm/fundamentals/transactions): Run several writes so they all succeed or all fail together with db.transaction().