Skip to content

docs(rest): map exported tables to their automatic REST endpoints - #650

Open
Ethan-Arrowood wants to merge 2 commits into
mainfrom
docs/table-rest-endpoint-mapping
Open

docs(rest): map exported tables to their automatic REST endpoints#650
Ethan-Arrowood wants to merge 2 commits into
mainfrom
docs/table-rest-endpoint-mapping

Conversation

@Ethan-Arrowood

@Ethan-Arrowood Ethan-Arrowood commented Aug 27, 2026

Copy link
Copy Markdown
Member

What

Adds a canonical Tables and Their Automatic Endpoints section to reference/rest/overview.md, plus a one-sentence pointer from the @export directive in reference/database/schema.md.

Why this shape

The facts already existed but were never joined: @export is documented in the schema reference, rest: true under Configuration in the REST overview, and the per-method behavior in that same file's GET/PUT/POST/PATCH/DELETE sections. Nothing connected "I defined and exported a table" to "here is the HTTP surface I now have." That disconnect is the reported gap.

The new section is a compact endpoint table whose rows link down to the existing per-method sections rather than restating them. Restating PUT/PATCH semantics in two places on the same page would guarantee drift, and PUT/PATCH are exactly the two whose nuance matters most. Each row carries a one-line description and an anchor to the fuller section below.

It lives in rest/overview.md rather than schema.md because rest: true and all per-method nuance are already on that page. schema.md gets a pointer, not a duplicate table.

The rest: true gate

The section states plainly that both halves are required:

  • @table @export in the schema - without it there is no REST route and callers get 404.
  • rest: true in the application's config.yaml - without it the REST handler is never registered for the application, so even an exported table does not respond to HTTP.

With one carve-out, called out in an admonition: a component directory with no configuration file at all falls back to components/DEFAULT_CONFIG.ts, which sets rest: true and loads *.graphql from the component root. componentLoader.ts:602-614 selects a config file or the built-in default verbatim, with no merge, which makes the two cases fully disjoint - so a config.yaml that omits rest turns REST off even though the same directory would have had it with no file present. That is the gotcha the issue centers on, stated without over-claiming.

Verified against harper origin/main (c4dd96237)

  • rest: true gate. rest is a key in TRUSTED_RESOURCE_PLUGINS (components/componentLoader.ts:300) mapping to server/REST.ts; absent config keys are skipped by the loader. static/defaultConfig.yaml has no rest key; components/DEFAULT_CONFIG.ts:2 does set rest: true, and applies only when no config file exists (see carve-out above).
  • Method surface. server/REST.ts dispatches GET/HEAD/POST/PUT/DELETE/PATCH/OPTIONS/CONNECT/TRACE/QUERY/COPY/MOVE, delegating to the matched Resource class's same-named static and returning 405 via missingMethod() when the underlying instance method is absent. A table's working surface is GET, HEAD, PUT, POST, PATCH, DELETE, and QUERY.
  • PUT is create-or-replace, not update. Table.put -> update(..., fullUpdate = true); the fullUpdate branch in _writeUpdate replaces the stored record. Properties absent from the body are removed.
  • PUT's three exceptions to full replacement (Table.ts:2461-2487): @createdTime retains the original record's value, @updatedTime is re-stamped with the write time, and the primary key is forced to the URL {id} even when the body carries a different one. Recorded as a note rather than a table row so the "omitted properties are removed" warning keeps the lede.
  • PATCH merge is shallow. Table.patch -> update(..., fullUpdate = false), a top-level merge. Row links to the existing detailed warning.
  • POST. Responds 201 (REST.ts:319). The new key is returned in Location, and the value is the bare primary key, not a URL - Resource.ts:246 sets context.newLocation = id ?? results?.[primaryKey] and REST.ts:320 emits it verbatim. POST /Table without the trailing slash returns 404 from a purpose-built ClientError during argument normalization (Resource.ts:747); only POST /Table/{id} falls through to missingMethod -> 405.
  • QUERY works on a plain exported table. Resource.ts:347 dispatches method: 'query' to resource.search, which Table.ts:3244 implements. Exercised end-to-end over real HTTP in integrationTests/security/query-row-allowread-checkpermission.test.ts (QUERY /Vault/ -> 200), with unitTests/server/serverHelpers/uwsServer.test.js confirming the server accepts the verb.
  • Trailing slash is load-bearing. RequestTarget sets isCollection = true only for a trailing slash (or a bare query string); an exact resource-path match sets isCollection = false, id = null. Table.get then returns a describe object (table name, database, attributes, and an href to ./) for GET /Table. recordCount/estimatedRecordRange are undefined unless expensive estimates are requested, so the section does not promise a record count.
  • Collection DELETE. Table.delete treats any collection target as a search target and deletes every matching record. With no query parameters that is every record in the table. The row says so accurately without making it sound casual.

No place where the source contradicted the existing text of rest/overview.md; the page was accurate on every point checked.

Corrections made after initial review

Three problems were found in the first push and are fixed here:

  1. Removed an incorrect HEAD/OPTIONS/405 note. It claimed OPTIONS "reports the resource's supported methods." It does not. allowedMethods() (resources/Resource.ts:1003) iterates KNOWN_METHODS = get, head, put, post, delete, patch, query, move, copy and pushes any typeof resource[method] === 'function' - and REST.ts's OPTIONS case calls it with the Resource class, so it inspects statics rather than instance methods. Resource defines statics for every entry except head, so a table's Allow header over-reports MOVE and COPY (the statics exist and dispatch to instance methods a table does not define, so both 405) and omits HEAD (served, but no static). Rather than document that mismatch, the note now states only what is true: HEAD is GET minus the body (REST.ts:352), and QUERY runs a body-supplied search on the collection path.
  2. Added the no-config-file carve-out described above. The blanket "rest is not enabled by default" would have told a reader with no config.yaml that their exported tables 404 when they actually work.
  3. Corrected the POST status code. No-trailing-slash POST is 404, not 405, and the two cases have different codes for different reasons (see above).

Note for a follow-up skills PR

The hand-authored rule harper-best-practices/rules/adding-tables-with-schemas in @harperfast/skills contains a PUT-semantics error: it says PUT /{Table}/{id} "Updates an existing record." PUT is create-or-replace with upsert semantics, and properties omitted from the body are removed - the difference between an update and silent data loss. The same rule also omits that PATCH's merge is shallow, and omits the Location header on POST. That rule is out of scope here (separate repo, gated work); a follow-up skills PR will correct it, and can then source from this section.

Separately, the Allow mismatch found while checking correction 1 (MOVE/COPY advertised but 405, HEAD served but unadvertised) looks like a harper bug rather than something to document. Not filed here.

Verification

  • Rebased on main at ca6143ef.
  • npm run format:write clean, npm run format:check passes.
  • npm run build succeeds. Every anchor added was verified by grepping the generated build/reference/v5/**.html for the target id (rather than trusting the exit code, since onBrokenAnchors only warns): #export, #createdtime, #updatedtime, #configuration, #get, #post, #put, #patch, #delete, #url-structure, #openapi, #tables-and-their-automatic-endpoints, plus the four cross-page targets. The only broken anchors reported are the two known pre-existing ones (backups/overview and release-notes/v5-lincoln/5.1), owned elsewhere.

Closes #538

🤖 Generated with Claude Code

@Ethan-Arrowood
Ethan-Arrowood requested a review from a team as a code owner August 27, 2026 20:57

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the documentation to clarify the requirements for serving tables over REST, specifically noting that both the @export directive and the rest plugin must be enabled. It also introduces a comprehensive section detailing the automatic REST endpoints generated for exported tables. The feedback suggests replacing em dashes with hyphens in the newly added Markdown text to maintain consistency with the repository's style guidelines.


The optional `name` parameter specifies the URL path segment (e.g., `/my-table/`). Without `name`, the type name is used.

`@export` alone does not serve HTTP traffic — the `rest` plugin must also be enabled in the application's `config.yaml`. See [REST Overview / Tables and Their Automatic Endpoints](../rest/overview.md#tables-and-their-automatic-endpoints) for the endpoints an exported table produces and the `rest: true` requirement.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Per the repository's general rules, please use a hyphen (-) instead of an em dash () for consistency. The rule states to use hyphens even if there are pre-existing inconsistencies in the file.

Suggested change
`@export` alone does not serve HTTP traffic the `rest` plugin must also be enabled in the application's `config.yaml`. See [REST Overview / Tables and Their Automatic Endpoints](../rest/overview.md#tables-and-their-automatic-endpoints) for the endpoints an exported table produces and the `rest: true` requirement.
`@export` alone does not serve HTTP traffic - the `rest` plugin must also be enabled in the application's `config.yaml`. See [REST Overview / Tables and Their Automatic Endpoints](../rest/overview.md#tables-and-their-automatic-endpoints) for the endpoints an exported table produces and the `rest: true` requirement.
References
  1. In Markdown documentation, use hyphens ('-') instead of em dashes ('—') as field separators to adhere to the style guide, even if the file has pre-existing inconsistencies using em dashes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not taking this one. The em-dash rule is being over-generalized here.

AGENTS.md says em dashes "are fine for parenthetical asides and match existing prose; do not use them as field separators." The field-separator prohibition refers to the bullet immediately above it - Type: / Default: reference blocks, where each field goes on its own line. It is not a ban on - **term** — description in prose lists.

Two checks: the docs tree has 633 em-dash list-item lines across 63 reference files, and AGENTS.md itself uses this exact pattern - including in the rule being quoted (- **Hyphens vs. dashes** — em dashes...).

So these are the established convention, not "pre-existing inconsistencies." Applying the suggestion would leave the touched lines inconsistent with every sibling line in the same list.

sent with Claude Opus 5

Comment thread reference/rest/overview.md Outdated
rest: true
```

Neither half is sufficient on its own. Without `@export` the table has no REST route and callers get `404`. Without `rest: true` the REST handler is never registered for the application, so exported tables do not respond to HTTP requests at all — `rest` is not enabled by default.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Per the repository's general rules, please use a hyphen (-) instead of an em dash (). This change should be applied consistently throughout the new content in this file where em dashes are used (e.g., on lines 67, 72, 73, and 75).

Suggested change
Neither half is sufficient on its own. Without `@export` the table has no REST route and callers get `404`. Without `rest: true` the REST handler is never registered for the application, so exported tables do not respond to HTTP requests at all `rest` is not enabled by default.
Neither half is sufficient on its own. Without `@export` the table has no REST route and callers get `404`. Without `rest: true` the REST handler is never registered for the application, so exported tables do not respond to HTTP requests at all - `rest` is not enabled by default.
References
  1. In Markdown documentation, use hyphens ('-') instead of em dashes ('—') as field separators to adhere to the style guide, even if the file has pre-existing inconsistencies using em dashes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not taking this one. The em-dash rule is being over-generalized here.

AGENTS.md says em dashes "are fine for parenthetical asides and match existing prose; do not use them as field separators." The field-separator prohibition refers to the bullet immediately above it - Type: / Default: reference blocks, where each field goes on its own line. It is not a ban on - **term** — description in prose lists.

Two checks: the docs tree has 633 em-dash list-item lines across 63 reference files, and AGENTS.md itself uses this exact pattern - including in the rule being quoted (- **Hyphens vs. dashes** — em dashes...).

So these are the established convention, not "pre-existing inconsistencies." Applying the suggestion would leave the touched lines inconsistent with every sibling line in the same list.

sent with Claude Opus 5

@github-actions
github-actions Bot temporarily deployed to pr-650 August 27, 2026 21:01 Inactive
@github-actions

Copy link
Copy Markdown

🚀 Preview Deployment

Your preview deployment is ready!

🔗 Preview URL: https://preview.harper-documentation.harperfabric.com/pr-650

This preview will update automatically when you push new commits.

Ethan-Arrowood and others added 2 commits August 27, 2026 15:11
Adds a "Tables and Their Automatic Endpoints" section to the REST
overview that joins the two facts a reader currently has to assemble
from separate pages: a table needs `@table @export` in the schema AND
`rest: true` in the application's config.yaml before it answers any HTTP
request. `rest` is not in defaultConfig.yaml, so without it the REST
handler is never registered and exported tables 404.

The section is a compact endpoint table whose rows link down to the
existing per-method sections rather than restating them, so PUT/PATCH
semantics stay defined in exactly one place.

Also adds a one-sentence pointer from the schema reference's `@export`
directive to the new section.

Closes #538

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…T status

Three corrections to the endpoints section, each re-verified against
harper origin/main c4dd96237:

- Remove the incorrect HEAD/OPTIONS/405 note. `allowedMethods()`
  (resources/Resource.ts:1003) is called with the Resource *class* in
  server/REST.ts's OPTIONS case, so it inspects statics, not instance
  methods: it over-reports MOVE and COPY (statics exist and dispatch to
  instance methods a table does not define, so both 405) and omits HEAD
  (no static). "OPTIONS reports the supported methods" overstated it.
  Replaced with what is actually true: HEAD is GET minus the body
  (REST.ts:352), and QUERY works on the collection path, dispatching
  through `Resource.query` to the table's `search` (Resource.ts:347).

- Add a carve-out for components with no configuration file.
  static/defaultConfig.yaml has no `rest` key, but
  components/DEFAULT_CONFIG.ts sets `rest: true`, and
  componentLoader.ts:602-614 selects one or the other verbatim with no
  merge. The two cases are disjoint, so the blanket "not enabled by
  default" told a reader with no config.yaml their exported tables would
  404 when they work.

- Correct the POST row: `POST /Table` without the trailing slash returns
  404 from a purpose-built ClientError during argument normalization
  (Resource.ts:747), not 405; only `POST /Table/{id}` reaches
  missingMethod. Also note the response is 201 and that `Location`
  carries the bare primary key rather than a URL (Resource.ts:246,
  REST.ts:319-320).

Also folds in PUT's three always-applied exceptions to full replacement
(Table.ts:2461-2487) as a note rather than a table row, so the
"omitted properties are removed" warning keeps the lede.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🚀 Preview Deployment

Your preview deployment is ready!

🔗 Preview URL: https://preview.harper-documentation.harperfabric.com/pr-650

This preview will update automatically when you push new commits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Document the table → automatic REST endpoint mapping (and rest: true requirement)

2 participants