Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b
with:
node-version: lts/*
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9
- uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809
name: Load Yarn cache
with:
path: ${{ steps.yarn-cache.outputs.dir }}
Expand Down
143 changes: 140 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,141 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
dist/
yarn-error.log
coverage/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.*
!.env.example

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist
.output

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp
.cache

# Sveltekit cache directory
.svelte-kit/

# vitepress build output
**/.vitepress/dist

# vitepress cache directory
**/.vitepress/cache

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# Firebase cache directory
.firebase/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v3
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

# Vite files
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
.vite/
1 change: 1 addition & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
82 changes: 71 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ const globalClient = new PrismaClient()

export const client = globalClient.$extends(
// This is a function, don't forget to call it:
fieldEncryptionExtension()
fieldEncryptionExtension({
// schemaPath: './custom/path/to/schema.prisma' // Optional: only if schema is not in default location
})
)
```

Expand Down Expand Up @@ -406,23 +408,77 @@ _Tip: the current encryption key is already part of the decryption keys, no need
Key rotation on existing fields (decrypt with old key and re-encrypt with the
new one) is done by [data migrations](#migrations).

## Custom Prisma client location
## Migration from DMMF to AST-based schema parsing (v2.x Breaking Change)

> _Support: introduced in version 1.4.0_
Starting from version 2.0, this package has moved from DMMF-based schema analysis to AST-based schema parsing for better compatibility and performance. This is a **breaking change** that affects how you configure the extension.

### What changed

If you are generating your Prisma client to a custom location, you'll need to
tell the extension where to look for the DMMF _(the internal AST generated by Prisma that we use to read those triple-slash comments)_:
The `dmmf` parameter has been **removed** from `fieldEncryptionExtension()` and `fieldEncryptionMiddleware()`. Instead, the package now reads your schema directly from the file system using a new optional `schemaPath` parameter.

### Migration guide

**Before (v1.x):**
```ts
import { Prisma } from '../my/prisma/client'
import { Prisma } from '@prisma/client'
import { fieldEncryptionExtension } from 'prisma-field-encryption'

prismaClient.$extends(
const client = new PrismaClient().$extends(
fieldEncryptionExtension({
dmmf: Prisma.dmmf
dmmf: Prisma.dmmf, // ❌ This parameter is no longer supported
encryptionKey: 'your-key'
})
)
```

**After (v2.x):**
```ts
import { fieldEncryptionExtension } from 'prisma-field-encryption'

const client = new PrismaClient().$extends(
fieldEncryptionExtension({
// ✅ No dmmf parameter needed
encryptionKey: 'your-key'
// schemaPath: './custom/path/to/schema.prisma' // Optional: only if using custom location
})
)
```

### Default behavior

By default, the extension will look for your schema at `./prisma/schema.prisma`. You only need to specify `schemaPath` if your schema is located elsewhere.

### Custom schema locations

If your Prisma schema is in a custom location, use the `schemaPath` parameter:

```ts
fieldEncryptionExtension({
schemaPath: './my-custom-path/schema.prisma',
encryptionKey: 'your-key'
})
```

The path should be relative to your project root or an absolute path.

## Custom Prisma client location

> _Support: introduced in version 1.4.0, updated in version 2.0_

If you are generating your Prisma client to a custom location, the extension will automatically read your schema from the default location (`./prisma/schema.prisma`). If your schema is also in a custom location, you can specify it using the `schemaPath` parameter:

```ts
fieldEncryptionExtension({
schemaPath: './path/to/your/schema.prisma'
})
```

The `schemaPath` should point to your `schema.prisma` file and can be either:
- A relative path from your project root
- An absolute path to the schema file

> **Note**: Starting from version 2.0, the `dmmf` parameter is no longer supported. The extension now reads schema annotations directly from the schema file using AST parsing, which provides better performance and compatibility.

## Encryption / decryption modes

> _Support: introduced in version 1.4.0_
Expand Down Expand Up @@ -521,6 +577,8 @@ though its impact hasn't been measured yet.
> client extensions mechanism described above.
> For retro-compatibility, we're providing a middleware interface until this
> this feature is removed altogether from the Prisma client.
>
> **Starting from version 2.0**: The middleware interface also uses the new AST-based schema parsing. The `dmmf` parameter is no longer supported.

```ts
import { PrismaClient } from '@prisma/client'
Expand All @@ -530,7 +588,9 @@ export const client = new PrismaClient()

client.$use(
// This is a function, don't forget to call it:
fieldEncryptionMiddleware()
fieldEncryptionMiddleware({
// schemaPath: './custom/path/to/schema.prisma' // Optional: only if using custom location
})
)
```

Expand All @@ -540,8 +600,8 @@ _Any middleware registered after field encryption will receive encrypted data fo

## How does this work ?

The extension reads the Prisma AST (DMMF) to find annotations (only triple-slash
comments make it there) and build a list of encrypted Model.field pairs.
The extension reads your Prisma schema file directly using AST parsing to find annotations (only triple-slash
comments make it there) and build a list of encrypted Model.field pairs. Starting from version 2.0, this replaced the previous DMMF-based approach for better performance and compatibility.

When a query is received, if there's input data to encrypt (write operations),
the relevant fields are encrypted. Then the encrypted data is sent to the
Expand Down
9 changes: 3 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
"main": "dist/index.js",
"types": "dist/index.d.ts",
"license": "MIT",
"bin": {
"prisma-field-encryption": "./dist/generator/main.js"
},
"bin": "./dist/generator/main.js",
"author": {
"name": "François Best",
"email": "[email protected]",
Expand Down Expand Up @@ -46,19 +44,18 @@
},
"dependencies": {
"@47ng/cloak": "^1.2.0",
"@mrleebo/prisma-ast": "^0.13.0",
"@prisma/generator-helper": "^5.9.1",
"debug": "^4.3.4",
"immer": "^10.0.3",
"object-path": "^0.11.8",
"zod": "^3.22.4"
"object-path": "^0.11.8"
},
"peerDependencies": {
"@prisma/client": ">= 4.7"
},
"devDependencies": {
"@commitlint/config-conventional": "^17.8.1",
"@prisma/client": "5.9.1",
"@prisma/internals": "^5.9.1",
"@types/debug": "^4.1.12",
"@types/jest": "^29.5.12",
"@types/node": "^20.11.16",
Expand Down
Loading
Loading