fraiseql-wire 2.0.0

Streaming JSON query engine for Postgres 17
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
# fraiseql-wire

[![Build Status](https://github.com/fraiseql/fraiseql-wire/workflows/CI/badge.svg?branch=main)](https://github.com/fraiseql/fraiseql-wire/actions/workflows/ci.yml)
[![Code Coverage](https://codecov.io/gh/fraiseql/fraiseql-wire/branch/main/graph/badge.svg)](https://codecov.io/gh/fraiseql/fraiseql-wire)
[![Crates.io Version](https://img.shields.io/crates/v/fraiseql-wire.svg)](https://crates.io/crates/fraiseql-wire)
[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](https://github.com/fraiseql/fraiseql-wire#license)
[![MSRV: 1.75+](https://img.shields.io/badge/MSRV-1.75%2B-blue)](https://github.com/fraiseql/fraiseql-wire)
[![Documentation](https://docs.rs/fraiseql-wire/badge.svg)](https://docs.rs/fraiseql-wire)

**Streaming JSON queries for Postgres 17, built for FraiseQL**

`fraiseql-wire` is a **minimal, async Rust query engine** that streams JSON data from Postgres with low latency and bounded memory usage.

It is **not a general-purpose Postgres driver**.
It is a focused, purpose-built transport for JSON queries of the form:

```sql
SELECT data
FROM {source}
[WHERE predicate]
[ORDER BY expression [COLLATE collation] [ASC|DESC]]
[LIMIT N] [OFFSET M]
```

Where `{source}` is a JSON-shaped relation (`v_{entity}` views or `tv_{entity}` tables).

The primary goal is to enable **efficient, backpressure-aware streaming of JSON** from Postgres into Rust, with support for hybrid filtering (SQL + Rust predicates), adaptive chunking, pause/resume flow control, and comprehensive metrics.

---

## Why fraiseql-wire?

Traditional database drivers are optimized for flexibility and completeness. FraiseQL-Wire is optimized for:

* πŸš€ **Low latency** (process rows as soon as they arrive)
* 🧠 **Low memory usage** (no full result buffering)
* πŸ” **Streaming-first APIs** (`Stream<Item = Result<Value, _>>`)
* 🧩 **Hybrid filtering** (SQL + Rust predicates)
* πŸ” **JSON-native workloads**

If your application primarily:

* Reads JSON (`json` / `jsonb`)
* Uses views as an abstraction layer
* Needs to process large result sets incrementally

…then `fraiseql-wire` is a good fit.

---

## Non-goals

`fraiseql-wire` intentionally does **not** support:

* Writes (`INSERT`, `UPDATE`, `DELETE`)
* Transactions
* Prepared statements
* Arbitrary SQL
* Multi-column result sets
* Full Postgres type decoding

If you need those features, use `tokio-postgres` or `sqlx`.

---

## Supported Query Shape

All queries must conform to:

```sql
SELECT data
FROM {source}
[WHERE <predicate>]
[ORDER BY <expression> [COLLATE <collation>] [ASC|DESC]]
[LIMIT <count>]
[OFFSET <count>]
```

### Query Components

| Component | Support | Notes |
|-----------|---------|-------|
| **SELECT** | `SELECT data` only | Result column must be named `data` and type `json`/`jsonb` |
| **FROM** | `v_{entity}` / `tv_{entity}` | Views and tables with JSON column |
| **WHERE** | SQL predicates | Optional; use `where_sql()` in builder |
| **ORDER BY** | Server-side sorting | With optional COLLATE; server-executed, no client buffering |
| **LIMIT/OFFSET** | Pagination | For result set reduction |
| **Filtering** | SQL + Rust predicates | Hybrid: SQL reduces wire traffic, Rust refines streamed data |

### Hard Constraints

* Exactly **one column** in result set (named `data`)
* Column type must be `json` or `jsonb`
* Results streamed in-order (server-side ordering for ORDER BY)
* One active query per connection
* No client-side reordering or aggregation

---

## Example

### Streaming JSON results

```rust
use futures::StreamExt;

let client = FraiseClient::connect("postgres:///example").await?;

let mut stream = client
    .query("user")
    .where_sql("data->>'status' = 'active'")
    .chunk_size(256)
    .execute()
    .await?;

while let Some(item) = stream.next().await {
    let json = item?;
    println!("{json}");
}
```

### Collecting (optional)

```rust
let users: Vec<serde_json::Value> =
    stream.collect::<Result<_, _>>()?;
```

---

## Hybrid Predicates (SQL + Rust)

Not all predicates belong in SQL. FraiseQL-Wire supports **hybrid filtering**:

```rust
let stream = client
    .query("user")
    .where_sql("data->>'type' = 'customer'")
    .where_rust(|json| expensive_check(json))
    .execute()
    .await?;
```

* SQL predicates reduce data sent over the wire
* Rust predicates allow expressive, application-level filtering
* Filtering happens **while streaming**

---

## Streaming Model

Under the hood:

* Results are read incrementally from the Postgres socket
* Rows are batched into small chunks
* Chunks are sent through a bounded async channel
* Consumers apply backpressure naturally via `.await`

This ensures:

* Bounded memory usage
* CPU and I/O overlap
* Fast time-to-first-row

---

## Cancellation & Drop Semantics

If the stream is dropped early:

* The in-flight query is cancelled
* The connection is closed
* Background tasks are terminated

This prevents runaway queries and resource leaks.

---

## Postgres 17 & Chunked Rows Mode

`fraiseql-wire` is designed to take advantage of **Postgres 17 streaming behavior**, and can optionally leverage **chunked rows mode** via a libpq-based backend.

The public API remains the same regardless of backend; chunking is an internal optimization.

---

## Quick Start

### Installation

Add to `Cargo.toml`:

```toml
[dependencies]
fraiseql-wire = "0.1"
tokio = { version = "1", features = ["full"] }
futures = "0.3"
serde_json = "1"
```

### Basic Usage

```rust
use fraiseql_wire::client::FraiseClient;
use futures::stream::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Connect to Postgres
    let client = FraiseClient::connect("postgres://localhost/mydb").await?;

    // Stream results
    let mut stream = client.query("users").execute().await?;

    while let Some(item) = stream.next().await {
        let json = item?;
        println!("{}", json);
    }

    Ok(())
}
```

### Running Examples

See `examples/` directory:

```bash
# Start Postgres with test data
docker-compose up -d

# Run examples
cargo run --example basic_query
cargo run --example filtering
cargo run --example ordering
cargo run --example streaming
cargo run --example error_handling
```

---

## Error Handling

Errors are surfaced as part of the stream:

```rust
Stream<Item = Result<serde_json::Value, FraiseError>>
```

Possible error sources include:

* Connection or authentication failures
* SQL execution errors
* Protocol violations
* Invalid result schema
* JSON decoding failures
* Query cancellation

Fatal errors terminate the stream.

For detailed error diagnosis, see [TROUBLESHOOTING.md](TROUBLESHOOTING.md).

---

## Performance Characteristics

* πŸ“‰ Memory usage scales with `chunk_size`, not result size
* ⏱ First rows are available immediately
* πŸ”„ Server I/O and client processing overlap
* πŸ“¦ JSON decoding is incremental

### Benchmarked Performance (v0.1.0)

**Memory Efficiency**: The key advantage

| Scenario | fraiseql-wire | tokio-postgres | Difference |
|----------|---------------|----------------|-----------|
| 10K rows | 1.3 KB | 2.6 MB | **2000x** |
| 100K rows | 1.3 KB | 26 MB | **20,000x** |
| 1M rows | 1.3 KB | 260 MB | **200,000x** |

fraiseql-wire uses **O(chunk_size)** memory while traditional drivers use **O(result_size)**.

**Latency & Throughput**: Comparable to tokio-postgres

| Metric | fraiseql-wire | tokio-postgres |
|--------|---------------|----------------|
| Connection setup | ~250 ns (CPU) | ~250 ns (CPU) |
| Query parsing | ~5-30 Β΅s | ~5-30 Β΅s |
| Throughput | 100K-500K rows/sec | 100K-500K rows/sec |
| Time-to-first-row | 2-5 ms | 2-5 ms |

**For detailed performance analysis**, see [PERFORMANCE_TUNING.md](PERFORMANCE_TUNING.md) and [benches/COMPARISON_GUIDE.md](benches/COMPARISON_GUIDE.md).

---

## When to Use fraiseql-wire

Use this crate if you:

* Stream large JSON result sets
* Want predictable memory usage
* Use Postgres views as an API boundary
* Prefer async streams over materialized results
* Are building FraiseQL or similar query layers

---

## When *Not* to Use It

Avoid this crate if you need:

* Writes or transactions
* Arbitrary SQL
* Strong typing across many Postgres types
* Multi-query sessions
* Compatibility with existing ORMs

---

## Advanced Features

### Type-Safe Deserialization

Stream results as custom structs instead of raw JSON:

```rust
#[derive(Deserialize)]
struct Project {
    id: String,
    name: String,
    status: String,
}

let stream = client.query::<Project>("projects").execute().await?;
while let Some(project) = stream.next().await {
    let p: Project = project?;
    println!("{}: {}", p.id, p.name);
}
```

Type `T` affects **only** deserialization; SQL, filtering, and ordering are identical regardless of `T`.

### Stream Control (Pause/Resume)

Pause and resume streams for advanced flow control:

```rust
let mut stream = client.query("entities").execute().await?;

// Process some rows
while let Some(item) = stream.next().await {
    println!("{item?}");
    break;  // Stop after one
}

// Pause to do other work
stream.pause().await?;
// ... perform other operations ...
stream.resume().await?;  // Continue from where we left off
```

### Adaptive Chunking

Automatic chunk size optimization based on channel occupancy:

```rust
let stream = client
    .query("large_table")
    .adaptive_chunking(true)    // Enabled by default
    .adaptive_min_size(16)      // Don't go below 16
    .adaptive_max_size(1024)    // Don't exceed 1024
    .execute()
    .await?;
```

### SQL Field Projection

Reduce payload size via database-level field filtering:

```rust
let stream = client
    .query("users")
    .select_projection("jsonb_build_object('id', data->>'id', 'name', data->>'name')")
    .execute()
    .await?;
// Returns only id and name fields, reducing network overhead
```

### Metrics & Tracing

Built-in metrics via the `metrics` crate:

* `fraiseql_stream_rows_yielded` – Total rows yielded from streams
* `fraiseql_stream_rows_filtered` – Rows filtered by predicates
* `fraiseql_query_duration_ms` – Query execution time
* `fraiseql_memory_usage_bytes` – Estimated memory consumption

Enable tracing with:

```bash
RUST_LOG=fraiseql_wire=debug cargo run
```

---

## Project Status

βœ… **Production Ready**

* API is stable and well-tested
* 166+ unit tests, comprehensive integration tests
* Zero clippy warnings (strict `-D warnings`)
* Fully optimized streaming engine with proven performance characteristics
* Ready for production use

All core features implemented with comprehensive CI validation:

* βœ… Async JSON streaming (integration tests across PostgreSQL 15-18)
* βœ… Hybrid SQL + Rust predicates (25+ WHERE operators with full test coverage)
* βœ… Type-safe deserialization (generic streaming API with custom struct support)
* βœ… Stream pause/resume (backpressure-aware flow control)
* βœ… Adaptive chunking (automatic memory-aware chunk optimization)
* βœ… SQL field projection (SELECT clause optimization for reduced payload)
* βœ… Server-side ordering (ORDER BY with COLLATE support, no client buffering)
* βœ… Pagination (LIMIT/OFFSET for result set reduction)
* βœ… Metrics & tracing (comprehensive observability via metrics crate)
* βœ… Error handling (detailed error types and recovery patterns)
* βœ… Connection pooling support (documented integration patterns)
* βœ… TLS/SCRAM authentication (PostgreSQL 17+ security features)

---

## Roadmap

* [x] Connection pooling integration guide (CONNECTION_POOLING.md)
* [x] Advanced filtering patterns (ADVANCED_FILTERING.md)
* [x] PostgreSQL 15-18 compatibility (POSTGRES_COMPATIBILITY.md)
* [x] SCRAM/TLS end-to-end integration tests in CI
* [x] Comprehensive metrics and tracing
* [x] Server-side ordering (ORDER BY with COLLATE)
* [x] Pagination support (LIMIT/OFFSET)
* [x] SQL field projection for payload optimization
* [ ] Extended metric examples and dashboards
* [ ] Performance tuning guide for large datasets
* [ ] PostgreSQL 19+ compatibility tracking
* [ ] Binary protocol optimization (extended query protocol)

---

## Documentation & Guides

* **[QUICK_START.md]QUICK_START.md** – Installation and first steps
* **[TESTING_GUIDE.md]TESTING_GUIDE.md** – How to run unit, integration, and load tests
* **[TROUBLESHOOTING.md]TROUBLESHOOTING.md** – Error diagnosis and common issues
* **[CI_CD_GUIDE.md]CI_CD_GUIDE.md** – GitHub Actions, local development, releases
* **[PERFORMANCE_TUNING.md]PERFORMANCE_TUNING.md** – Benchmarking and optimization
* **[CONTRIBUTING.md]CONTRIBUTING.md** – Development workflows and architecture
* **[PRD.md]PRD.md** – Product requirements and design
* **[.github/PUBLISHING.md].github/PUBLISHING.md** – Automatic crates.io publishing setup and workflow

### Examples

* **[examples/basic_query.rs]examples/basic_query.rs** – Simple streaming usage
* **[examples/filtering.rs]examples/filtering.rs** – SQL and Rust predicates
* **[examples/ordering.rs]examples/ordering.rs** – ORDER BY with collation
* **[examples/streaming.rs]examples/streaming.rs** – Large result handling and chunk tuning
* **[examples/error_handling.rs]examples/error_handling.rs** – Error handling patterns

---

## Philosophy

> *This is not a Postgres driver.*
> *It is a JSON query pipe.*

By narrowing scope, `fraiseql-wire` delivers performance and clarity that general-purpose drivers cannot.

---

## Credits

**Author:**

* Lionel Hamayon (@evoludigit)

**Part of:** FraiseQL β€” Compiled GraphQL for deterministic Postgres execution

---

## License

MIT OR Apache-2.0