You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -16,37 +16,37 @@ Instructions for the agent to follow when enabling and using Harper's automatica
16
16
17
17
## When to Use
18
18
19
-
Apply this rule when adding REST or WebSocket API access to Harper tables or custom resources. Use it when configuring `config.yaml` to expose endpoints, mapping HTTP methods to resource operations, or implementing real-time WebSocket connections on a resource class.
19
+
Apply this rule when adding REST or WebSocket API access to Harper tables or custom resources. Use it when configuring `config.yaml` to expose endpoints, mapping HTTP methods to resource operations, or implementing real-time WebSocket communication with a resource.
20
20
21
21
## How It Works
22
22
23
-
1.**Enable the REST plugin**: Add `rest: true` to your application's `config.yaml`. This activates the HTTP REST interface andenables WebSocket support by default.
23
+
1.**Enable the REST plugin**: Add `rest: true` to your application's `config.yaml`. This activates the HTTP REST interface and, by default, also enables WebSocket support.
24
24
25
25
```yaml
26
26
rest: true
27
27
```
28
28
29
-
To configure optional behavior:
29
+
To configure additional options:
30
30
31
31
```yaml
32
32
rest:
33
33
lastModified: true # enables Last-Modified response header support
34
34
webSocket: false # disables automatic WebSocket support (enabled by default)
35
35
```
36
36
37
-
2. **Export your resource in the schema**: Tables are not exposed by default. Use the `@export` directive in your schema definition to make a table available as a REST endpoint. The exported name defines the base URL path, served on the application HTTP server port (default `9926`).
37
+
2. **Export your resource**: Tables are not exposed by default. Use the `@export` directive in your schema definition to make a table available as a REST endpoint. The exported name defines the base URL path, served on the application HTTP server port (default `9926`).
38
38
39
-
3. **Use the correct URL structure**: The REST interface follows a consistent path convention.
39
+
3. **Use the correct URL structure**: The REST interface follows a consistent URL pattern.
| `/my-resource/` | Trailing slash — represents the full collection; append query parameters to search |
45
-
| `/my-resource/record-id` | A specific record identified by its primary key |
45
+
| `/my-resource/record-id` | A specific record by primary key |
46
46
| `/my-resource/record-id/` | Trailing slash — collection of records with the given id prefix |
47
47
| `/my-resource/record-id/with/multiple/parts` | Record id with multiple path segments |
48
48
49
-
4. **Map HTTP methods to operations**: Each HTTP method maps to a resource method and operation.
49
+
4. **Map operations to HTTP methods**: Each HTTP method maps to a resource method.
50
50
- **GET** — Retrieve a record or search. Calls `get()`.
51
51
52
52
```
@@ -75,7 +75,7 @@ Apply this rule when adding REST or WebSocket API access to Harper tables or cus
75
75
{ "name": "some data" }
76
76
```
77
77
78
-
- **PATCH** — Partially update a record, merging only provided properties. Unspecified properties are preserved.
78
+
- **PATCH** — Partially update a record, merging only provided top-level properties. Calls the resource's patch handler. Merge is **shallow** — nested objects are replaced entirely, not deep-merged.
79
79
80
80
```
81
81
PATCH /MyTable/123
@@ -90,13 +90,7 @@ Apply this rule when adding REST or WebSocket API access to Harper tables or cus
90
90
DELETE /MyTable/?status=archived
91
91
```
92
92
93
-
5. **Access the auto-generated OpenAPI spec**: Harper generates an OpenAPI specification for all exported resources. Retrieve it at:
94
-
95
-
```
96
-
GET /openapi
97
-
```
98
-
99
-
6. **Connect via WebSocket**: When `rest` is enabled, WebSocket support is on by default. Connect to a resource URL to subscribe to change events for that resource.
93
+
5. **Connect via WebSocket**: When `rest` is enabled, WebSocket support is on by default. Connect to a resource URL to subscribe to change events for that resource.
100
94
101
95
```javascript
102
96
let ws = new WebSocket('wss://server/my-resource/341');
@@ -105,13 +99,18 @@ Apply this rule when adding REST or WebSocket API access to Harper tables or cus
105
99
};
106
100
```
107
101
108
-
Connecting to `wss://server/my-resource/341` accesses the `my-resource` resource with record id `341` and subscribes to it. When the record changes or a message is published to it, the WebSocket connection receives the update.
102
+
When the record at `my-resource/341` changes or a message is published to it, the WebSocket connection receives the update. See [real-time-apps.md](real-time-apps.md) for building real-time features on top of this.
109
103
110
-
7. **Implement a custom `connect()` handler**: Override `connect(incomingMessages)` on a resource class to control WebSocket behavior. The method must return an async iterable or generator that produces messages to send to the client.
104
+
6. **Implement a custom `connect()` handler**: Override `connect(incomingMessages)` on a resource class to control WebSocket behavior. The method must return an async iterable or generator that produces messages to send to the client.
105
+
106
+
7. **Access the OpenAPI spec**: Harper automatically generates an OpenAPI specification for all exported resources, available at:
107
+
```
108
+
GET /openapi
109
+
```
111
110
112
111
## Examples
113
112
114
-
**Simple echo server using an async generator**:
113
+
**Simple echo WebSocket server**:
115
114
116
115
```javascript
117
116
export class Echo extends Resource {
@@ -123,7 +122,7 @@ export class Echo extends Resource {
123
122
}
124
123
```
125
124
126
-
**Using the default `connect()` with event-style access and a timer**:
125
+
**Custom `connect()` using the default handler with event-style access**:
127
126
128
127
```javascript
129
128
export class Example extends Resource {
@@ -147,19 +146,16 @@ export class Example extends Resource {
147
146
}
148
147
```
149
148
150
-
**Minimal `config.yaml` enabling REST with WebSocket disabled**:
149
+
**MQTT over WebSocket**: Set the sub-protocol header as required by the MQTT specification:
151
150
152
-
```yaml
153
-
rest:
154
-
webSocket: false
151
+
```
152
+
Sec-WebSocket-Protocol: mqtt
155
153
```
156
154
157
155
## Notes
158
156
159
-
- Tables must be explicitly exported using `@export` in the schema — they are not exposed by default.
160
-
- `rest: true` is the minimal configuration to enable both REST and WebSocket support. See [real-time-apps.md](real-time-apps.md) for patterns around real-time WebSocket usage.
161
-
- For full query syntax on `GET` and `DELETE` with query parameters, see [querying-rest-apis.md](querying-rest-apis.md).
162
-
- The default `connect()` returns an iterable with a `send(message)` method and a `close` event for cleanup on disconnect.
163
-
- For MQTT over WebSockets, set the sub-protocol header `Sec-WebSocket-Protocol: mqtt`.
164
-
- In distributed environments, non-retained messages are delivered in the order received per node; retained messages (PUT/updated records) keep only the latest-timestamp version as the winning record across the cluster.
165
-
- Use the `Content-Type` request header to specify body format and the `Accept` header to request a specific response format.
157
+
- `rest: true` is the minimal config to activate both REST and WebSocket support. Set `webSocket: false` under the `rest` key to disable WebSocket only.
158
+
- PATCH merges are shallow (top-level only). Nested objects in the body replace the entire existing sub-object. To update a single nested field, either read-modify-write the parent or send the full nested object with updated values. Dot-path keys (e.g., `"settings.theme"`) are stored literally and are not interpreted as paths.
159
+
- In distributed (multi-node) environments, non-retained messages are delivered in the order received per node. Retained messages (PUT/updated records) use the latest timestamp as the winning record for eventual consistency.
160
+
- Use the `Content-Type` header for request bodies and the `Accept` header to request a specific response format.
161
+
- See [querying-rest-apis.md](querying-rest-apis.md) for the full URL query syntax, operators, and examples.
@@ -16,7 +16,7 @@ Instructions for the agent to follow when defining and querying relationships be
16
16
17
17
## When to Use
18
18
19
-
Apply this rule whenever a schema requires linking two tables via a foreign key — for example, modeling shows and networks, products and brands, or orders and items. Use it when queries need to filter or select nested related records using dot-syntax.
19
+
Apply this rule whenever you need to link two tables via a foreign key and enable join queries or nested property selection in results. Use it when modeling one-to-many, many-to-one, many-to-many, or self-referential table relationships in a Harper GraphQL schema.
20
20
21
21
## How It Works
22
22
@@ -26,7 +26,7 @@ Apply this rule whenever a schema requires linking two tables via a foreign key
5. **Select nested relationship fields with `select()`**: Relationship attributes are not included by default. Use `select()` to include them in results. When selecting without a filter on the related table, this acts as a LEFT JOIN — the relationship property is omitted if the foreign key is null or references a non-existent record.
83
+
5. **Select relationship attributes explicitly with `select()`**: Relationship fields are not included in results by default. Use `select()` to include them, optionally with nested field selection using `{}`.
85
84
86
85
```
87
86
GET /Product/?brand.name=Microsoft&select(name,brand)
88
87
GET /Product/?brand.name=Microsoft&select(name,brand{name})
89
88
GET /Product/?name=Keyboard&select(name,brand{name,id})
90
89
```
91
90
91
+
When selecting a relationship without filtering on it, Harper performs a LEFT JOIN — the relationship property is omitted if the foreign key is null or references a non-existent record.
92
+
93
+
6. **Model many-to-many without a junction table**: Use an array of foreign key values on the owning side. The array order of the foreign key values is preserved when resolving the relationship.
GET /Product/?brand.name=Microsoft&select(name,brand{name,id})
135
134
```
136
135
137
-
Querywithnestedselect:
136
+
**Many-to-many query with nested select:**
138
137
139
138
```
140
139
GET /Product/?resellers.name=Cool Shop&select(id,name,resellers{name,id})
141
140
```
142
141
143
-
**Foreign key to foreign key join** — order item joined on SKU:
144
-
145
-
```graphql
146
-
type OrderItem @table @export {
147
-
id: Long @primaryKey
148
-
orderId: Long @indexed
149
-
productSku: Long @indexed
150
-
product: Product @relationship(from: productSku, to: sku)
151
-
}
152
-
153
-
type Product @table @export {
154
-
id: Long @primaryKey
155
-
sku: Long @indexed
156
-
name: String
157
-
}
158
-
```
142
+
**Self-referential relationships** are also supported, enabling parent-child hierarchies within a single table using the same `@relationship` directive patterns above.
159
143
160
144
## Notes
161
145
162
-
- The `@relationship` directive requires the referenced attribute to be `@indexed`on the foreign key side.
163
-
-Self-referential relationships are supported, enabling parent-child hierarchies within a single table.
164
-
-The array order of foreign key values (e.g., `resellerIds`) is preserved when resolving many-to-many relationships.
165
-
-When using `select()` without a filter on the related table, the join behaves as a LEFT JOIN — missing or null foreign keys result in the relationship property being omitted rather than causing an error.
146
+
- The `from` parameter refers to an attribute on the **current** table; the `to` parameter refers to an attribute on the **target** table.
147
+
- `@relationship(to: attribute)` and `@relationship(from: attribute, to: attribute)` both require the result type to be an array (`[TargetType]`).
148
+
- Schemas can define self-referential relationships for parent-child hierarchies within a single table.
149
+
- Foreign key attributes used in `@relationship` should be marked `@indexed` for efficient join resolution.
0 commit comments