Guide/guides
Upgrading Guren
Use this guide for minor-to-minor upgrades.
Upgrading Guren
Use this guide for minor-to-minor upgrades.
Upgrade Workflow (Required)
- Read
CHANGELOG.mdand release notes. - Check
docs/en/guides/release-policy.mdcompatibility matrix. - Update dependencies and regenerate artifacts:
bun install
bunx guren codegen
- Check the project for deprecated API usage:
bunx guren upgrade --check-only
Each finding names the version the API was deprecated in, the version it is removed in, the replacement to move to, and the files that use it. Nothing is written to disk.
- Run validations:
bun run build
bun run typecheck
bun run test
- Apply migration notes for your source/target versions.
Migration Notes
1.x → 2.0.0
Structural mass assignment
- What changed:
static guardedandstatic strictFillableare removed.fillableis always strict; the primary key (id) is always silently stripped. OnAuthenticatableModelsubclasses, the password-hash and remember-token columns can never be mass-assigned — a request body carrying them throws aMassAssignmentException, whateverfillablesays. - Who is affected: Models declaring
guardedorstrictFillable(now flagged as errors byguren check), and code that mass-assigns a precomputed hash or remember token throughcreate()/update(). - How to migrate: Delete
guarded/strictFillabledeclarations —bunx guren upgrade --check-onlylists the affected files. If aguardedlist carried app-specific fields beyondidand the credential columns (e.g.tenantId,isAdmin), deleting the line makes them mass-assignable: declarestatic fillable = [...]without those fields to keep them protected. Where a model relied onstrictFillable = false, each new throw names a field that was being silently dropped: add it tofillableor remove it from the payload. Replacecreate({ ..., passwordHash })withcreate({ ..., password })and let the model hash it, orforceCreate({ ..., passwordHash: 'oauth:...' })for trusted server-side values — never with request input.
// Before
export class User extends defineModel(users, { base: AuthenticatableModel }) {
static fillable = ['name', 'email', 'password']
static guarded = ['id', 'passwordHash', 'rememberToken'] // now a check error
}
// After — the framework denies the credential columns itself
export class User extends defineModel(users, { base: AuthenticatableModel }) {
static fillable = ['name', 'email', 'password']
}
ModelUserProvider now reads credential column names from the model (passwordHashField / the new rememberTokenField), so a renamed column needs no matching provider option; explicit passwordColumn/rememberTokenColumn options still win. The deprecated createType option of defineModel() is removed — use optionalOnCreate/requireOnCreate.
rc → 1.0.0
Strict mass assignment
- What changed: Models that define
fillablenow throw aMassAssignmentExceptionwhencreate()/update()receives a field outside the allowlist. Previously, extra fields were silently discarded. - Who is affected: Any code that passes unfiltered objects (spread request bodies, merged defaults) to
create()/update(). - How to migrate: Pass only allowlisted fields, or use
forceCreate()/forceUpdate()for trusted server-side data such as seeders and system records.
// Before: authorId silently dropped when not in fillable
await Post.create({ ...data, authorId: user.id })
// After: either add authorId to fillable, or use forceCreate for trusted data
await Post.forceCreate({ ...validated, authorId: user.id })
Sanitized auth user records
- What changed:
auth.user()no longer contains the password column, the remember-token column, or fields the model lists inhidden. - Who is affected: Code that read those fields off the authenticated user object.
- How to migrate: Load the model explicitly (e.g.
User.findOrFail(user.id)) in the rare server-side flows that need the raw record.
SSE broadcasting
- What changed:
private-/presence-channels without a registered authorizer are now denied by default, and subscribing requires theclientIddelivered in the SSEconnectedevent. - Who is affected: Apps using the SSE broadcasting endpoints.
- How to migrate: Register authorizers with
broadcast.privateChannel()/broadcast.presenceChannel(), capture theclientIdfrom theconnectedevent, and send it inPOST /broadcasting/authto authorize and subscribe in one call. See the Broadcasting guide.
Verify the upgrade:
bun run typecheck && bun run test
Breaking Change Template (for future releases)
For each breaking item, document:
- What changed
- Why
- Who is affected
- Before/After code examples
- One-command verification