TeaQL is a DDD-oriented data runtime for applications that want generated, typed domain APIs instead of hand-written repository boilerplate.
The Rust workspace provides the runtime pieces: entity metadata, a query AST,
SQL compilation, relation enhancement, graph writes, checkers, mutation events,
and PostgreSQL/SQLite executors. The sibling teaql-code-gen project can turn a
compact domain model into a typed Rust service crate with entity structs,
Q::merchants()-style query builders, behavior/checker hooks, and graph-save
entrypoints.
TeaQL is not trying to be a general replacement for Diesel, SeaORM, or direct
sqlx use:
- use TeaQL when the domain model is central, relation graphs matter, and you want generated high-level APIs that resemble the Java TeaQL style;
- use Diesel or SeaORM when you want a conventional Rust ORM with a broad ecosystem;
- use
sqlxdirectly when explicit SQL is the right abstraction and generated domain APIs would get in the way.
The Rust rewrite keeps the scope deliberately narrow:
- PostgreSQL and SQLite only
- Rust-native metadata and query AST
- SQL compiler and runtime separated from web/framework concerns
- compatibility with every Java implementation detail is not a goal, but the high-level TeaQL programming model is being carried over where it is useful
Progress tracking lives in PROGRESS.md.
Current published release: 4.0.0.
The quickest demo uses SQLite in memory and needs no database server:
cargo run -p teaql-examples --bin sqlite_relations_graphIt bootstraps the schema, saves an Order graph with nested OrderLine and
Product objects, reloads the relation path lines.product, and prints the
typed result.
For a smaller schema/bootstrap and CRUD path:
cargo run -p teaql-examples --bin sqlite_schema_crudFor generated-service style APIs, use teaql-code-gen to generate a service
crate and write application code against Q:
use crm_erp_service::Q;
let platforms = Q::platforms()
.select_merchant_list_with(
Q::merchants()
.select_name()
.which_names_contain("TeaQL"),
)
.execute_for_list(&ctx)
.await?;teaql-core: metadata, entity traits, base entity data, values, filters, ordering, aggregates, query model, andSmartList<T>teaql-sql: SQL dialect trait, compiled query types, DDL helpers, and AST-to-SQL compilerteaql-runtime: minimal runtime mechanism,UserContext, metadata lookup, repository boundary, repository registry, behavior registry, checker registry, entity event sink, id generation,RuntimeModule, and in-memory executionteaql-provider-postgres: PostgreSQL native adapter (deadpool-postgres), schema bootstrap, transaction wrapper, row decoding, and ID-space generatorteaql-provider-sqlite: SQLite native adapter (rusqlite), schema bootstrap, transaction wrapper, row decoding, and ID-space generatorteaql-provider-mysql: MySQL native adapter (mysql_async), schema bootstrap, transaction wrapper, row decoding, and ID-space generatorteaql-provider-rusqlite: synchronous SQLite adapter for embedded and multi-architecture deployments such as routers, robots, and appliance controllersteaql-macros:TeaqlEntityderive macro plus attribute parsing and record/entity mapping generation
The large crates are now split by function instead of keeping all implementation in a single
lib.rs:
teaql-core/src:entity.rs,expr.rs,list.rs,meta.rs,mutation.rs,naming.rs,query.rs,value.rsteaql-sql/src:dialect.rs,types.rsteaql-runtime/src:checker.rs,context.rs,error.rs,event.rs,graph.rs,id.rs,memory.rs,registry.rs,repository/teaql-provider-postgres/src: PostgreSQL provider adapterteaql-provider-sqlite/src: SQLite provider adapterteaql-provider-mysql/src: MySQL provider adapterteaql-provider-rusqlite/src: synchronous rusqlite provider adapterteaql-runtime/src/repository:base.rs,cache.rs,context.rs,executor.rs,graph.rs,helpers.rs,relation.rs,resolved.rs,types.rsteaql-macros/src:attr.rs,derive_impl.rs,mapping.rs,types.rs
The current implementation focuses on the Rust-native core runtime:
- entity and relation descriptors
- query projection, filter, sort, group-by, limit, offset
- aggregate projection
- richer query builders for expressions, projections, sort, pagination, relation loads, and aggregates
- Java-style null-safe value expressions through
SafeExpression, includingapply,or_else, empty checks, callbacks,BaseEntityid/version helpers, andSmartListsize/first/get helpers - Java-style web API payload helpers through
WebStyle,WebAction, andWebResponsefor server-controlled display metadata and frontend response data - Java-style simplified Excel block model through
XlsBlock,XlsBlockBuildContext,XlsPage, andXlsWorkbook - extended predicates including
between,is null,is not null, Java-stylecontain/begin_with/end_with,not like,not in, andsoundlikethroughSOUNDEX - grouped aggregate SQL and memory execution, including
COUNT(*) - aggregate decimal results use
Value::Decimal/PostgreSQLNUMERICinstead of lossyf64 u64, signed integers, and Decimal now have explicit checked conversion behavior in entity mapping and database bind/decode paths- PostgreSQL
IN_LARGE/NOT_IN_LARGEcompile to array binds withANY/ALL - subquery filters can compile
field IN (SELECT ...)/field NOT IN (SELECT ...) - expression projections, expression/function ordering, extended aggregates, and
HAVING - PostgreSQL and SQLite placeholder differences
- insert, update, delete, and recover command models
- optimistic locking through
id + versionpredicates on update/delete/recover - recover and batch mutation helpers
- in-memory metadata store for bootstrapping descriptors
- Rust-native
UserContextfor metadata resolution, typed resources, named resources, and local request-scope storage UserContextcan assemble a repository from registered dialect and executor resources, making it the main runtime entry pointUserContextcan also resolve repositories by entity type throughRepositoryRegistry- per-entity
RepositoryBehaviorhooks can mutate queries/commands and expose relation-load plans - Java-style checker infrastructure through
Checker,CheckResult,ObjectLocation, object status, andCheckerRegistry - repository insert/update preparation invokes the single
UserContext::check_and_fix_record()path and lets checkers distinguish create/update by object status - Java-style natural-language checker message translation with 15 built-in languages and
UserContextlanguage switching - Java-style entity mutation events through
EntityEvent,EntityEventKind, andEntityEventSink UserContextcan dispatch created/updated/deleted/recovered events from ordinary repository mutations and graph writes- relation preload plans can now be resolved from behavior hooks and converted into child batch queries from parent rows
- relation enhancement supports batch child-query generation and backfilling related records into parent records
- nested relation enhancement supports paths like
lines.product - relation aggregate enhancement supports count/statistic properties on parent rows, including cases where the database column name differs from the entity property name
TeaqlEntityderive support for declarative entity descriptors- typed entity mapping through
EntityandSmartList<T> - typed nested relation enhancement through
fetch_enhanced_entities::<T>() - unified graph write path through
GraphNodeandsave_graph(), covering create and upsert requests - graph upsert path through
save_graph(), including parent update, child merge, child insert, and missing-child soft delete - graph writes build a
GraphMutationPlanclassified by entity type and operation before execution - graph planning is available through
UserContext::plan_for_save_graph()for debugging - graph planning assigns missing create ids before batching; creates/deletes merge by entity type, updates merge only when the updated field set is identical
save_graph()requires a transactional executor and rolls back the graph write when any planned operation fails- typed entity graph extraction through
graph_node_from_entity()andsave_entity_graph() - graph write state hints:
Upsert,Reference, andRemove - stricter graph state semantics: reference nodes validate existence/version/deleted state, remove nodes validate existence, and many-relation merge rejects duplicate child ids
- relation metadata for graph writes:
attach/detachedanddelete_missing/keep_missing - native transaction boundary helpers for graph-write wrapping and rollback testing live in the per-database provider crates
- declarative runtime assembly through
RuntimeModuleandmodule! - built-in
SnowflakeIdGeneratorandUserContext-driven id generation; nativeteaql_id_spacegenerators live in the per-database provider crates BaseEntityData/BaseEntityfor sharedid + version + dynamicentity state- dynamic-property capture through
#[teaql(dynamic)], with JSON flattening for aggregate-style outputs MemoryRepositoryfor no-database tests and lightweight in-memory execution- PostgreSQL, SQLite, and MySQL execution moved to per-database provider crates using native drivers
- SQLite
ensure_schemasupport for create-table and add-missing-column flows inteaql-provider-sqlite - PostgreSQL
ensure_schemasupport with real multi-table integration validation, includingsoundex(text)andteaql_id_spacebootstrap, inteaql-provider-postgres UserContext::ensure_schema()as the database-neutral schema entry point after a provider is registered- JSON, Decimal, date, and timestamp bind/decode support in the per-database providers
- SQLite in-memory integration tests for CRUD and relation enhancement in the provider
- SQLite integration coverage for nested create-graph writes
- SQLite integration coverage for nested graph update diff
- SQLite integration coverage for reference-only nodes, explicit remove, keep-missing relation metadata, and transaction rollback
- SQLite relation aggregate coverage through generated high-level
QAPIs in an external generated service crate - PostgreSQL integration coverage for graph-write transaction rollback when
TEAQL_TEST_PG_URLis provided - PostgreSQL integration tests in the provider when
TEAQL_TEST_PG_URLis provided
TeaqlEntity derive now generates both metadata and typed Entity mapping. Repository APIs can
return either raw Record rows or typed SmartList<T> collections.
use teaql_core::{Expr, SelectQuery, SmartList, TeaqlEntity};
#[derive(Clone, Debug, Default, TeaqlEntity)]
#[teaql(entity = "CatalogProduct", table = "catalog_product_data")]
struct CatalogProductRow {
#[teaql(id, column = "id")]
id: u64,
#[teaql(version, column = "version")]
version: i64,
#[teaql(column = "name")]
name: String,
}
fn query() -> SelectQuery {
SelectQuery::new("CatalogProduct").filter(Expr::eq("name", "desk"))
}
async fn fetch_products(
repo: &teaql_runtime::ResolvedRepository<'_>,
) -> Result<SmartList<CatalogProductRow>, teaql_runtime::RepositoryError> {
repo.fetch_entities::<CatalogProductRow>(&query())
}SmartList<T> keeps TeaQL-style list metadata alongside the typed rows:
datatotal_countaggregationssummary
When the entity defines #[teaql(id)] or #[teaql(version)], SmartList<T> also exposes:
ids()versions()into_records()
fetch_enhanced_entities::<T>() runs record-based relation enhancement first, then converts the
result into typed nested entities.
use teaql_core::{SelectQuery, SmartList, TeaqlEntity};
#[derive(Clone, Debug, Default, TeaqlEntity)]
#[teaql(entity = "Product", table = "product_data")]
struct ProductRow {
#[teaql(id, column = "id")]
id: u64,
#[teaql(version, column = "version")]
version: i64,
#[teaql(column = "name")]
name: String,
}
#[derive(Clone, Debug, Default, TeaqlEntity)]
#[teaql(entity = "OrderLine", table = "order_line_data")]
struct OrderLineRow {
#[teaql(id, column = "id")]
id: u64,
#[teaql(version, column = "version")]
version: i64,
#[teaql(column = "order_id")]
order_id: u64,
#[teaql(
relation(
target = "Product",
local_key = "product_id",
foreign_key = "id"
)
)]
product: Option<ProductRow>,
}
#[derive(Clone, Debug, Default, TeaqlEntity)]
#[teaql(entity = "Order", table = "order_data")]
struct OrderRow {
#[teaql(id, column = "id")]
id: u64,
#[teaql(version, column = "version")]
version: i64,
#[teaql(
relation(
target = "OrderLine",
local_key = "id",
foreign_key = "order_id",
many
)
)]
lines: SmartList<OrderLineRow>,
}
async fn fetch_orders(
repo: &teaql_runtime::ResolvedRepository<'_>,
) -> Result<SmartList<OrderRow>, teaql_runtime::RepositoryError> {
repo.fetch_enhanced_entities::<OrderRow>(&SelectQuery::new("Order"))
}For nested enhancement, register relation paths from repository behavior, for example:
lineslines.product
Schema setup is implemented by the selected database provider, but exposed through the database-neutral runtime entry point:
ctx.ensure_schema().await?;Each database provider exposes a registration helper that installs its dialect,
executor, and schema provider into UserContext.
PostgreSQL:
use teaql_provider_postgres::{
PgMutationExecutor, PostgresProviderExt,
};
ctx.use_postgres_provider(PgMutationExecutor::new(pg_pool));
ctx.ensure_schema().await?;SQLite:
use teaql_provider_sqlite::{SqliteMutationExecutor, SqliteProviderExt};
ctx.use_sqlite_provider(SqliteMutationExecutor::new(sqlite_pool));
ctx.ensure_schema().await?;MySQL:
use teaql_provider_mysql::{
MysqlMutationExecutor, MysqlProviderExt,
};
ctx.use_mysql_provider(MysqlMutationExecutor::new(mysql_pool));
ctx.ensure_schema().await?;rusqlite:
use rusqlite::Connection;
use teaql_provider_rusqlite::{RusqliteMutationExecutor, RusqliteProviderExt};
let executor = RusqliteMutationExecutor::new(Connection::open("app.db")?);
ctx.use_rusqlite_provider(executor);
ctx.ensure_schema().await?;Current ensure_schema scope:
- create missing tables
- add missing columns to existing tables
- do not attempt destructive migrations such as drop column, type rewrite, or primary-key rebuild
Runnable examples live in the teaql-examples workspace package:
cargo run -p teaql-examples --bin sqlite_schema_crud
cargo run -p teaql-examples --bin sqlite_relations_graphThe PostgreSQL example uses real schema bootstrap and PG query features. Set
TEAQL_TEST_PG_URL before running it:
TEAQL_TEST_PG_URL=postgres://postgres:postgres@127.0.0.1:55440/teaql_examples \
cargo run -p teaql-examples --bin pg_query_featuresCurrent examples cover:
- SQLite schema bootstrap, CRUD, optimistic lock delete/recover, and typed entity fetch
- SQLite typed entity graph writes through
save_entity_graph() - SQLite relation enhancement through
fetch_enhanced_entities::<T>() - PostgreSQL
soundlike/SOUNDEX, array-bound large IN, expression projection/sort, extended aggregates, andHAVING - Generated-service validation with high-level
QAPIs covers complex object commit, subtrait-style DDD methods, JSON serialization, JSON-expression search, and simple-to-relation statistics against SQLite
TeaQL supports the following environment variables for configuration and debugging:
TEAQL_LOG_ENDPOINT: If set, specifies an absolute file path where TeaQL will append all internal execution logs (e.g., SQL queries, audit events). This allows external AI agents or log-collectors to analyze the runtime execution of the framework directly.TEAQL_LOG_FORMAT: Controls the format of the output log specified byTEAQL_LOG_ENDPOINT. Can be set tojson(ordebug) for structured JSON logging, orhuman(default) for human-readable output.TEAQL_TEST_PG_URL: The PostgreSQL connection URL used during tests and examples.
- Expand
MemoryRepositorytoward relation enhancement and richer parity with the SQL-backed path. - Add typed checker generation and richer Java-style validation semantics.
- Keep expanding value coverage beyond the current JSON/date/timestamp/Decimal set, especially
Uuidand bytes. - Decide whether a Rust-native service layer is needed above repository/runtime APIs.