Stored queries
Fallen-8 has no query language — queries are C# delegates (delegates). A stored query is the precompiled flavor of that: a named, compile-validated definition — either a path filter/cost set or a subgraph pattern template — registered once and afterwards invoked by name from the path and subgraph endpoints, with nothing compiled per request. Each namespace has its own library (namespaces); like every namespace-scoped route, /storedquery also answers under /ns/{ns}/….
flowchart LR
R["POST /storedquery<br/>compile + validate once"] --> L[("library<br/>per namespace, WAL-durable")]
L --> P["POST /path/{from}/to/{to}<br/>{ "storedQuery": "…" }"]
L --> S["PUT /subgraph<br/>{ "storedQuery": "…" }"]
REST surface
Section titled “REST surface”| Route | Effect | Notable responses |
|---|---|---|
POST /storedquery |
Register: validate, compile once, publish | 201 summary · 400 malformed or compile failure (body carries compiler diagnostics) · 401 no credential (when a key is configured) · 409 duplicate name or library quota · 413 body over 1 MiB · 429 rate-limited |
GET /storedquery |
List summaries, ordered by name | 200 |
GET /storedquery/{name} |
Full detail incl. the stored specification JSON (also your migration path between instances) and, for Failed entries, recompile diagnostics |
200 · 404 |
DELETE /storedquery/{name} |
Deregister | 204 · 404 |
Entries are immutable: to change one, delete and re-register.
Registration model
Section titled “Registration model”| Field | Required | Notes |
|---|---|---|
name |
yes | ^[A-Za-z0-9_-]{1,128}$, case-sensitive, unique per namespace |
kind |
yes | Exactly Path or SubGraph |
description |
no | Free text, shown in list/detail |
path |
iff kind = Path |
The filter / cost blocks of a path request |
subGraph |
iff kind = SubGraph |
The vertexFilter / edgeFilter / patterns of a subgraph request |
Exactly one of path / subGraph must be present and must match kind. The fragments inside are the same C# fragments the inline endpoints accept, compiled with the same bounds. Deliberately not stored — these stay per invocation:
- Path:
pathAlgorithmName,maxDepth,maxResults,maxPathWeight,semantic - SubGraph: the instance
name,additionalInformation
List/detail responses report a compileState: Compiled (invocable), Failed (recompile on load failed — invoking returns 409, diagnostics on the detail; delete and re-register), or SourceOnly (loaded without a compiler, e.g. the embedded engine).
Invocation
Section titled “Invocation”Reference the entry with "storedQuery": "<name>" instead of inline fragments — mutually exclusive with them (400 when mixed):
POST /path/{from}/to/{to}accepts a stored query of kindPath; bounds and algorithm come from the request. Thesemanticblock still works — stored fragments read the query vector via theircontextparameter (semantic traversal).PUT /subgraphaccepts a stored query of kindSubGraphand instantiates the template under the request’sname. The created subgraph is self-contained — deleting the stored query later does not affect it.semanticis not available on a stored-template invocation (400); inline the filters instead.
Invocation errors: 404 unknown name, 400 wrong kind, 409 not invocable (Failed/SourceOnly compile state).
Worked example: register once, invoke by name
Section titled “Worked example: register once, invoke by name”Register a path query that only traverses vertices with age > 30:
curl -X POST http://localhost:8080/storedquery \ -H "Content-Type: application/json" \ -d '{ "name": "adults-shortest", "kind": "Path", "description": "age>30 vertices, weight-by-distance", "path": { "filter": { "vertexFilter": "return (v) => v.TryGetProperty(out int age, \"age\") && age > 30;" }, "cost": { "edgeCost": "return (e) => 1.0;" } } }'$spec = @{ name = "adults-shortest" kind = "Path" description = "age>30 vertices, weight-by-distance" path = @{ filter = @{ vertexFilter = 'return (v) => v.TryGetProperty(out int age, "age") && age > 30;' } cost = @{ edgeCost = 'return (e) => 1.0;' } }} | ConvertTo-Json -Depth 5Invoke-RestMethod -Method Post -Uri http://localhost:8080/storedquery -ContentType "application/json" -Body $specInvoke it — the request carries no code, only the name and per-invocation bounds:
curl -X POST http://localhost:8080/path/1/to/5 \ -H "Content-Type: application/json" \ -d '{ "storedQuery": "adults-shortest", "maxDepth": 5 }'Invoke-RestMethod -Method Post -Uri http://localhost:8080/path/1/to/5 -ContentType "application/json" ` -Body '{ "storedQuery": "adults-shortest", "maxDepth": 5 }'A subgraph invocation looks the same, with the per-instance name alongside the reference:
PUT /subgraph{ "name": "adults-2026", "storedQuery": "person-net" }What the library buys you
Section titled “What the library buys you”- Compile once, invoke many. A fragment is validated and compiled a single time at registration; every invocation reuses the pinned artifact, so a hot query pays no per-request Roslyn cost and callers send only a name plus bounds.
- A curated catalog. An operator registers a vetted set of named queries that agents and clients reference by name instead of re-sending raw C#, and
GET /storedquerylists them for discovery. - Portable definitions.
GET /storedquery/{name}returns the stored specification JSON — your migration path between instances — and entries survive save/load via the WAL.
Registration carries the same authentication as the inline code endpoints (the API key when one is configured, 401 without it); invocation by name is never gated, since it introduces no new code — the path and subgraph endpoints simply check whether a request carries inline fragments, and a stored-query reference carries none.
Stated honestly: dynamic code execution is always on (there is no switch to lock the engine down to stored-queries-only), and an invoked stored query still runs in-process with full trust. The library is a reuse and curation convenience, not a sandbox. The API key is documented in security.
Durability and limits
Section titled “Durability and limits”Registrations and removals are transactions: they survive PUT /save / load and crash recovery via the write-ahead log, with sources recompiled on load (save games).
| Limit | Value |
|---|---|
| Library size | 256 per namespace (Fallen8:StoredQueries:MaxCount); exceeding it is a 409 |
| Name length | 128 characters |
| Registration body | 1 MiB; registration is rate-limited like the other code endpoints |
See also
Section titled “See also”- Delegates — the no-query-language philosophy, fragment shape, compilation,
/delegates/validate - Security — the API key that gates access to the code endpoints
- Path finding · Subgraphs — the endpoints that accept
storedQuery - Semantic traversal — the
semanticblock stored path queries can combine with - Save games — checkpoints and the write-ahead log
- Namespaces — per-namespace isolation and the
/ns/{ns}/…route twins - REST API — OpenAPI document and Scalar reference