Migration to 2.0.0
This guide covers all breaking changes when upgrading from pinia-orm 1.x to 2.0.0.
Requirements
- pinia >= 3
- TypeScript >= 5.2 with standard decorator semantics (see below)
- A build toolchain that transforms standard ECMAScript decorators (esbuild >= 0.21, current Vite/Nuxt versions)
Standard ECMAScript decorators
The property decorators are now standard TC39 decorators instead of the legacy experimentalDecorators implementation.
1. Update your tsconfig
Remove the legacy flags — TypeScript uses standard decorators by default:
{
"compilerOptions": {
- "experimentalDecorators": true,
- "useDefineForClassFields": false,
}
}Also remove any experimentalDecorators entries from vite.esbuild.tsconfigRaw in your Nuxt/Vite config.
2. Migrate your models
Standard decorators cannot be applied to declare fields. Replace declare with the definite assignment assertion (!) on decorated fields:
class User extends Model {
static entity = 'users'
- @Uid() declare id: string
- @Str('') declare name: string
+ @Uid() id!: string
+ @Str('') name!: string
@HasMany(() => Post, 'userId')
- declare posts: Post[]
+ posts!: Post[]
}Rules of thumb:
- decorated field →
field!: Type(or keepfield?: Typefor optional relations) - undecorated typing for
fields()based models → keep (or change to)declare field: Type - a subclass overriding a decorated field of its base class needs an initializer:
@Attr('dog') type = 'dog' @NonEnumerablenow requires theaccessorkeyword:@NonEnumerable accessor pivot!: Pivot
Automated migration
A codemod that applies the model changes automatically ships with the repository:
curl -O https://raw.githubusercontent.com/CodeDredd/pinia-orm/main/scripts/migrate-standard-decorators.mjs
node migrate-standard-decorators.mjs --dry ./src # preview
node migrate-standard-decorators.mjs ./src # applyIt only touches declare fields that are decorated, so fields() based models stay untouched.
Bonus: decorator inheritance
Decorated fields are now registered through decorator metadata, which is inherited along the class hierarchy. Derived STI models see the decorated fields of their base classes without re-declaring them — this did not work in 1.x.
fresh() fires lifecycle hooks
fresh() now fires the saving/creating and saved/created hooks for every record. Pass { raw: true } as second argument to restore the old silent behaviour:
useRepo(User).fresh(records, { raw: true })Casts run before validation on save
When saving, casts are applied before the field type check. If a field has both a set-mutator and a cast, the mutator now receives the casted value.
Decorator state is scoped per entity
Mutators, casts and hidden fields registered via decorators or setMutator()/setCast()/setHidden() no longer leak to other entities with same-named fields.