Skip to content

programmatic-table-requests: Document proxy objects, async iterables, and conditions structure #18

@jcohen-hdb

Description

@jcohen-hdb

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:

{
  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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions