HarperFast / HarperFast/skills
programmatic-table-requests: Document proxy objects, async iterables, and conditions structure
- Dominant language
- JavaScript
- Stars
- 3
- Forks
- 0
- Avg merge
- 3d 7h
- Merged PRs (30d)
- 8
Description
## Context
While building a production Harper Fabric app (~15 tables, custom resources, external API integrations), we ran into several runtime behaviors around programmatic table access that aren't documented in `programmatic-table-requests.md`. We solved them through trial and error, but they probably belong in the skill so other builders don't hit the same walls.
**Caveat:** We're not 100% certain these are canonical Harper best practices — they're what we (a developer + Claude Code) encountered and how we solved them. If Harper has better patterns for any of these, the skill should document those instead.
---
### 1. Records from `.get()` / `.search()` are proxy objects
**What we hit:** When we retrieved records and tried to spread them, serialize them, or return them in API responses, we got unexpected behavior — missing properties, circular reference errors, or empty objects.
**Why we think it happens:** Harper returns proxy-wrapped objects from its data layer (presumably for reactivity / change tracking).
**What we did:** We wrote a `toPlain()` helper that unwraps via `JSON.parse(JSON.stringify(record))` and call it on every record before using it in business logic. This is the single most common gotcha we hit — it caused subtle bugs that were hard to trace because the proxy *looks* like a normal object in console.log.
**What the skill should do:** Either document this behavior and the unwrap pattern, or document the correct way to work with proxy records if `JSON.parse(JSON.stringify())` isn't the recommended approach.
---
### 2. `search()` returns async iterables, NOT arrays
**What we hit:** We called `search()` expecting an array back and tried `.map()`, `.filter()`, `.length` — all undefined. Silent failures.
**Why we think it happens:** `search()` is designed for streaming large result sets.
**What we did:** We wrote a `toArray()` helper that collects results via `for await`. The rule's example shows `for await` iteration but doesn't explicitly warn that the result is NOT an array and that array methods won't work.
**What the skill should do:** Add a clear callout: "`search()` returns an async iterable, not an array. Use `for await` to iterate, or collect into an array first." Maybe show a `toArray()` helper pattern.
---
### 3. The `conditions` structure is never fully documented
**What we hit:** We needed to do conditional queries (e.g., find all records matching two fields). The rule mentions `conditions: [...]` but never shows the object shape.
**What we did:** Through experimentation, we determined the shape is:
```js
{
conditions: [
{ attribute: "status", comparator: "equals", value: "active" },
{ attribute: "categoryId", comparator: "equals", value: "abc123" }
]
}
```
**What the skill should do:** Document the full conditions object structure — `attribute`, `comparator`, `value` — and list available comparators (equals, greater_than, less_than, contains, starts_with, etc., whatever Harper supports). This is one of the most common operations and right now you have to guess at the API.
---
### 4. `databases.dbname.TableName` vs `tables.TableName`
**What we hit:** In our custom resource class, `tables.TableName` didn't resolve correctly when accessing tables from a named database. We had to use `databases.mydb.TableName` instead.
**Why we think it happens:** When a Harper app defines a named database (via schema), the tables live under that database namespace, not the global `tables` object.
**What we did:** We wrote a `getTable()` helper that accesses `databases.mydb.TableName` and documented the pattern. The rules consistently show `tables.X` everywhere, which may only work in certain contexts (table extensions? default database?).
**What the skill should do:** Clarify when to use `tables.X` vs `databases.dbname.X`, or if one is always preferred. If `tables.X` is supposed to work everywhere and we were doing something wrong, explain what.
Contributor guide
Assessment
This issue has not been assessed yet.