@harperfast/template-react-ts-studio 1.11.3 → 1.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/harper-best-practices/AGENTS.md +567 -490
- package/.agents/skills/harper-best-practices/rules/automatic-apis.md +46 -78
- package/.agents/skills/harper-best-practices/rules/defining-relationships.md +41 -63
- package/.agents/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +59 -45
- package/.agents/skills/harper-best-practices/rules/load-env.md +24 -23
- package/.agents/skills/harper-best-practices/rules/programmatic-table-requests.md +62 -78
- package/.agents/skills/harper-best-practices/rules/querying-rest-apis.md +92 -65
- package/.agents/skills/harper-best-practices/rules/schema-design-tooling.md +53 -55
- package/.agents/skills/harper-best-practices/rules/v5-upgrade.md +98 -62
- package/.agents/skills/harper-best-practices/rules/vector-indexing.md +113 -42
- package/.agents/skills/harper-mcp/AGENTS.md +501 -0
- package/.agents/skills/harper-mcp/SKILL.md +91 -0
- package/.agents/skills/harper-mcp/rules/automatic-verb-tools.md +50 -0
- package/.agents/skills/harper-mcp/rules/connecting-clients.md +59 -0
- package/.agents/skills/harper-mcp/rules/custom-mcp-prompts.md +52 -0
- package/.agents/skills/harper-mcp/rules/custom-mcp-resources.md +64 -0
- package/.agents/skills/harper-mcp/rules/custom-mcp-tools.md +72 -0
- package/.agents/skills/harper-mcp/rules/durable-quotas.md +73 -0
- package/.agents/skills/harper-mcp/rules/enabling-mcp.md +63 -0
- package/.agents/skills/harper-mcp/rules/rate-limiting.md +62 -0
- package/.agents/skills/harper-mcp/rules/resources-surface.md +44 -0
- package/.agents/skills/harper-mcp/rules/security-posture.md +36 -0
- package/.agents/skills/harper-mcp/rules.manifest.yaml +203 -0
- package/agent/skills/harper-best-practices/AGENTS.md +567 -490
- package/agent/skills/harper-best-practices/rules/automatic-apis.md +46 -78
- package/agent/skills/harper-best-practices/rules/defining-relationships.md +41 -63
- package/agent/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +59 -45
- package/agent/skills/harper-best-practices/rules/load-env.md +24 -23
- package/agent/skills/harper-best-practices/rules/programmatic-table-requests.md +62 -78
- package/agent/skills/harper-best-practices/rules/querying-rest-apis.md +92 -65
- package/agent/skills/harper-best-practices/rules/schema-design-tooling.md +53 -55
- package/agent/skills/harper-best-practices/rules/v5-upgrade.md +98 -62
- package/agent/skills/harper-best-practices/rules/vector-indexing.md +113 -42
- package/agent/skills/harper-mcp/AGENTS.md +501 -0
- package/agent/skills/harper-mcp/SKILL.md +83 -0
- package/agent/skills/harper-mcp/rules/automatic-verb-tools.md +50 -0
- package/agent/skills/harper-mcp/rules/connecting-clients.md +59 -0
- package/agent/skills/harper-mcp/rules/custom-mcp-prompts.md +52 -0
- package/agent/skills/harper-mcp/rules/custom-mcp-resources.md +64 -0
- package/agent/skills/harper-mcp/rules/custom-mcp-tools.md +72 -0
- package/agent/skills/harper-mcp/rules/durable-quotas.md +73 -0
- package/agent/skills/harper-mcp/rules/enabling-mcp.md +63 -0
- package/agent/skills/harper-mcp/rules/rate-limiting.md +62 -0
- package/agent/skills/harper-mcp/rules/resources-surface.md +44 -0
- package/agent/skills/harper-mcp/rules/security-posture.md +36 -0
- package/agent/skills/harper-mcp/rules.manifest.yaml +203 -0
- package/package.json +1 -1
- package/skills-lock.json +7 -1
|
@@ -7,8 +7,8 @@ metadata:
|
|
|
7
7
|
mode: generate
|
|
8
8
|
sources:
|
|
9
9
|
- reference/v5/environment-variables/overview.md
|
|
10
|
-
sourceCommit:
|
|
11
|
-
inputHash:
|
|
10
|
+
sourceCommit: 3749d0c54be457a2a65d9a63c738a5dc88989ecd
|
|
11
|
+
inputHash: b4db5ede6b93d426
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
# Load Environment Variables with loadEnv
|
|
@@ -17,18 +17,18 @@ Instructions for the agent to follow when loading environment variables from `.e
|
|
|
17
17
|
|
|
18
18
|
## When to Use
|
|
19
19
|
|
|
20
|
-
Apply this rule when a Harper application needs to
|
|
20
|
+
Apply this rule when a Harper application needs to supply secrets, API endpoints, or other configuration values to component code via `process.env` without hardcoding them. Use `loadEnv` any time you need to load one or more `.env` files at application startup.
|
|
21
21
|
|
|
22
22
|
## How It Works
|
|
23
23
|
|
|
24
|
-
1. **Declare `loadEnv` in `config.yaml`**: Add `loadEnv` as
|
|
24
|
+
1. **Declare `loadEnv` in `config.yaml`**: Add `loadEnv` as the first entry in `config.yaml`. It is built into Harper and requires no installation.
|
|
25
25
|
|
|
26
26
|
```yaml
|
|
27
27
|
loadEnv:
|
|
28
28
|
files: '.env'
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
2. **Place `loadEnv` first**:
|
|
31
|
+
2. **Place `loadEnv` first**: Harper is a single-process application. List `loadEnv` before all other components so that environment variables are available on `process.env` before dependent components start.
|
|
32
32
|
|
|
33
33
|
```yaml
|
|
34
34
|
# config.yaml — loadEnv must come first
|
|
@@ -41,9 +41,9 @@ Apply this rule when a Harper application needs to load secrets or configuration
|
|
|
41
41
|
files: './src/*.js'
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
3. **
|
|
44
|
+
3. **Access loaded values in component code**: After `loadEnv` runs, all loaded values are available on `process.env` and shared across all components.
|
|
45
45
|
|
|
46
|
-
4. **
|
|
46
|
+
4. **Control override behavior**: By default, existing environment variables take precedence over values in `.env` files. Set `override: true` to make loaded values win instead.
|
|
47
47
|
|
|
48
48
|
```yaml
|
|
49
49
|
loadEnv:
|
|
@@ -51,14 +51,17 @@ Apply this rule when a Harper application needs to load secrets or configuration
|
|
|
51
51
|
override: true
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
5. **Load multiple files
|
|
54
|
+
5. **Load multiple files**: Pass an array of paths or a glob pattern to `files`. Files are loaded in the order specified.
|
|
55
|
+
|
|
55
56
|
```yaml
|
|
56
57
|
loadEnv:
|
|
57
58
|
files:
|
|
58
59
|
- '.env'
|
|
59
60
|
- '.env.local'
|
|
60
61
|
```
|
|
61
|
-
|
|
62
|
+
|
|
63
|
+
or with a glob:
|
|
64
|
+
|
|
62
65
|
```yaml
|
|
63
66
|
loadEnv:
|
|
64
67
|
files: 'env-vars/*'
|
|
@@ -73,17 +76,23 @@ Apply this rule when a Harper application needs to load secrets or configuration
|
|
|
73
76
|
|
|
74
77
|
## Examples
|
|
75
78
|
|
|
76
|
-
**
|
|
79
|
+
**Single file, default behavior:**
|
|
77
80
|
|
|
78
81
|
```yaml
|
|
82
|
+
# config.yaml
|
|
79
83
|
loadEnv:
|
|
80
84
|
files: '.env'
|
|
85
|
+
|
|
86
|
+
rest: true
|
|
87
|
+
|
|
88
|
+
myApp:
|
|
89
|
+
files: './src/*.js'
|
|
81
90
|
```
|
|
82
91
|
|
|
83
|
-
**
|
|
92
|
+
**Multiple files with override:**
|
|
84
93
|
|
|
85
94
|
```yaml
|
|
86
|
-
# config.yaml
|
|
95
|
+
# config.yaml
|
|
87
96
|
loadEnv:
|
|
88
97
|
files:
|
|
89
98
|
- '.env'
|
|
@@ -96,16 +105,8 @@ myApp:
|
|
|
96
105
|
files: './src/*.js'
|
|
97
106
|
```
|
|
98
107
|
|
|
99
|
-
**Glob pattern:**
|
|
100
|
-
|
|
101
|
-
```yaml
|
|
102
|
-
loadEnv:
|
|
103
|
-
files: 'env-vars/*'
|
|
104
|
-
```
|
|
105
|
-
|
|
106
108
|
## Notes
|
|
107
109
|
|
|
108
|
-
- `loadEnv`
|
|
109
|
-
-
|
|
110
|
-
-
|
|
111
|
-
- `files` is the only required option; omitting it will produce an invalid configuration.
|
|
110
|
+
- `loadEnv` loads values into `process.env` for **application** code only — it does not configure Harper itself.
|
|
111
|
+
- Harper's own instance-wide configuration is composed at startup **before** any component's `loadEnv` runs. Variables such as `HARPER_CONFIG`, `HARPER_SET_CONFIG`, and `HARPER_DEFAULT_CONFIG` delivered through a `.env` file are read too late and are ignored. Set Harper configuration directly in the configuration file or export variables in the real process/container environment before Harper starts.
|
|
112
|
+
- For production credentials, prefer the encrypted secrets store over a committed `.env` file. Secrets are also delivered to components via `process.env`.
|
|
@@ -14,8 +14,8 @@ metadata:
|
|
|
14
14
|
reference/v5/resources/resource-api.md#`addTo(property: string, value:
|
|
15
15
|
number)`
|
|
16
16
|
- reference/v5/components/javascript-environment.md#Module Loading
|
|
17
|
-
sourceCommit:
|
|
18
|
-
inputHash:
|
|
17
|
+
sourceCommit: 3749d0c54be457a2a65d9a63c738a5dc88989ecd
|
|
18
|
+
inputHash: ace5c3f4a0ad4519
|
|
19
19
|
---
|
|
20
20
|
|
|
21
21
|
# Programmatic Table Requests
|
|
@@ -24,11 +24,11 @@ Instructions for the agent to interact with Harper tables programmatically using
|
|
|
24
24
|
|
|
25
25
|
## When to Use
|
|
26
26
|
|
|
27
|
-
Apply this rule when writing server-side
|
|
27
|
+
Apply this rule when writing server-side code that reads from or writes to Harper tables directly — for example, in request handlers, background jobs, or SSR rendering — without going through the REST API. Use it whenever you need to construct queries with `conditions`, `select`, `sort`, or `search(`.
|
|
28
28
|
|
|
29
29
|
## How It Works
|
|
30
30
|
|
|
31
|
-
1. **Import `tables`**:
|
|
31
|
+
1. **Import `tables`**: Pull `tables` from the `harper` package. Each property on `tables` corresponds to a table defined in `schema.graphql`.
|
|
32
32
|
|
|
33
33
|
```javascript
|
|
34
34
|
import { tables } from 'harper';
|
|
@@ -36,10 +36,9 @@ Apply this rule when writing server-side Harper code that reads from or writes t
|
|
|
36
36
|
// same as: databases.data.Product
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
2. **Define your schema**: Declare tables in `schema.graphql
|
|
39
|
+
2. **Define your schema**: Declare tables with `@table` in `schema.graphql`. Each type becomes a property on `tables`.
|
|
40
40
|
|
|
41
41
|
```graphql
|
|
42
|
-
# schema.graphql
|
|
43
42
|
type Product @table {
|
|
44
43
|
id: Long @primaryKey
|
|
45
44
|
name: String
|
|
@@ -47,57 +46,75 @@ Apply this rule when writing server-side Harper code that reads from or writes t
|
|
|
47
46
|
}
|
|
48
47
|
```
|
|
49
48
|
|
|
50
|
-
3. **Create and
|
|
49
|
+
3. **Create and modify records**: Use `create`, `patch`, and `get` for basic CRUD.
|
|
51
50
|
|
|
52
51
|
```javascript
|
|
53
|
-
// Create a new record (id auto-generated)
|
|
54
52
|
const created = await Product.create({ name: 'Shirt', price: 9.5 });
|
|
55
|
-
|
|
56
|
-
// Modify the record
|
|
57
53
|
await Product.patch(created.id, { price: Math.round(created.price * 0.8 * 100) / 100 });
|
|
58
|
-
|
|
59
|
-
// Retrieve by primary key
|
|
60
54
|
const record = await Product.get(created.id);
|
|
61
55
|
```
|
|
62
56
|
|
|
63
|
-
4. **Query with `search(
|
|
57
|
+
4. **Query with `search(`**: Pass a query object to `Product.search(query)`. It returns an async iterable.
|
|
64
58
|
|
|
65
59
|
```javascript
|
|
66
60
|
const query = {
|
|
67
61
|
conditions: [{ attribute: 'price', comparator: 'less_than', value: 8.0 }],
|
|
68
62
|
};
|
|
69
63
|
for await (const record of Product.search(query)) {
|
|
70
|
-
//
|
|
64
|
+
// process record
|
|
71
65
|
}
|
|
72
66
|
```
|
|
73
67
|
|
|
74
|
-
5. **
|
|
68
|
+
5. **Build `conditions`**: Each condition object filters records. Nest conditions with `operator` for boolean logic.
|
|
69
|
+
|
|
70
|
+
| Property | Description |
|
|
71
|
+
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
72
|
+
| `attribute` | Property name, or array for chained/joined properties (e.g. `['brand', 'name']`) |
|
|
73
|
+
| `value` | The value to match |
|
|
74
|
+
| `comparator` | `equals` (default), `greater_than`, `greater_than_equal`, `less_than`, `less_than_equal`, `starts_with`, `contains`, `ends_with`, `between`, `not_equal` |
|
|
75
|
+
| `conditions` | Nested conditions array |
|
|
76
|
+
| `operator` | `and` (default) or `or` for the nested `conditions` |
|
|
77
|
+
|
|
78
|
+
6. **Use `select`** to control which properties are returned. Accepts an array of property names, a string for a single property, or nested objects for relationships.
|
|
75
79
|
|
|
76
80
|
```javascript
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
// Array of names
|
|
82
|
+
Product.search({ select: ['name', 'price'] });
|
|
79
83
|
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
id: 42,
|
|
83
|
-
select: ['id', 'title', { name: 'author', select: ['name'] }],
|
|
84
|
-
});
|
|
84
|
+
// Nested relationship select
|
|
85
|
+
Book.get({ id: 42, select: ['id', 'title', { name: 'author', select: ['name'] }] });
|
|
85
86
|
```
|
|
86
87
|
|
|
87
|
-
|
|
88
|
+
Special `select` values:
|
|
89
|
+
- `$id` — returns the primary key regardless of its name
|
|
90
|
+
- `$updatedtime` — returns the last-updated timestamp
|
|
91
|
+
- `$distance` — returns the computed distance when querying a vector index
|
|
92
|
+
|
|
93
|
+
7. **Use `addTo`** for concurrent-safe numeric increments (CRDT incrementation, safe across threads and nodes).
|
|
88
94
|
|
|
89
95
|
```javascript
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
record.addTo('quantity', -1); // decrement safely across nodes
|
|
93
|
-
}
|
|
96
|
+
const record = await Product.update(target.id);
|
|
97
|
+
record.addTo('quantity', -1);
|
|
94
98
|
```
|
|
95
99
|
|
|
96
|
-
|
|
100
|
+
8. **Apply `sort`, `limit`, and `offset`** for ordering and pagination. A `sort` attribute must be `@indexed`, or at least one `conditions` entry must be present. Pass `allowFullScan: true` to permit an unconditional ordered scan.
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
Product.search({
|
|
104
|
+
conditions: [{ attribute: 'id', comparator: 'greater_than', value: '' }],
|
|
105
|
+
sort: { attribute: 'id' },
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
| Sort property | Description |
|
|
110
|
+
| ------------- | ---------------------------------------------------------- |
|
|
111
|
+
| `attribute` | Property name (or array for chained relationship property) |
|
|
112
|
+
| `descending` | Sort descending if `true` (default: `false`) |
|
|
113
|
+
| `next` | Secondary sort to resolve ties (same structure) |
|
|
97
114
|
|
|
98
115
|
## Examples
|
|
99
116
|
|
|
100
|
-
|
|
117
|
+
**Nested conditions with `or`:**
|
|
101
118
|
|
|
102
119
|
```javascript
|
|
103
120
|
Product.search({
|
|
@@ -114,23 +131,25 @@ Product.search({
|
|
|
114
131
|
});
|
|
115
132
|
```
|
|
116
133
|
|
|
117
|
-
|
|
134
|
+
**Chained attribute reference (relationship join):**
|
|
118
135
|
|
|
119
136
|
```javascript
|
|
120
137
|
Product.search({ conditions: [{ attribute: ['brand', 'name'], value: 'Harper' }] });
|
|
121
138
|
```
|
|
122
139
|
|
|
123
|
-
|
|
140
|
+
**Deep nested `select` across multiple relationships:**
|
|
124
141
|
|
|
125
142
|
```javascript
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
]
|
|
143
|
+
Product.search({
|
|
144
|
+
select: [
|
|
145
|
+
'id',
|
|
146
|
+
'name',
|
|
147
|
+
{ name: 'segments', select: ['id', 'name', { name: 'client', select: ['id', 'name'] }] },
|
|
148
|
+
],
|
|
149
|
+
});
|
|
131
150
|
```
|
|
132
151
|
|
|
133
|
-
|
|
152
|
+
**SSR usage — read directly from `tables` in a render function:**
|
|
134
153
|
|
|
135
154
|
```typescript
|
|
136
155
|
import { tables } from 'harper';
|
|
@@ -143,44 +162,9 @@ export async function render(url: string): Promise<string> {
|
|
|
143
162
|
|
|
144
163
|
## Notes
|
|
145
164
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
| `greater_than_equal` | Greater than or equal |
|
|
153
|
-
| `less_than` | Strictly less |
|
|
154
|
-
| `less_than_equal` | Less than or equal |
|
|
155
|
-
| `starts_with` | String prefix match |
|
|
156
|
-
| `contains` | String contains |
|
|
157
|
-
| `ends_with` | String suffix match |
|
|
158
|
-
| `between` | Range match |
|
|
159
|
-
| `not_equal` | Inequality match |
|
|
160
|
-
|
|
161
|
-
### Query object options
|
|
162
|
-
|
|
163
|
-
| Property | Description |
|
|
164
|
-
| ----------------------- | ----------------------------------------------------------------------- |
|
|
165
|
-
| `conditions` | Array of condition objects to filter records |
|
|
166
|
-
| `operator` | Top-level `and` (default) or `or` for the `conditions` array |
|
|
167
|
-
| `limit` | Maximum number of records to return |
|
|
168
|
-
| `offset` | Number of records to skip (for pagination) |
|
|
169
|
-
| `select` | Properties to include in each returned record |
|
|
170
|
-
| `sort` | Sort order object with `attribute`, `descending`, and `next` properties |
|
|
171
|
-
| `explain` | If `true`, returns conditions reordered as Harper will execute them |
|
|
172
|
-
| `enforceExecutionOrder` | If `true`, forces conditions to execute in the order supplied |
|
|
173
|
-
|
|
174
|
-
### `select` special properties
|
|
175
|
-
|
|
176
|
-
- `$id` — Returns the primary key regardless of its name
|
|
177
|
-
- `$updatedtime` — Returns the last-updated timestamp
|
|
178
|
-
- `$distance` — Returns the computed distance from the target vector when the query uses a vector index
|
|
179
|
-
|
|
180
|
-
### Relationship join behavior
|
|
181
|
-
|
|
182
|
-
- Selecting a relationship **without** filtering on it behaves as a **LEFT JOIN** — records with no related row are still returned.
|
|
183
|
-
- Adding a `conditions` entry on a related attribute (e.g. `attribute: ['author', 'name']`) behaves as an **INNER JOIN** — only records with a matching related row are returned.
|
|
184
|
-
|
|
185
|
-
- Keep `harper` external when bundling for SSR (e.g. `ssr: { external: ['harper'] }` in `vite.config`) so it resolves to the runtime instead of being bundled.
|
|
186
|
-
- `tables`, `databases`, and other Harper APIs are the same live, process-wide objects regardless of whether accessed as globals or via `import { tables } from 'harper'`.
|
|
165
|
+
- Scope destructive operations (`update`, `patch`, `delete`) with specific `conditions` and validate the affected set before writing. These operate on live data and are not easily reversible.
|
|
166
|
+
- Sorting by a bare `@primaryKey` with no conditions raises `HdbError: <attribute> is not indexed and not combined with any other conditions`. Add an open-ended condition or pass `allowFullScan: true`.
|
|
167
|
+
- Selecting a relationship field without filtering on it behaves as a **LEFT JOIN**. Adding a condition on a related attribute (e.g. `attribute: ['author', 'name']`) behaves as an **INNER JOIN**.
|
|
168
|
+
- A to-many relationship resolves to an array; `await` the property before iterating when needed.
|
|
169
|
+
- `tables` and `databases.data` reference the same live objects — a record written through one component is immediately visible to all others.
|
|
170
|
+
- Keep `harper` external when bundling for SSR (e.g. `ssr: { external: ['harper'] }` in `vite.config`) so it resolves to the runtime.
|
|
@@ -5,17 +5,17 @@ metadata:
|
|
|
5
5
|
mode: generate
|
|
6
6
|
sources:
|
|
7
7
|
- reference/v5/rest/querying.md
|
|
8
|
-
sourceCommit:
|
|
9
|
-
inputHash:
|
|
8
|
+
sourceCommit: 3749d0c54be457a2a65d9a63c738a5dc88989ecd
|
|
9
|
+
inputHash: d5ed0e937cd2d7e0
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
# Querying REST APIs
|
|
12
|
+
# Querying Harper REST APIs
|
|
13
13
|
|
|
14
|
-
Instructions for the agent to filter, sort, select, and paginate Harper
|
|
14
|
+
Instructions for the agent to filter, sort, select, and paginate records using Harper's URL-based query language on REST collection endpoints.
|
|
15
15
|
|
|
16
16
|
## When to Use
|
|
17
17
|
|
|
18
|
-
Apply this rule when building or
|
|
18
|
+
Apply this rule when building or debugging REST API calls against Harper tables that require filtering by attribute values, comparison ranges, sorting, field selection, or result pagination. This rule also covers OR logic, grouping, and querying across related tables via dot-syntax joins. See [automatic-apis.md](automatic-apis.md) for how Harper exposes tables as REST endpoints.
|
|
19
19
|
|
|
20
20
|
## How It Works
|
|
21
21
|
|
|
@@ -26,21 +26,27 @@ Apply this rule when building or modifying code that queries Harper REST endpoin
|
|
|
26
26
|
GET /Product/?category=software&inStock=true
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
2. **
|
|
29
|
+
2. **Filter for null values**: Use `=null` as the value to match null or non-null records.
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
| `=
|
|
41
|
-
| `=
|
|
42
|
-
|
|
|
43
|
-
|
|
|
31
|
+
```
|
|
32
|
+
GET /Product/?discount=null
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
3. **Apply comparison operators (FIQL syntax)**: Use FIQL operators in query parameters for range and string matching.
|
|
36
|
+
|
|
37
|
+
| Operator | Meaning |
|
|
38
|
+
| -------------------- | -------------------------------------- |
|
|
39
|
+
| `==` | Equal |
|
|
40
|
+
| `=lt=` | Less than |
|
|
41
|
+
| `=le=` | Less than or equal |
|
|
42
|
+
| `=gt=` | Greater than |
|
|
43
|
+
| `=ge=` | Greater than or equal |
|
|
44
|
+
| `=ne=`, `!=` | Not equal |
|
|
45
|
+
| `=ct=` | Contains (strings) |
|
|
46
|
+
| `=sw=`, `==<value>*` | Starts with (strings) |
|
|
47
|
+
| `=ew=` | Ends with (strings) |
|
|
48
|
+
| `=`, `===` | Strict equality (no type conversion) |
|
|
49
|
+
| `!==` | Strict inequality (no type conversion) |
|
|
44
50
|
|
|
45
51
|
```
|
|
46
52
|
GET /Product/?price=gt=100
|
|
@@ -55,32 +61,52 @@ Apply this rule when building or modifying code that queries Harper REST endpoin
|
|
|
55
61
|
GET /Product/?listDate=gt=2017-03-08T09%3A30%3A00.000Z
|
|
56
62
|
```
|
|
57
63
|
|
|
58
|
-
|
|
64
|
+
4. **Chain conditions for range queries**: Omit the attribute name on the second condition to apply it to the same attribute. Only `gt`/`ge` combined with `lt`/`le` is supported for chaining.
|
|
59
65
|
|
|
60
66
|
```
|
|
61
67
|
GET /Product/?price=gt=100<=200
|
|
62
68
|
```
|
|
63
69
|
|
|
64
|
-
|
|
70
|
+
5. **Apply type conversion**: For FIQL comparators, Harper converts values automatically. Use explicit prefixes to force a type.
|
|
71
|
+
|
|
72
|
+
| Syntax | Behavior |
|
|
73
|
+
| ----------------------------------------- | ------------------------------------------- |
|
|
74
|
+
| `name==null` | Converts to `null` |
|
|
75
|
+
| `name==123` | Converts to number if attribute is untyped |
|
|
76
|
+
| `name==true` | Converts to boolean if attribute is untyped |
|
|
77
|
+
| `name==number:123` | Explicit number conversion |
|
|
78
|
+
| `name==boolean:true` | Explicit boolean conversion |
|
|
79
|
+
| `name==string:some%20text` | Keep as string with URL decode |
|
|
80
|
+
| `name==date:2024-01-05T20%3A07%3A27.955Z` | Explicit Date conversion |
|
|
81
|
+
|
|
82
|
+
For strict operators (`=`, `===`, `!==`), no automatic type conversion is applied.
|
|
83
|
+
|
|
84
|
+
6. **Combine conditions with OR logic**: Use `|` instead of `&` to express OR between conditions.
|
|
65
85
|
|
|
66
86
|
```
|
|
67
87
|
GET /Product/?rating=5|featured=true
|
|
68
88
|
```
|
|
69
89
|
|
|
70
|
-
|
|
90
|
+
7. **Group conditions**: Use parentheses or square brackets to control evaluation order. Prefer square brackets when building queries from user input, since `[` and `]` are safely URI-encoded.
|
|
71
91
|
|
|
72
92
|
```
|
|
73
93
|
GET /Product/?rating=5|(price=gt=100&price=lt=200)
|
|
74
94
|
GET /Product/?rating=5&[tag=fast|tag=scalable|tag=efficient]
|
|
75
95
|
```
|
|
76
96
|
|
|
77
|
-
|
|
97
|
+
Build grouped queries in JavaScript:
|
|
78
98
|
|
|
79
99
|
```javascript
|
|
80
100
|
let url = `/Product/?rating=5&[${tags.map(encodeURIComponent).join('|')}]`;
|
|
81
101
|
```
|
|
82
102
|
|
|
83
|
-
|
|
103
|
+
Nest groups for complex conditions:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
GET /Product/?price=lt=100|[rating=5&[tag=fast|tag=scalable|tag=efficient]&inStock=true]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
8. **Select specific properties with `select(`**: Append `select(...)` as a query function separated by `&` to control which fields are returned.
|
|
84
110
|
|
|
85
111
|
| Syntax | Returns |
|
|
86
112
|
| -------------------------------------- | ------------------------------------------- |
|
|
@@ -95,108 +121,109 @@ Apply this rule when building or modifying code that queries Harper REST endpoin
|
|
|
95
121
|
GET /Product/?brand.name=Microsoft&select(name,brand{name})
|
|
96
122
|
```
|
|
97
123
|
|
|
98
|
-
|
|
124
|
+
9. **Paginate results with `limit(`**: Use `limit(end)` or `limit(start,end)` to restrict the number of records returned.
|
|
99
125
|
|
|
100
126
|
```
|
|
101
127
|
GET /Product/?rating=gt=3&inStock=true&select(rating,name)&limit(20)
|
|
102
128
|
GET /Product/?rating=gt=3&limit(10,30)
|
|
103
129
|
```
|
|
104
130
|
|
|
105
|
-
|
|
131
|
+
10. **Sort results with `sort(`**: Use `sort(property)` or `sort(+property,-property,...)` to order results. Prefix `+` or no prefix = ascending; `-` = descending.
|
|
106
132
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
133
|
+
```
|
|
134
|
+
GET /Product/?rating=gt=3&sort(+name)
|
|
135
|
+
GET /Product/?sort(+rating,-price)
|
|
136
|
+
```
|
|
111
137
|
|
|
112
|
-
|
|
138
|
+
11. **Query across relationships using dot-syntax**: Filter on related table attributes using dot-chained property names. Relationships must be defined in the schema with `@relationship`.
|
|
113
139
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
140
|
+
```
|
|
141
|
+
GET /Product/?brand.name=Microsoft
|
|
142
|
+
GET /Brand/?products.name=Keyboard
|
|
143
|
+
```
|
|
118
144
|
|
|
119
|
-
|
|
145
|
+
Use `select()` to include relationship attributes in the response (they are excluded by default):
|
|
120
146
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
147
|
+
```
|
|
148
|
+
GET /Product/?brand.name=Microsoft&select(name,brand)
|
|
149
|
+
GET /Product/?brand.name=Microsoft&select(name,brand{name})
|
|
150
|
+
```
|
|
124
151
|
|
|
125
|
-
|
|
152
|
+
12. **Access a specific property by URL**: Append `.propertyName` to a record ID in the URL path. Only works for properties declared in the schema.
|
|
126
153
|
```
|
|
127
154
|
GET /MyTable/123.propertyName
|
|
128
155
|
```
|
|
129
156
|
|
|
130
157
|
## Examples
|
|
131
158
|
|
|
132
|
-
**Range filter with select and limit
|
|
159
|
+
**Range filter with select and limit**:
|
|
133
160
|
|
|
134
161
|
```
|
|
135
162
|
GET /Product/?category=software&price=gt=100&price=lt=200&select(name,price)&limit(20)
|
|
136
163
|
```
|
|
137
164
|
|
|
138
|
-
**Sort
|
|
165
|
+
**Sort and paginate**:
|
|
139
166
|
|
|
140
167
|
```
|
|
141
|
-
GET /Product/?sort(+rating,-price)
|
|
168
|
+
GET /Product/?rating=gt=3&sort(+rating,-price)&limit(10,30)
|
|
142
169
|
```
|
|
143
170
|
|
|
144
|
-
**OR
|
|
171
|
+
**OR with grouping**:
|
|
145
172
|
|
|
146
173
|
```
|
|
147
174
|
GET /Product/?price=lt=100|[rating=5&[tag=fast|tag=scalable|tag=efficient]&inStock=true]
|
|
148
175
|
```
|
|
149
176
|
|
|
150
|
-
**
|
|
151
|
-
|
|
152
|
-
```
|
|
153
|
-
GET /Product/?brand.name=Microsoft&select(name,brand{name,id})
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
**Schema defining a relationship for join queries:**
|
|
177
|
+
**Join query with nested select** — schema first:
|
|
157
178
|
|
|
158
179
|
```graphql
|
|
159
180
|
type Product @table @export {
|
|
160
181
|
id: Long @primaryKey
|
|
161
182
|
name: String
|
|
162
183
|
brandId: Long @indexed
|
|
163
|
-
brand: Brand @
|
|
184
|
+
brand: Brand @relationship(from: "brandId")
|
|
164
185
|
}
|
|
165
186
|
type Brand @table @export {
|
|
166
187
|
id: Long @primaryKey
|
|
167
188
|
name: String
|
|
168
|
-
products: [Product] @
|
|
189
|
+
products: [Product] @relationship(to: "brandId")
|
|
169
190
|
}
|
|
170
191
|
```
|
|
171
192
|
|
|
172
|
-
|
|
193
|
+
Then query:
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
GET /Product/?brand.name=Microsoft&select(name,brand{name,id})
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Many-to-many relationship** — schema:
|
|
173
200
|
|
|
174
201
|
```graphql
|
|
175
202
|
type Product @table @export {
|
|
176
203
|
id: Long @primaryKey
|
|
177
204
|
name: String
|
|
178
205
|
resellerIds: [Long] @indexed
|
|
179
|
-
resellers: [Reseller] @
|
|
206
|
+
resellers: [Reseller] @relationship(from: "resellerIds")
|
|
180
207
|
}
|
|
181
208
|
```
|
|
182
209
|
|
|
210
|
+
Query:
|
|
211
|
+
|
|
183
212
|
```
|
|
184
213
|
GET /Product/?resellers.name=Cool Shop&select(id,name,resellers{name,id})
|
|
185
214
|
```
|
|
186
215
|
|
|
187
|
-
**
|
|
216
|
+
**Date range with URL-encoded colons**:
|
|
188
217
|
|
|
189
218
|
```
|
|
190
|
-
GET /Product/?
|
|
191
|
-
GET /Product/?active==boolean:true
|
|
192
|
-
GET /Product/?listDate==date:2024-01-05T20%3A07%3A27.955Z
|
|
219
|
+
GET /Product/?listDate=gt=2017-03-08T09%3A30%3A00.000Z
|
|
193
220
|
```
|
|
194
221
|
|
|
195
222
|
## Notes
|
|
196
223
|
|
|
197
|
-
-
|
|
198
|
-
-
|
|
199
|
-
-
|
|
200
|
-
-
|
|
201
|
-
-
|
|
202
|
-
-
|
|
224
|
+
- All filtered attributes must be indexed unless at least one other attribute in the same query is indexed.
|
|
225
|
+
- Null queries (`?attr=null`) require indexes created after null indexing support was added. Rebuild existing indexes (remove and re-add) to enable null queries on them.
|
|
226
|
+
- When selecting a related attribute without filtering on it, the join behaves as a LEFT JOIN — the relationship property is omitted if the foreign key is null or references a non-existent record.
|
|
227
|
+
- The array order of foreign key values (e.g., `resellerIds`) is preserved when resolving many-to-many relationships.
|
|
228
|
+
- Square brackets (`[`, `]`) are preferred over parentheses for grouping when constructing queries programmatically, because standard URI encoding safely encodes them.
|
|
229
|
+
- `directURLMapping: true` can be set on a resource to change URL path handling semantics; see your schema configuration for details.
|