From a6ca04874990761d7f56a378398038cd44da8a58 Mon Sep 17 00:00:00 2001 From: Kris Zyp Date: Tue, 4 Aug 2026 09:31:23 -0600 Subject: [PATCH 1/4] docs: mark static loadAsInstance as deprecated in Resource API reference The flag was dropped from the v5 docs without explanation, so AI coding agents copying older example code (e.g. engineering-metrics/DevLogin.ts) reintroduce it as if still required. Document that the v4 loadAsInstance=false behavior is unconditional in v5 and the flag is now a no-op. Co-Authored-By: Claude Sonnet 5 --- reference/resources/resource-api.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/reference/resources/resource-api.md b/reference/resources/resource-api.md index ffc836e1..be684b30 100644 --- a/reference/resources/resource-api.md +++ b/reference/resources/resource-api.md @@ -25,6 +25,14 @@ Resource classes have static methods that directly map to RESTful methods or HTT Static methods are defined on a Resource class and are the preferred way to interact with tables and resources from application code. They handle transaction setup, access checks, and request parsing automatically. These methods also map to RESTful HTTP verbs and can be overridden to define custom behavior for requests. +### `static loadAsInstance?: boolean` (deprecated) + +> **Deprecated — do not set this in v5.** In v4, `static loadAsInstance = false` opted a resource's overridden static methods (`get`, `put`, `patch`, `post`, `delete`) into the "endpoint" behavior documented throughout this section: `target` as the first argument, `data` as a promise, and `getContext()` called as a top-level function rather than `this.getContext()`. **In v5, that behavior is unconditional** — static methods always work this way regardless of whether `loadAsInstance` is set to `true`, `false`, or omitted. Adding `static loadAsInstance = false;` to a new v5 resource has no effect; it's a no-op left over from v4. +> +> If you find this flag in an existing resource, or an AI coding agent adds it (older example code, including some still circulating internally, uses this pattern), it's safe to delete. If the surrounding method also uses instance-style access — `this.getId()`, `this.getContext()` — inside a `static` method, convert those to the v5 equivalents: read from the `target` parameter, and call the top-level `getContext()` (imported from `harper`) instead of `this.getContext()`. Reserve actual instance methods for record mutations reached via `update()` — see [Resource Instance Methods](#resource-instance-methods) below. + +--- + ### `get(target: RequestTarget | Id | Query, context?: Resource | Context): Promise | ExtendedIterable` Retrieves a record by primary key, or queries for records when given a `Query` object or a collection `RequestTarget`. From 641db8a6e7e1095f2dbd4b570f41106fcf06163e Mon Sep 17 00:00:00 2001 From: Kris Zyp Date: Tue, 4 Aug 2026 09:38:31 -0600 Subject: [PATCH 2/4] Apply suggestion from @gemini-code-assist[bot] Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- reference/resources/resource-api.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/reference/resources/resource-api.md b/reference/resources/resource-api.md index be684b30..53e4cc87 100644 --- a/reference/resources/resource-api.md +++ b/reference/resources/resource-api.md @@ -27,6 +27,8 @@ Static methods are defined on a Resource class and are the preferred way to inte ### `static loadAsInstance?: boolean` (deprecated) + + > **Deprecated — do not set this in v5.** In v4, `static loadAsInstance = false` opted a resource's overridden static methods (`get`, `put`, `patch`, `post`, `delete`) into the "endpoint" behavior documented throughout this section: `target` as the first argument, `data` as a promise, and `getContext()` called as a top-level function rather than `this.getContext()`. **In v5, that behavior is unconditional** — static methods always work this way regardless of whether `loadAsInstance` is set to `true`, `false`, or omitted. Adding `static loadAsInstance = false;` to a new v5 resource has no effect; it's a no-op left over from v4. > > If you find this flag in an existing resource, or an AI coding agent adds it (older example code, including some still circulating internally, uses this pattern), it's safe to delete. If the surrounding method also uses instance-style access — `this.getId()`, `this.getContext()` — inside a `static` method, convert those to the v5 equivalents: read from the `target` parameter, and call the top-level `getContext()` (imported from `harper`) instead of `this.getContext()`. Reserve actual instance methods for record mutations reached via `update()` — see [Resource Instance Methods](#resource-instance-methods) below. From 2ff8ff07e5e386ba12e7022ea60418de7b0e524a Mon Sep 17 00:00:00 2001 From: Kris Zyp Date: Tue, 4 Aug 2026 10:52:27 -0600 Subject: [PATCH 3/4] =?UTF-8?q?docs:=20correct=20loadAsInstance=20mechanis?= =?UTF-8?q?m=20=E2=80=94=20it=20governs=20instance,=20not=20static,=20meth?= =?UTF-8?q?ods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous revision wrongly claimed the flag affected overridden static methods and was a v5 no-op that was safe to delete. Verified against resources/Resource.ts and resources/Table.ts: the flag is only read by Harper's built-in static dispatch when it calls a resource's *instance* methods, controlling arg order ((data, target) vs (target, data)), record pre-loading, get(string) semantics, and record freezing. An overridden static replaces that dispatch, so the flag never applies to it — which is why static methods are the recommended shape. The behavior is still live in v5, so the line cannot be removed unless the instance methods are converted too. Co-Authored-By: Claude Sonnet 5 --- reference/resources/resource-api.md | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/reference/resources/resource-api.md b/reference/resources/resource-api.md index 53e4cc87..a60080f6 100644 --- a/reference/resources/resource-api.md +++ b/reference/resources/resource-api.md @@ -25,13 +25,24 @@ Resource classes have static methods that directly map to RESTful methods or HTT Static methods are defined on a Resource class and are the preferred way to interact with tables and resources from application code. They handle transaction setup, access checks, and request parsing automatically. These methods also map to RESTful HTTP verbs and can be overridden to define custom behavior for requests. -### `static loadAsInstance?: boolean` (deprecated) +### `static loadAsInstance?: boolean` - - -> **Deprecated — do not set this in v5.** In v4, `static loadAsInstance = false` opted a resource's overridden static methods (`get`, `put`, `patch`, `post`, `delete`) into the "endpoint" behavior documented throughout this section: `target` as the first argument, `data` as a promise, and `getContext()` called as a top-level function rather than `this.getContext()`. **In v5, that behavior is unconditional** — static methods always work this way regardless of whether `loadAsInstance` is set to `true`, `false`, or omitted. Adding `static loadAsInstance = false;` to a new v5 resource has no effect; it's a no-op left over from v4. +> **You do not need this flag when you override static methods — which is the recommended approach.** `loadAsInstance` is a legacy compatibility flag that only affects how Harper's **built-in** dispatch calls a resource's **instance** methods. It does not change the signature or behavior of a static method you define yourself. +> +> When you override a static verb (`static get`, `static post`, …), your method _replaces_ the built-in dispatch — including the code that reads `loadAsInstance`. Your static method always receives `(target, data)`, so the flag is irrelevant to it. This is the main reason static methods are the recommended shape for custom endpoints: they sidestep the flag entirely. +> +> Where the flag still matters is the legacy pattern of defining REST-mirroring **instance** methods (instance `get`, `put`, `patch`, `post`, `delete`, `publish`, `search`). Harper's built-in static dispatch instantiates the resource and calls those instance methods, and `loadAsInstance` decides how: > -> If you find this flag in an existing resource, or an AI coding agent adds it (older example code, including some still circulating internally, uses this pattern), it's safe to delete. If the surrounding method also uses instance-style access — `this.getId()`, `this.getContext()` — inside a `static` method, convert those to the v5 equivalents: read from the `target` parameter, and call the top-level `getContext()` (imported from `harper`) instead of `this.getContext()`. Reserve actual instance methods for record mutations reached via `update()` — see [Resource Instance Methods](#resource-instance-methods) below. +> | | `loadAsInstance` unset (default) | `loadAsInstance = false` | +> | ---------------------------- | -------------------------------------------------------------- | ------------------------------- | +> | Instance verb arguments | `(data, target)` | `(target, data)` | +> | Record pre-loading | instance is pre-loaded with the record before your method runs | not pre-loaded; read explicitly | +> | Instance `get('someString')` | treated as `getProperty('someString')` | treated as an id/target read | +> | Records passed to your code | mutable | frozen | +> +> **This is still live behavior in v5, not a no-op — so removing the line is not automatically safe.** Deleting `static loadAsInstance = false;` from a resource that still defines REST-mirroring instance methods silently flips the argument order and re-enables record pre-loading, which will break those methods. Remove it only as part of converting those instance methods to static methods. +> +> For new code, prefer static methods and skip the flag. Reserve instance methods for record mutation via `update()` and for `validate()` — see [Resource Instance Methods](#resource-instance-methods). --- @@ -863,6 +874,8 @@ The following instances are also implemented on Resource instances for [backward - `create` - `subscribe` +How Harper's built-in dispatch passes arguments to these instance methods, and whether the record is pre-loaded first, depends on [`static loadAsInstance`](#static-loadasinstance-boolean). Overriding the equivalent static method avoids that dependency entirely and is preferred for new code. + ## Concurrency and Safe Concurrent Writes When multiple writers may touch the same record concurrently, only atomic deltas are safe. Harper resolves deltas at commit time, so the committed result is exact regardless of how the writers interleave: From 5c8db7869c987c030cecbb56dae5788e594bfbdd Mon Sep 17 00:00:00 2001 From: Kris Zyp Date: Tue, 4 Aug 2026 11:09:10 -0600 Subject: [PATCH 4/4] docs: defer loadAsInstance mode details to the v4 reference Drop the per-behavior comparison table. The "records mutable vs frozen" row conflated two things: records are frozen regardless of the flag; what is mutable is the Resource instance the instance verbs are called on. Rather than restate mode internals in the v5 reference, link to the v4 doc's API Versions section, which already documents both modes in full. Keeps the v5 page focused on the actionable guidance: statics don't need the flag, and the line can't be deleted while REST-mirroring instance verbs remain. Co-Authored-By: Claude Sonnet 5 --- reference/resources/resource-api.md | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/reference/resources/resource-api.md b/reference/resources/resource-api.md index a60080f6..9de39d36 100644 --- a/reference/resources/resource-api.md +++ b/reference/resources/resource-api.md @@ -27,22 +27,15 @@ Static methods are defined on a Resource class and are the preferred way to inte ### `static loadAsInstance?: boolean` -> **You do not need this flag when you override static methods — which is the recommended approach.** `loadAsInstance` is a legacy compatibility flag that only affects how Harper's **built-in** dispatch calls a resource's **instance** methods. It does not change the signature or behavior of a static method you define yourself. +> **New v5 code does not need this flag.** It is a v4-era compatibility flag that selects between two behavioral modes for a resource's **instance** methods. It has no effect on a static method you define yourself. > -> When you override a static verb (`static get`, `static post`, …), your method _replaces_ the built-in dispatch — including the code that reads `loadAsInstance`. Your static method always receives `(target, data)`, so the flag is irrelevant to it. This is the main reason static methods are the recommended shape for custom endpoints: they sidestep the flag entirely. +> When you override a static verb (`static get`, `static post`, …), your method _replaces_ Harper's built-in dispatch — including the code that reads `loadAsInstance`. Your static method always receives `(target, data)`, so the flag is irrelevant to it. This is a large part of why static methods are the recommended shape for custom endpoints: they sidestep the flag entirely. > -> Where the flag still matters is the legacy pattern of defining REST-mirroring **instance** methods (instance `get`, `put`, `patch`, `post`, `delete`, `publish`, `search`). Harper's built-in static dispatch instantiates the resource and calls those instance methods, and `loadAsInstance` decides how: +> The flag does still apply in v5 to the legacy pattern of defining REST-mirroring **instance** verbs (instance `get`, `put`, `patch`, `post`, `delete`, `publish`, `search`), where it selects the argument order those methods receive and whether the record is preloaded onto the instance. Because that behavior is live rather than inert, **`static loadAsInstance = false;` cannot simply be deleted** from an existing resource — dropping the line reverses the argument order for those instance methods and will break them. Remove it only as part of converting them to static methods. > -> | | `loadAsInstance` unset (default) | `loadAsInstance = false` | -> | ---------------------------- | -------------------------------------------------------------- | ------------------------------- | -> | Instance verb arguments | `(data, target)` | `(target, data)` | -> | Record pre-loading | instance is pre-loaded with the record before your method runs | not pre-loaded; read explicitly | -> | Instance `get('someString')` | treated as `getProperty('someString')` | treated as an id/target read | -> | Records passed to your code | mutable | frozen | +> The two modes are documented in full in the v4 reference — see [API Versions](../../reference_versioned_docs/version-v4/resources/resource-api.md#api-versions). > -> **This is still live behavior in v5, not a no-op — so removing the line is not automatically safe.** Deleting `static loadAsInstance = false;` from a resource that still defines REST-mirroring instance methods silently flips the argument order and re-enables record pre-loading, which will break those methods. Remove it only as part of converting those instance methods to static methods. -> -> For new code, prefer static methods and skip the flag. Reserve instance methods for record mutation via `update()` and for `validate()` — see [Resource Instance Methods](#resource-instance-methods). +> For new code, prefer static methods and omit the flag. Reserve instance methods for record mutation via `update()` and for `validate()` — see [Resource Instance Methods](#resource-instance-methods). --- @@ -874,7 +867,7 @@ The following instances are also implemented on Resource instances for [backward - `create` - `subscribe` -How Harper's built-in dispatch passes arguments to these instance methods, and whether the record is pre-loaded first, depends on [`static loadAsInstance`](#static-loadasinstance-boolean). Overriding the equivalent static method avoids that dependency entirely and is preferred for new code. +The arguments these instance methods receive depend on [`static loadAsInstance`](#static-loadasinstance-boolean). Overriding the equivalent static method avoids that dependency entirely and is preferred for new code. ## Concurrency and Safe Concurrent Writes