fraiseql_db/traits/relay.rs
1//! Relay cursor pagination trait.
2//!
3//! [`RelayDatabaseAdapter`] extends [`DatabaseAdapter`](super::DatabaseAdapter)
4//! with keyset-based pagination for Relay-style GraphQL connections.
5
6use std::future::Future;
7
8use fraiseql_error::Result;
9
10use super::{CursorValue, DatabaseAdapter, RelayPageResult};
11use crate::{types::sql_hints::OrderByClause, where_clause::WhereClause};
12
13/// Database adapter supertrait for adapters that implement Relay cursor pagination.
14///
15/// Only adapters that genuinely support keyset pagination need to implement this trait.
16/// Non-implementing adapters carry no relay code at all — no stubs, no flags.
17///
18/// # Implementors
19///
20/// - `PostgresAdapter` — full keyset pagination
21/// - `MySqlAdapter` — keyset pagination with `?` params
22/// - `CachedDatabaseAdapter<A>` — delegates to inner `A`
23///
24/// # Usage
25///
26/// Construct an `Executor` with `Executor::new_with_relay` to enable relay
27/// query execution. The bound `A: RelayDatabaseAdapter` is enforced at that call site.
28pub trait RelayDatabaseAdapter: DatabaseAdapter {
29 /// Execute keyset (cursor-based) pagination against a JSONB view.
30 ///
31 /// # Arguments
32 ///
33 /// * `view` — SQL view name (will be quoted before use)
34 /// * `cursor_column` — column used as the pagination key (e.g. `pk_user`, `id`)
35 /// * `after` — forward cursor: return rows where `cursor_column > after`
36 /// * `before` — backward cursor: return rows where `cursor_column < before`
37 /// * `limit` — row fetch count (pass `page_size + 1` to detect `hasNextPage`)
38 /// * `forward` — `true` → ASC order; `false` → DESC (re-sorted ASC via subquery)
39 /// * `where_clause` — optional user-supplied filter applied after the cursor condition
40 /// * `order_by` — optional custom sort; cursor column appended as tiebreaker
41 /// * `include_total_count` — when `true`, compute the matching row count before LIMIT
42 ///
43 /// # Errors
44 ///
45 /// Returns `FraiseQLError::Database` on SQL execution failure.
46 fn execute_relay_page<'a>(
47 &'a self,
48 view: &'a str,
49 cursor_column: &'a str,
50 after: Option<CursorValue>,
51 before: Option<CursorValue>,
52 limit: u32,
53 forward: bool,
54 where_clause: Option<&'a WhereClause>,
55 order_by: Option<&'a [OrderByClause]>,
56 include_total_count: bool,
57 ) -> impl Future<Output = Result<RelayPageResult>> + Send + 'a;
58}