Skip to main content

Prisma

@camusdb/prisma-adapter lets a TypeScript application use Prisma Client with CamusDB. Define models in a Prisma schema, generate a typed client, and use Prisma queries, relations, and transactions against CamusDB over REST or gRPC. The package also includes camusdb-prisma, a CLI for translating and applying schema migrations with a recorded history.

Use it when you want Prisma's model-based API. For direct SQL or access to the full database administration API, use the TypeScript connector underneath the adapter. Source and a runnable application are in camusdb-prisma.

Install​

Use Prisma 7.10 or later within the 7.x or 8.x lines, and the camusdb driver 0.8.x. Supported Node.js versions are ^20.19.0 || >=22.12.0. The repository reports testing against CamusDB 0.16; choose a server with the SQL features your models use.

npm install @camusdb/prisma-adapter camusdb @prisma/client
npm install --save-dev prisma

Keep prisma and @prisma/client on matching versions within the supported range. Start a CamusDB server using the installation guide. The examples use REST at http://localhost:5095.

Define and generate a client​

Create prisma/schema.prisma:

prisma/schema.prisma
generator client {
provider = "prisma-client"
output = "../generated/prisma"
}

datasource db {
provider = "mysql"
relationMode = "prisma"
}

model User {
id String @id @default(uuid())
email String @unique
name String?
posts Post[]
}

model Post {
id String @id @default(uuid())
title String
published Boolean @default(false)
authorId String
author User @relation(fields: [authorId], references: [id])

@@index([authorId])
}

provider = "mysql" selects the SQL dialect Prisma generates. The adapter rewrites the differences CamusDB needs, then sends statements through the CamusDB driver. CamusDB itself serves REST and gRPC, without a MySQL wire endpoint.

relationMode = "prisma" uses Prisma's relation handling without database foreign keys. Keep indexes on referencing columns, as with authorId above. See relation enforcement before choosing a mode.

Add the Prisma CLI configuration:

prisma.config.ts
import { defineConfig } from 'prisma/config';

export default defineConfig({
schema: './prisma/schema.prisma',
datasource: { url: 'mysql://camusdb@localhost:5095/camusdb' },
});

This MySQL URL is a placeholder required by the file-based prisma migrate diff workflow. That comparison does not open it. The real CamusDB connection is configured separately below.

Generate the client and apply the initial migration:

npx prisma generate
export CAMUSDB_URL="Endpoint=http://localhost:5095;Database=app"
npx camusdb-prisma migrate dev --name init

The CLI creates the target database if absent. Pass --no-create-database when database creation is managed separately.

Query your models​

In an ESM application at the project root:

import { PrismaCamusDb } from '@camusdb/prisma-adapter';
import { PrismaClient } from './generated/prisma/client.js';

const adapter = new PrismaCamusDb({
endpoint: 'http://localhost:5095',
database: 'app',
});
const prisma = new PrismaClient({ adapter });

try {
const user = await prisma.user.create({
data: { email: 'ada@example.com', name: 'Ada' },
});
await prisma.post.create({
data: { title: 'First post', published: true, authorId: user.id },
});

const authors = await prisma.user.findMany({
where: { posts: { some: { published: true } } },
include: { posts: true },
});
console.log(authors);
} finally {
await prisma.$disconnect();
}

PrismaCamusDb also accepts a CamusDB connection string. To use the environment variable set earlier, check it before constructing the adapter:

const connectionString = process.env.CAMUSDB_URL;
if (!connectionString) throw new Error('Set CAMUSDB_URL');
const adapter = new PrismaCamusDb(connectionString);

The options object accepts the same connection settings as CamusClient, including authentication and protocol: 'grpc'. For gRPC, use its endpoint (normally port 5096) and install the connector's optional gRPC dependencies. See connection options.

Prisma reads included relations with separate statements. The adapter disables Prisma's relation-join strategy because it depends on MySQL JSON aggregation functions CamusDB does not provide.

Choose relation enforcement​

ModeBehaviorConsiderations
relationMode = "prisma"Prisma handles relations and referential actions.Supports actions such as Cascade and SetNull through Prisma. Raw SQL and other writers do not gain database foreign-key enforcement.
relationMode = "foreignKeys" (default)CamusDB enforces foreign keys at the end of each statement.Only Restrict and NoAction actions are supported.

Prisma defaults to onUpdate: Cascade, and optional relations default to onDelete: SetNull. In foreign-key mode the translator replaces unsupported CASCADE, SET NULL, and SET DEFAULT actions with NO ACTION and reports warnings. Prisma then leaves enforcement to the database; it does not perform the replaced action. A parent delete or key update can fail with P2003.

To make database enforcement explicit, declare supported actions:

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

Cycles across two or more tables are refused with CADB0416; self-references are accepted. See foreign keys for database behavior.

Migrations​

Use camusdb-prisma for schema changes. Standard prisma migrate dev attempts a MySQL wire connection through Prisma's schema engine, so it cannot connect to CamusDB through this adapter.

The CLI compares schema files with prisma migrate diff, translates the MySQL DDL to CamusSQL, and applies it through the CamusDB driver:

export CAMUSDB_URL="Endpoint=http://localhost:5095;Database=app"

npx camusdb-prisma migrate dev --name add_posts
npx camusdb-prisma migrate deploy
npx camusdb-prisma migrate status
npx camusdb-prisma db push
CommandPurpose
migrate dev --name labelGenerates a migration from the last snapshot and applies pending migrations.
migrate deployApplies pending migration directories in name order.
migrate statusReports applied, pending, changed, and missing migration files.
db pushApplies the schema difference and updates the snapshot without writing a migration history entry. Use for disposable development databases.

Run npx camusdb-prisma --help for connection and path flags. Use npx camusdb-prisma translate -- migration.sql to inspect the CamusSQL translation of a MySQL DDL file without applying it.

Keep the snapshot and review warnings​

Commit the entire prisma/migrations directory, including:

  • .snapshot.prisma: the schema used as the starting point of the next diff.
  • <timestamp>_<label>/migration.sql: the CamusSQL statements that are applied.
  • <timestamp>_<label>/migration.mysql.sql: the original DDL for reference.

The CLI uses the snapshot instead of replaying history into a shadow database. A missing snapshot makes the next diff start from an empty schema. It does not inspect the live database for drift.

_camusdb_migrations records each completed migration's name, checksum, application time, and statement count. migrate status detects edited or missing applied files; deployment skips names already recorded as applied.

Statements run sequentially, without a transaction around the migration. If a later statement fails, earlier changes can remain while the migration is still unrecorded. Inspect and resolve that partial state before rerunning it.

Read translation warnings: unsupported referential actions, decimal precision, and in-place type changes can make the applied schema differ from your model. CamusDB cannot change a column's type or length in place; the translator applies nullable changes and warns about the rest. For a type change, add a replacement column, copy values, drop the old column, and rename the replacement.

Type mapping​

Prisma fieldCamusDB storage
StringVARCHAR(191) by default; @db.Text maps to TEXT.
Int, BigIntINT64.
Int @default(autoincrement())INT64 GENERATED BY DEFAULT AS IDENTITY.
FloatFLOAT64.
BooleanBOOL.
DateTimeDATETIME.
BytesBYTES.
DecimalNUMERIC, with 38 digits total and 9 fractional digits.
JsonSTRING; returned as text, without JSON-path filtering.
EnumSTRING; the database does not restrict values to enum members.

Integer and scalar decimal values travel through the adapter without conversion to JavaScript floating-point numbers. Use BigInt in your Prisma model when application values need the 64-bit integer range. Decimal values beyond 9 fractional digits are rounded; more than 29 integer digits are refused with P2020. @db.Decimal(p, s) still maps to CamusDB's fixed precision and scale. Decimal list parameters use FLOAT64 because the connector has no NUMERIC array mapping; they do not have the same exactness as scalar decimal parameters.

Let the database generate identity keys. Explicitly inserting a key does not advance its sequence, so a later generated value can collide with it (P2002). The adapter discovers identity columns with DESCRIBE and uses RETURNING to report generated keys to Prisma.

Native MySQL types without a mapping, including @db.Time, @db.Year, @db.Bit, and spatial types, are refused by the translator.

Transactions and errors​

prisma.$transaction uses CamusDB transaction handles. For example, using the models above:

await prisma.$transaction(async (tx) => {
await tx.post.updateMany({
where: { authorId: userId },
data: { published: true },
});
await tx.user.update({ where: { id: userId }, data: { name: 'Ada Lovelace' } });
}, { isolationLevel: 'Serializable' });

Serializable and ReadCommitted are supported. Leave the option unset to use the server default. RepeatableRead, ReadUncommitted, and Snapshot are rejected. Savepoints are not supported.

The adapter maps matching database errors to Prisma error kinds:

CamusDB codePrisma result
CADB0300P2002: unique constraint violation.
CADB0301P2011: null constraint violation.
CADB0304, CADB0305, CADB0306P2003: foreign-key violation.
CADB0417P2020: value out of range.
CADB0011P2021: table does not exist.
CADB0404P2022: column not found.
CADB0502, CADB0504TransactionWriteConflict.
CADB0516P1000: authentication failed.

Other CamusDB errors remain raw database errors. Adapter error payloads retain originalCode and originalMessage for CamusDB failures. See transaction isolation and error codes.

Raw SQL and C# extensions​

Write CamusSQL in $queryRaw and $executeRaw; raw statements are not a general MySQL-to-CamusSQL translation path. Quote table and column identifiers with backticks, especially names such as User that are reserved words:

const rows = await prisma.$queryRaw<{ email: string }[]>`
SELECT \`email\` FROM \`User\` WHERE \`email\` = ${'ada@example.com'}
`;

Use $executeRaw for an installed C# procedure, or $queryRaw for an extension function. A procedure joins the enclosing Prisma transaction and reports its changed-row count. See C# extensions for deployment and CREATE EXTENSION installation requirements.

For triggers or other objects Prisma does not model, add a manually written migration.sql in a directory whose name sorts after existing migrations, then run migrate deploy. This file must contain CamusSQL; deployment runs it without translation. Schema diffs do not track these manually managed objects.

Count rows matching a relation​

Relation filters such as some, none, and every work in both findMany and count on a server with nested-subquery binding support:

const publishedAuthors = await prisma.user.count({
where: { posts: { some: { published: true } } },
});

Prisma can place this filter inside a derived table under COUNT(*). Older server builds rejected that shape with CADB0099; upgrade the server rather than fetching matching rows just to count them.

Query limitations​

  • Scalar subqueries in a projection are unsupported, including in raw SQL.
  • JSON fields return text, and filters inside JSON documents are unsupported.
  • Database foreign keys support only Restrict and NoAction; use Prisma relation mode when application-managed cascading is required.

To inspect generated and rewritten statements during development:

DEBUG='prisma:driver-adapter:camusdb' node your-app.js

The repository's example application shows models, relation queries, grouping, transactions, and raw SQL together.