Skip to main content

Database

In our application, we use a single Prisma client instance which is then split into two contexts within a request:

  1. database (protected): This instance is enhanced with the user session using Zenstack. It provides access control based on the current user's permissions.

  2. databaseUnprotected: This is the raw Prisma client instance without any protection or enhancement.

Database Provider

The database provider is set up in app/core/database/index.ts:

import { PrismaClient } from '@prisma/client'

const singleton = globalThis as unknown as {
prisma: PrismaClient | undefined
}

if (!singleton.prisma) {
singleton.prisma = new PrismaClient({
log: ['error'],
})
}

export const Database = singleton.prisma

This setup ensures that we have a single Prisma client instance across the application.

Context Creation

The splitting of the database instance happens in the getPrisma function, typically located in app/core/authentication/server/context.tsx:

const getPrisma = (session: Awaited<ReturnType<typeof getSession>>) => {
const databaseProtected = enhance(Database, { user: session.user })

return {
database: databaseProtected,
databaseUnprotected: Database,
prisma: databaseProtected,
masterPrisma: Database,
}
}

This function applies the Zenstack enhancer to create a protected database instance based on the user's session.

Usage

When working with the database, you'll typically use the database instance for operations that should respect user permissions, and databaseUnprotected for operations that bypass permission checks.

For a deeper understanding of how user sessions affect SQL queries and how roles and permissions are managed, please refer to the Roles and Permissions section. Zenstack manages these permissions to provide a simple interface for access control in your database operations.