Problem
There is no bulk upsert operation. Developers must loop over individual upsert calls, resulting in N separate SQL round-trips. This is unacceptable for data sync workflows, CSV imports, and any scenario where hundreds or thousands of rows need to be created-or-updated atomically.
Current pattern (N round-trips)
// Painfully slow: 500 individual upserts = 500 SQL round-trips
for (final record in syncPayload) {
await _prisma.client.contact.upsert(
where: ContactWhereUniqueInput(externalId: record.externalId),
create: ContactCreateInput(
externalId: record.externalId,
name: record.name,
email: record.email,
),
update: ContactUpdateInput(
name: record.name,
email: record.email,
),
);
}
Desired pattern (single SQL round-trip)
// Single SQL statement: INSERT ... ON CONFLICT DO UPDATE
final results = await _prisma.client.contact.upsertMany(
data: syncPayload.map((r) => ContactUpsertInput(
where: ContactWhereUniqueInput(externalId: r.externalId),
create: ContactCreateInput(
externalId: r.externalId,
name: r.name,
email: r.email,
),
update: ContactUpdateInput(
name: r.name,
email: r.email,
),
)).toList(),
);
Implementation approach
- Generate
upsertMany on each model delegate accepting a List<ModelUpsertInput>.
- In
SqlCompiler, emit a single INSERT INTO ... VALUES (...), (...), ... ON CONFLICT (unique_key) DO UPDATE SET ... statement.
- Detect the conflict target from the
where unique input fields.
- Map
update fields to the SET clause using EXCLUDED.column references.
- For PostgreSQL, use
ON CONFLICT ... DO UPDATE. (MySQL adapter, when available, would use ON DUPLICATE KEY UPDATE.)
- Handle
RETURNING * to return all upserted rows.
- Chunk very large batches (e.g., >1000 rows) to stay within parameter limits, executing multiple statements inside a single transaction.
- Add unit tests for: basic bulk upsert, conflict on composite unique keys, partial updates, and chunked batches.
Impact
- Transforms O(N) round-trips into O(1) for sync and import flows
- Critical for mobile offline-sync patterns where hundreds of records sync on reconnect
- PostgreSQL
ON CONFLICT is battle-tested and efficient
- Prisma JS lacks
upsertMany — this would be a differentiating feature
Problem
There is no bulk upsert operation. Developers must loop over individual
upsertcalls, resulting in N separate SQL round-trips. This is unacceptable for data sync workflows, CSV imports, and any scenario where hundreds or thousands of rows need to be created-or-updated atomically.Current pattern (N round-trips)
Desired pattern (single SQL round-trip)
Implementation approach
upsertManyon each model delegate accepting aList<ModelUpsertInput>.SqlCompiler, emit a singleINSERT INTO ... VALUES (...), (...), ... ON CONFLICT (unique_key) DO UPDATE SET ...statement.whereunique input fields.updatefields to theSETclause usingEXCLUDED.columnreferences.ON CONFLICT ... DO UPDATE. (MySQL adapter, when available, would useON DUPLICATE KEY UPDATE.)RETURNING *to return all upserted rows.Impact
ON CONFLICTis battle-tested and efficientupsertMany— this would be a differentiating feature