-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Cannot type extended client in function arguments #20326
Description
Bug description
Some context
I maintain a middleware that provides field-level encryption, which I'm porting to the client extension mechanism (PR 47ng/prisma-field-encryption#66).
It can also be used as a generator to generate data migration files to rotate encryption keys. Those generated files look like the following: a function that takes a Prisma client as an argument to perform the work.
import { PrismaClient } from '.location/to/generated/prisma/client/to/allow/custom/locations'
export async function migrate(client: PrismaClient) {
// generated migration code
}The reason why it's accepting a client rather than creating one itself is because of configuration of the middleware/extension, and to potentially include other middleware and extensions. The idea is to make the data migration process as transparent as possible: reading & updating records in an iterative manner, just as it would be done in application code.
The problem
The issue I encounter is with defining the type of client and connecting it to extended client type definitions. I'd like to maintain both a middleware and extension API for this library, for retrocompatibility, until Prisma drops middleware support entirely.
In integration tests, it seems like the only way to have a client type be either middleware-based or extension-based is to explicitly cast the extended client as PrismaClient, see 47ng/prisma-field-encryption#63 (comment).
There are also some discrepancies between using the PrismaClient type imported from @prisma/client vs .custom/client/location. The former doesn't seem to support interactive transaction types (the client argument of the transaction callback is typed as any), but the latter seems to fit the bill for all model operations, which is what we're using to allow supporting custom client locations anyway.
That being said, this PrismaClient type is not type-compatible with extended clients. Runtime compatibility is fine, all model operations work if using directives like explicit type casts or // @ts-ignore.
TL;DR: There doesn't seem to be a way at the moment to type extended clients as arguments of standalone functions.
cc @millsp
How to reproduce
Reproduction repository: https://github.com/franky47/prisma-field-encryption-sandbox
Steps to reproduce:
- Clone the repository
- Install dependencies with
yarn install - Generate migration files with
prisma generate - Open the
migrate.tsfile, and remove theas PrismaClienttype cast - Observe an error
Expected behavior
There should be a type definition for extended clients that allows them to be passed to external functions.
While the extended API can include anything added by extensions, having at least the ability to get a working type for the basic functionality (model operations, but not allowing $use or $on) would already be a great start. Those types could then be augmented by users if need be at specific call site locations with added methods from extensions.
Prisma information
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
output = "../prisma-client" // needs to support custom client locations
}
generator migrations {
provider = "prisma-field-encryption"
output = "./data-migrations"
}
model Post {
id Int @id @default(autoincrement())
title String
content String? /// @encrypted <- annotate fields to encrypt (must be a String)
published Boolean @default(false)
author User? @relation(fields: [authorId], references: [id], onDelete: Cascade, onUpdate: Cascade)
authorId Int?
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String? /// @encrypted
posts Post[]
}import { fieldEncryptionExtension } from 'prisma-field-encryption'
import { PrismaClient } from './prisma-client'
import { migrate } from './prisma/data-migrations'
async function main() {
const prisma = new PrismaClient().$extends(fieldEncryptionExtension())
await migrate(prisma)
}
main()Environment & setup
- OS: macOS 12.6.7
- Database: SQLite (but irrelevant to the issue)
- Node.js version: 18.12.0
Prisma Version
5.0.0