Skip to content

Conversation

@cloutiertyler
Copy link
Contributor

@cloutiertyler cloutiertyler commented Feb 10, 2026

Description of Changes

Standardizes the query builder API across all three language SDKs (Rust, TypeScript, C#) for consistency.

Rust:

  • Rename Query struct to RawQuery, make Query a trait with fn into_sql(self) -> String
  • All builder types (Table, FromWhere, LeftSemiJoin, RightSemiJoin) implement Query<T> trait
  • Views can return -> impl Query<T> instead of specifying exact builder types
  • The #[view] macro auto-detects impl Query<T> and rewrites to RawQuery<T>
  • Add Not variant to BoolExpr with .not() method

TypeScript:

  • Add ne() to ColumnExpression
  • Refactor BooleanExpr to BoolExpr class with chainable .and(), .or(), .not() methods
  • Make builders valid queries directly (.build() deprecated but still works)
  • Deprecate from() wrapper — use tables.person.where(...) directly
  • Merge query export into tables so table refs are also query builders
  • Add subscription callback form: subscribe(ctx => ctx.from.person.where(...))
  • Unify useTable with query builder syntax; deprecate filter.ts

C#:

  • Add Not() method to BoolExpr<TRow>
  • Add IQuery<TRow> interface implemented by all builder types (Table, FromWhere, LeftSemiJoin, RightSemiJoin, Query)
  • Add ToSql() to all builder types so .Build() is no longer required
  • Update AddQuery to accept IQuery<TRow> instead of Query<TRow>

API and ABI breaking changes

  • Rust: Query<T> is now a trait (was a struct). The struct is renamed to RawQuery<T>. This is a breaking change for any code that used Query<T> as a type directly.
  • TypeScript: BooleanExpr is now a BoolExpr class (was a discriminated union type). The query export is deprecated in favor of tables.
  • C#: AddQuery now accepts Func<QueryBuilder, IQuery<TRow>> instead of Func<QueryBuilder, Query<TRow>>. Existing .Build() calls still work since Query<TRow> implements IQuery<TRow>.

Expected complexity level and risk

3 — Changes touch multiple language SDKs and codegen, but each individual change is straightforward. The Rust macro rewrite for impl Query<T> detection is the most complex piece. All existing .build()/.Build() calls continue to work.

Testing

  • cargo test -p spacetimedb-query-builder — 16/16 tests pass
  • cargo check -p spacetimedb — clean, no warnings
  • cargo check on views-query, views-sql, views-basic, views-trapped smoketest modules — all clean
  • cargo test -p spacetimedb-codegen codegen_csharp — snapshot updated, passes
  • npm test (TypeScript) — 101/101 tests pass
  • C# QueryBuilder tests — new tests for Not(), IQuery<T> interface
  • CI passes

Documents inconsistencies across the query builder API in Rust server,
TypeScript server, TypeScript client, and React useTable, with a plan
to unify the syntax across all languages.
- Rust: Rename Query struct to RawQuery, make Query a trait so views
  can return `impl Query<T>` instead of specific builder types
- Rust: Add Not variant to BoolExpr with .not() method
- Rust: Implement Query trait for all builder types (Table, FromWhere,
  LeftSemiJoin, RightSemiJoin) and ViewReturn for RawQuery
- Rust: View macro detects `impl Query<T>` return type and rewrites to
  RawQuery<T> with automatic conversion
- TypeScript: Add ne() to ColumnExpression
- TypeScript: Refactor BooleanExpr to BoolExpr class with .and()/.or()/.not() methods
- TypeScript: Make builders valid queries directly (.build() deprecated)
- TypeScript: Deprecate from() wrapper, merge query into tables export
- TypeScript: Add subscription callback form accepting query functions
- TypeScript: Unify useTable with query builder syntax
- TypeScript: Deprecate filter.ts module
- Add Not() method to BoolExpr<TRow> for boolean negation
- Add IQuery<TRow> interface implemented by all builder types
  (Table, FromWhere, LeftSemiJoin, RightSemiJoin, Query)
- Add ToSql() to all builder types so .Build() is no longer required
- Update AddQuery to accept IQuery<TRow> instead of Query<TRow>
- Update codegen and all generated bindings
- Add tests for Not() and IQuery<TRow> interface usage
@cloutiertyler cloutiertyler force-pushed the tyler/query-builder-syntax-standardization branch from e9e3dca to 6e329ec Compare February 11, 2026 04:31
@cloutiertyler cloutiertyler changed the title Query builder syntax standardization plan Standardize query builder syntax across Rust, TypeScript, and C# Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant