Prisma
Prisma Client as a Suku service.
This follows the database pattern. The code is from examples/database and
is tested there against SQLite through Prisma's libSQL driver adapter.
Pin your Prisma versions
At the time of writing, the latest tag of the prisma CLI on npm is an 8.0 release
candidate with a different command set and no generate command, while @prisma/client is
7.10. Install matching versions explicitly. This guide was tested with 7.10.1 of both.
bun add @prisma/client@7.10.1 @prisma/adapter-libsql
bun add -d prisma@7.10.1Schema and config
Prisma 7 keeps the connection URL out of the schema file and requires a driver adapter.
generator client {
provider = "prisma-client"
output = "../src/generated/prisma"
}
datasource db {
provider = "sqlite"
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
@@map("users")
}import { defineConfig } from 'prisma/config';
export default defineConfig({
schema: 'prisma/schema.prisma',
datasource: { url: 'file:./prisma/dev.db' },
});Generate the client, and keep it out of version control.
{ "scripts": { "postinstall": "prisma generate" } }Connection
import { PrismaLibSql } from '@prisma/adapter-libsql';
import { PrismaClient } from './generated/prisma/client.js';
export class PrismaConnection {
constructor(readonly client: PrismaClient) {}
dispose() {
return this.client.$disconnect();
}
}
export const DatabaseFeature = () => {
const client = new PrismaClient({ adapter: new PrismaLibSql({ url: env.DATABASE_URL }) });
return feature('database', {
services: [factory(PrismaConnection, () => new PrismaConnection(client))],
});
};For PostgreSQL, use @prisma/adapter-pg in place of the libSQL adapter.
Repository
import { Prisma } from './generated/prisma/client.js';
const isUnique = (error: unknown) =>
error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002';
@Service()
export class PrismaUsers extends UsersRepository {
constructor(private readonly connection: PrismaConnection) {
super();
}
list() {
return this.connection.client.user.findMany({ orderBy: { id: 'asc' } });
}
async create(input: NewUser) {
try {
return await this.connection.client.user.create({ data: input });
} catch (error) {
if (isUnique(error)) throw new DuplicateEmail(input.email);
throw error;
}
}
}Transactions
async createMany(inputs: NewUser[]) {
return this.connection.client.$transaction(async (tx) => {
const created = [];
for (const input of inputs) created.push(await tx.user.create({ data: input }));
return created;
});
}Use tx for every query inside the callback. A query on the outer client runs outside the
transaction.
Notes
- Do not test with
:memory:. A Prisma transaction uses a second connection, and each in-memory SQLite connection is a separate, empty database. Use a temporary file. $disconnectis not final. The client reconnects on the next query. Afterapp.close(), do not keep using a client you held on to.- Migrations. Run
prisma migrate deployas a deploy step.