Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prisma client extension #66

Merged
merged 17 commits into from
Jul 23, 2023
Merged

Prisma client extension #66

merged 17 commits into from
Jul 23, 2023

Conversation

franky47
Copy link
Member

@franky47 franky47 commented Jun 21, 2023

Prisma client 4.16.0 deprecates middlewares and the $use method, and favours client extensions instead.

There's a little bit of plumbing needed to use the same logic across both interfaces, and the extension could probably be refined to only handle models with encrypted fields, but this first draft seems to work.

Tasks

Closes #63.

@github-actions
Copy link

github-actions bot commented Jun 21, 2023

Pull Request Test Coverage Report for Build 5637980703

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-0.06%) to 81.782%

Totals Coverage Status
Change from base Build 5637962771: -0.06%
Covered Lines: 244
Relevant Lines: 289

💛 - Coveralls

@franky47
Copy link
Member Author

franky47 commented Jun 21, 2023

Migration from 1.4.x

While the middleware interface is still exported and functional, it is recommended you upgrade your Prisma client to 4.7.0+ and switch to the client extension mechanism.

Prisma client version

For Prisma client versions 4.7.0 to 4.15.0 (included), you'll need to enable the clientExtensions feature flag:

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["clientExtensions"]
}

For Prisma client version 4.16.0+, the client extensions mechanism is generally available without the need for a preview feature flag.

Converting the middleware to an extension

This assumes you create and export your Prisma client and attach the middleware in the same file. In v1.4.0, it would have looked like this:

import { PrismaClient } from '@prisma/client'
import { fieldEncryptionMiddleware } from 'prisma-field-encryption'

export const client = new PrismaClient()

client.$use(fieldEncryptionMiddleware())

Client extensions require exporting the result of the extended client, so the equivalent code will look like this:

import { PrismaClient } from '@prisma/client'
import { fieldEncryptionExtension } from 'prisma-field-encryption'

// Create a Prisma client
const globalClient = new PrismaClient()

// Extend with encryption support and export the resulting client
export const client = globalClient.$extends(
  fieldEncryptionExtension()
)

Migrations

When calling the generated migrate function for data migrations (to rotate encryption keys), you'll have to do an explicit cast of the client type, due to an issue with Prisma types not allowing to precisely represent extended clients in function arguments (see prisma/prisma#20326):

// Import from your generated client location, not @prisma/client
import { PrismaClient } from '.prisma/client' // or custom path
import { migrate } from './where/you/want/your/migrations'
import { fieldEncryptionExtension } from 'prisma-field-encryption'

const client = new PrismaClient().$extends(fieldEncryptionExtension())

// Explicit cast needed here ↴
await migrate(client as PrismaClient)

@PrawiraGenestonlia
Copy link

Hi @franky47.. This is an extremely useful feature.. Are there anything that I can help to get this PR merged and release?

@franky47
Copy link
Member Author

The only downside I see right now is the need for an explicit cast when calling the migration functions. Which is not an issue at all, and all other feedback is good, so it can be merged. I'll do that tonight.

franky47 added 17 commits July 23, 2023 20:23
There's no reason for the test encryption key to be random.
Instead, use 32 bytes filled with 0xff, as it looks less dramatic
in base64url than if filled with zeros.
Prisma client 4.16.0 deprecates middlewares
and the `$use` method, and favours client extensions instead.

There's a little bit of plumbing needed to use
the same logic across both interfaces, and
the extension could probably be refined to
only handle models with encrypted fields,
but this first draft seems to work.

Closes #63.
Rather than requiring 4.16.0, we can target 4.7.0
using the `clientExtensions` preview feature flag.
Note: Prisma 5 fixes a bug where empty objects in
the `orderBy` array syntax were rejected and now pass,
so updating test to reflect this behaviour.

Also renaming the @prisma/sdk to @prisma/internals
for DMMF test utilities, as recommended by the install prompt.
- Replace the need for an external env var
by repeating the test with a different client.
- Replace the prescript hook to reset the database
by a scoped beforeAll hook

Note: we still need to cast the extended client to
satisfy TypeScript with the union of both client types.
Also import the DMMF without a `require`, which
may help with the transition to ESM.

Hopefully this still works with custom client locations.
With a way to opt-in back to concurrent mode.
Concurrent updates may result in timeouts or performance degradation,
so it's better off by default.
@franky47 franky47 force-pushed the feat/prisma-client-extension branch from f7e7a12 to f4dd5d7 Compare July 23, 2023 18:25
@franky47 franky47 merged commit 3ee07b2 into next Jul 23, 2023
@franky47 franky47 deleted the feat/prisma-client-extension branch July 23, 2023 18:39
@github-actions
Copy link

🎉 This PR is included in version 1.5.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

prisma.$use() deprecated
2 participants