New Features
Primitive Custom Types
Custom types can now be based on a scalar type, so you can reuse a set of validation rules across fields, arrays, and procedure inputs/outputs. Contributed by @sanny-io 🙌. #1455 #2526 doc
type UserName with String {
this String @length(2, 16)
}
type Age with Int {
this Int @gte(18)
}
model User {
id String @id @default(nanoid())
name UserName
age Age? @default(18)
tags UserName[]
}Values are converted the same way as their base type (e.g. a BigInt-based custom type returns bigint).
Field-Level Update Policies on Many-to-Many Relations
@allow('update', ...) and @deny('update', ...) can now be put on implicit many-to-many relation fields to control connect / disconnect for that side of the relation. A field-level policy takes precedence over the model-level update policy. If there isn't one, the model-level policy applies as before. Contributed by @ludovicmotte 🙏. #2382 doc
model Activity {
id String @id @default(uuid())
clubs Club[] @allow('update', auth() != null)
@@allow('read', true)
}Here Activity stays read-only, but signed-in users can link and unlink it from clubs.
Typed Per-Model Entity Mutation Hooks
Entity mutation hooks can now be registered per model, so the entities they load are strongly typed for that model. Per-model hooks run alongside $all hooks, and each model can set its own runAfterMutationWithinTransaction. Contributed by @genu 🎉. #2365 doc
const db = client.$use({
id: 'audit',
onEntityMutation: {
User: {
async afterEntityMutation(args) {
// typed as User rows
const users = await args.loadAfterMutationEntities();
},
},
Post: {
runAfterMutationWithinTransaction: true,
async beforeEntityMutation(args) {
const posts = await args.loadBeforeMutationEntities();
},
},
},
});Enum References in Policy and Validation Expressions
You can now use an enum as a list of its values in policy and validation rules, which is handy for checking a String field against an enum, including enums with @map-ed values. Contributed by @sanny-io. #1211 doc
enum PostStatus {
DRAFT
ACTIVE
CANCELLED
}
model Post {
id String @id
status String
@@validate(status in PostStatus)
}Graduated from Preview
The following features are now considered stable and are no longer marked as preview:
⚠️ Breaking Changes
- Catch-all entity mutation hooks moved under
onEntityMutation.$all. Plugins that definebeforeEntityMutation/afterEntityMutation/runAfterMutationWithinTransactiondirectly underonEntityMutationno longer type-check, so TypeScript users get a compile error until the hooks are moved under$all. At runtime the old shape is still supported, with a one-time deprecation warning. If both shapes are present,$allwins.
// before
const plugin = {
id: 'audit',
onEntityMutation: {
afterEntityMutation(args) { ... },
},
};
// after
const plugin = {
id: 'audit',
onEntityMutation: {
$all: {
afterEntityMutation(args) { ... },
},
},
};Fixes and Improvements
- [orm]
updateManynow sets@updatedAtfields - by @ErikDakoda - [orm] Fixed
deleteManyon a regular model deleting nothing when it's filtered by related polymorphic rows that it cascades to.limitis now supported in that case too - [orm] Fixed polymorphic model joins in relation subqueries shadowing outer tables #2870 - by @ErikDakoda
- [orm]
$exprfilters now receive amodelAliascontext argument, needed to reference the filtered model inside relation filters #2863 - by @Natansal-weme doc - [policy] Fixed
before()comparisons on enum fields failing on PostgreSQL #2727 - by @theluckystrike - [language] Fixed incorrect field resolution scoping in vscode for
@relationattribute #2746 - by @sanny-io - [language] Improved semantic highlighting for literals, enums, operators, function/procedure arguments, and regex patterns - by @sanny-io
- Upgraded dependencies flagged by Dependabot
Thanks to @genu, @sanny-io, @ludovicmotte, @ErikDakoda, @Natansal-weme, and @theluckystrike for contributing to this release!
Full Changelog: v3.9.8...v3.10.0