Skip to content

Upgrading 0.17 to 0.18

0.18.0 is released. These are the ### Breaking entries of its changelog section, one at a time, with what each one asks you to change. Under 0.x the minor is the breaking position, so this is the wall between 0.17.3 and 0.18.0 — read it before you move, not after.

Every one of them is a compile error or a refused boot rather than a silent change of behaviour, except the last, which rewrites a constraint the next db push will plan for you.


It was three overloads — Postgres, Firestore, MongoDB — and a failing call produced one diagnostic on defineCollection(, listing each overload’s first failure. There is one signature now, discriminated on engine (Postgres when absent), and errors are reported on the key that is wrong, all of them at once.

What to change: nothing, in a call that already compiled. What changes is where the red squiggle lands. Two type exports moved with it: UnknownPropertyKey is now NoSuchKey (and carries a didYouMean member), and PropertyTypeNotOnThisEngine is new. Both are internal machinery of the builder, so only code that imported the old name has an edit to make.

21. defineCollection keeps checking after the first bad property

Section titled “21. defineCollection keeps checking after the first bad property”

The builder constrained its property map, and a constraint TypeScript cannot satisfy is one it silently falls back from — so a single bad defaultValue widened the inferred entity to Record<string, unknown> and switched off every check on display.title, listProperties, propertiesOrder and previewProperties.

What to change: nothing to write, but expect new errors in collection files that compiled before. They were wrong then too; the compiler had stopped looking. Fix the property the first error names and rebuild — the rest of that file is being checked for the first time.

Section titled “22. A relation’s link field has to belong to its kind”

Relation has been a closed union since 0.11 — belongsTo owns localKey, hasOne/hasMany own foreignKeyOnTarget, manyToMany owns through, via owns joinPath — but excess-property checking ran against the union as a whole, so { kind: "belongsTo", foreignKeyOnTarget: "author_id" } compiled and then resolved to a different link than the one written.

What to change: put the link field the kind owns.

author: {
type: "relation",
relation: { kind: "belongsTo", foreignKeyOnTarget: "author_id", target: () => authors }
relation: { kind: "belongsTo", localKey: "author_id", target: () => authors }
}

The boot validator already refused these, so this moves the report from a failed deploy to a compile error. If your project boots today, it compiles today.

23. A relation’s validation moves up to the property

Section titled “23. A relation’s validation moves up to the property”

RelationBase.validation and ResolvedRelationBase.validation are deleted. There were two places to write required and two generators reading different ones: the DDL asked the property (so the column came out NOT NULL) while the SDK type generator asked the relation (so the generated Insert type made the field optional), and a create() that omitted the relation typechecked and then failed at the database.

What to change: move it one level up, beside every other field’s.

author: {
type: "relation",
validation: { required: true },
relation: { kind: "belongsTo", validation: { required: true }, target: () => authors }
relation: { kind: "belongsTo", target: () => authors }
}

Boot refuses the old key by name rather than ignoring it. Code that asked a relation whether it was required asks import { isRelationRequired, relationDeclaringProperty } from "@rebasepro/common" instead.

24. A required belongsTo deletes with RESTRICT, not CASCADE

Section titled “24. A required belongsTo deletes with RESTRICT, not CASCADE”

validation: { required: true } says the child cannot exist without a parent. It does not say deleting the parent should delete the child — but that is what the generator inferred, so every DELETE FROM authors cascaded through posts, their comments, and whatever hung off those. The default is RESTRICT now: the delete fails and names the constraint. Optional relations are unchanged (SET NULL), and a manyToMany junction is unchanged (CASCADE — the row it deletes is the link).

This one is a DDL change. The next rebase db push plans a DROP CONSTRAINT / ADD CONSTRAINT for every required relation that never named an onDelete, and after it those parent deletes start failing where they used to cascade.

What to change: to keep the old behaviour, write it down.

relation: { kind: "belongsTo", target: () => authors }
relation: { kind: "belongsTo", onDelete: "cascade", target: () => authors }

Then read the plan before applying it — rebase db push prints the statements.

Every published package declared >=20 at 0.17.3 — @rebasepro/cli declared no engines at all — and every one of them declares >=22.22.0 on main. The single source is the repository’s .nvmrc.

What to change: move to Node 22.22.0 before you upgrade. pnpm install answers an engines mismatch with [WARN] Unsupported engine and installs anyway, so the failure does not arrive at install time; it arrives later, somewhere with no mention of Node in it.

RebaseServerClient dropped data from its type so that server-side code has to say whose identity a read runs under, and the property was then left on the object as a runtime alias. It is deleted at boot: rebase.data is undefined.

What to change: in server code — functions, crons, callbacks — write rebase.dataAsAdmin where you mean the admin plane and context.data where you mean the caller’s. Typed code has been getting the error since the type changed; what breaks now is untyped code, and a scaffold’s ai-instructions.md that still tells an assistant to reach for rebase.data.<slug>rebase init rewrites that file, so regenerate it or edit the rule by hand. Browser code is untouched: rebase.data on the client SDK is the user-scoped plane and stays.

27. @rebasepro/cli publishes three exports

Section titled “27. @rebasepro/cli publishes three exports”

src/index.ts re-exported sixteen modules and put 95 names on npm. It exports entry, manifest and bundle now.

What to change: nothing, unless you import from @rebasepro/cli — which is unlikely, since nothing in this repository or the control plane did. If you do, the CLI is a binary: shell out to rebase <command> rather than calling its command functions, whose signatures were never a contract.

ui, forms, firebase, plugin-insights and cms-types declared react >=19.0.0, a range whose lower half cannot satisfy app and cms at 19.2.7 — so an installer that picked 19.0.0 produced a tree that resolved cleanly and broke at render. All five say ^19.2.7. @rebasepro/app’s typescript peer moves from >=5.0.0 to ^6.0.0.

What to change: be on React 19.2.7 or later, and on TypeScript 6 if you use @rebasepro/app’s Vite plugin. If your install previously resolved React 19.0.x you will now get a peer warning until you raise it; that warning is the tree you were already running, said out loud.

29. rebase cloud webhooks create takes --endpoint

Section titled “29. rebase cloud webhooks create takes --endpoint”

--url names the control plane for every command in this family, so the second --url this one declared for the customer’s endpoint could never win: the documented example sent the webhook URL to the client as the host to authenticate against.

What to change: rebase cloud webhooks create --endpoint https://… in any script that creates one. The old spelling could not have worked, so there is no behaviour to preserve — only lines to correct.

The shared leaf encoder parses liberally, because a short code arrives off the wire, and emits strictly, because a caller handing one to the serializer built the condition by hand. serializeFilter({ a: ["gt", 5] }) throws rather than round-tripping gte.

What to change: only if you call @rebasepro/common’s serializers directly. Use the operator names the types declare (gt, gte, lt, lte, …) rather than REST short codes. Query builders and the SDK were already spelling them this way; what changes is that the wrong spelling now says so.


Next: the upgrade checklist · 0.14 → 0.17, the hop before this one · Changelog, the release notes these sections summarise.