Backend Architecture
Scythe generates type-safe code from SQL queries. Each backend is defined by:
- Manifest (
manifest.toml) -- declares the language, type mappings, naming conventions, and import rules.
- Templates (Jinja2) -- render row structs, query functions, enums, and composites.
- Rust trait (
CodegenBackend) -- implements generate_row_struct, generate_query_fn, generate_enum_def, etc.
Manifest structure
[backend]
name = "rust-sqlx"
language = "rust"
file_extension = "rs"
engine = "postgresql"
[types.scalars]
int32 = "i32"
string = "String"
datetime_tz = "chrono::DateTime<chrono::Utc>"
[types.containers]
array = "Vec<{T}>"
nullable = "Option<{T}>"
range = "sqlx::postgres::types::PgRange<{T}>"
json_typed = "sqlx::types::Json<{T}>"
[naming]
struct_case = "PascalCase"
field_case = "snake_case"
fn_case = "snake_case"
enum_variant_case = "PascalCase"
row_suffix = "Row"
[imports.rules]
"chrono::" = "use chrono;"
"uuid::Uuid" = "use uuid::Uuid;"
Type resolution pipeline
SQL type --> neutral type --> language type
──────── ──────────── ─────────────
SERIAL int32 i32
TIMESTAMPTZ datetime_tz chrono::DateTime<chrono::Utc>
TEXT[] array<string> Vec<String>
user_status enum::user_status UserStatus
Neutral types are the bridge. The analyzer converts SQL types to neutral types; the backend manifest maps neutral types to language types. See Neutral Types for the full mapping table.
Supported backends
Scythe provides 70+ backends across 10 languages and 10 database engines. Some backends (like java-jdbc) support multiple engines via engine-specific manifests loaded at runtime.
PostgreSQL
| Backend |
Language |
Library |
rust-sqlx |
Rust |
sqlx |
rust-tokio-postgres |
Rust |
tokio-postgres |
python-psycopg3 |
Python |
psycopg3 |
python-asyncpg |
Python |
asyncpg |
typescript-postgres |
TypeScript |
postgres.js |
typescript-pg |
TypeScript |
pg (node-postgres) |
go-pgx |
Go |
pgx v5 |
java-jdbc |
Java |
JDBC |
kotlin-jdbc |
Kotlin |
JDBC |
csharp-npgsql |
C# |
Npgsql |
elixir-postgrex |
Elixir |
Postgrex |
ruby-pg |
Ruby |
pg gem |
php-pdo |
PHP |
PDO |
java-r2dbc |
Java |
R2DBC (Project Reactor) |
kotlin-r2dbc |
Kotlin |
R2DBC (coroutines) |
kotlin-exposed |
Kotlin |
Exposed |
MySQL
| Backend |
Language |
Library |
rust-sqlx |
Rust |
sqlx |
python-aiomysql |
Python |
aiomysql |
typescript-mysql2 |
TypeScript |
mysql2 |
go-database-sql |
Go |
database/sql |
java-jdbc |
Java |
JDBC |
kotlin-jdbc |
Kotlin |
JDBC |
csharp-mysqlconnector |
C# |
MySqlConnector |
elixir-myxql |
Elixir |
MyXQL |
ruby-mysql2 |
Ruby |
mysql2 gem |
php-pdo |
PHP |
PDO |
SQLite
| Backend |
Language |
Library |
rust-sqlx |
Rust |
sqlx |
python-aiosqlite |
Python |
aiosqlite |
typescript-better-sqlite3 |
TypeScript |
better-sqlite3 |
go-database-sql |
Go |
database/sql |
java-jdbc |
Java |
JDBC |
kotlin-jdbc |
Kotlin |
JDBC |
csharp-microsoft-sqlite |
C# |
Microsoft.Data.Sqlite |
elixir-exqlite |
Elixir |
Exqlite |
ruby-sqlite3 |
Ruby |
sqlite3 gem |
php-pdo |
PHP |
PDO |
DuckDB
| Backend |
Language |
Library |
python-duckdb |
Python |
duckdb |
rust-duckdb |
Rust |
duckdb-rs |
typescript-duckdb |
TypeScript |
duckdb-node |
CockroachDB
CockroachDB is wire-compatible with PostgreSQL. The following PostgreSQL backends support CockroachDB when the engine is set to cockroachdb:
| Backend |
Language |
Library |
rust-sqlx |
Rust |
sqlx |
python-psycopg3 |
Python |
psycopg3 |
go-pgx |
Go |
pgx v5 |
java-jdbc |
Java |
JDBC |
kotlin-jdbc |
Kotlin |
JDBC |
MSSQL
| Backend |
Language |
Library |
rust-tiberius |
Rust |
tiberius |
python-pyodbc |
Python |
pyodbc |
typescript-mssql |
TypeScript |
mssql (tedious) |
go-mssqldb |
Go |
go-mssqldb |
java-jdbc |
Java |
JDBC (Microsoft JDBC Driver) |
java-r2dbc |
Java |
R2DBC (r2dbc-mssql) |
kotlin-jdbc |
Kotlin |
JDBC (Microsoft JDBC Driver) |
kotlin-r2dbc |
Kotlin |
R2DBC (r2dbc-mssql) |
csharp-sqlclient |
C# |
Microsoft.Data.SqlClient |
ruby-tiny-tds |
Ruby |
tiny_tds |
php-pdo |
PHP |
PDO (sqlsrv driver) |
elixir-tds |
Elixir |
tds |
Oracle
| Backend |
Language |
Library |
rust-sibyl |
Rust |
sibyl |
python-oracledb |
Python |
oracledb |
typescript-oracledb |
TypeScript |
oracledb (node-oracledb) |
go-godror |
Go |
godror |
java-jdbc |
Java |
JDBC (Oracle JDBC / ojdbc) |
java-r2dbc |
Java |
R2DBC (oracle-r2dbc) |
kotlin-jdbc |
Kotlin |
JDBC (Oracle JDBC / ojdbc) |
kotlin-r2dbc |
Kotlin |
R2DBC (oracle-r2dbc) |
csharp-odpnet |
C# |
ODP.NET |
ruby-oci8 |
Ruby |
ruby-oci8 |
php-pdo |
PHP |
PDO (oci driver) |
elixir-jamdb-oracle |
Elixir |
jamdb_oracle |
MariaDB
MariaDB uses MySQL drivers with MariaDB-specific type resolution:
| Backend |
Language |
Library |
rust-sqlx |
Rust |
sqlx (MySQL driver) |
python-aiomysql |
Python |
aiomysql |
typescript-mysql2 |
TypeScript |
mysql2 |
go-database-sql |
Go |
database/sql |
java-jdbc |
Java |
JDBC (MariaDB Connector/J) |
kotlin-jdbc |
Kotlin |
JDBC (MariaDB Connector/J) |
csharp-mysqlconnector |
C# |
MySqlConnector |
elixir-myxql |
Elixir |
MyXQL |
ruby-mysql2 |
Ruby |
mysql2 |
php-pdo |
PHP |
PDO |
Redshift
Redshift uses PostgreSQL backends with Redshift-specific type resolution:
| Backend |
Language |
Library |
rust-sqlx |
Rust |
sqlx (PostgreSQL driver) |
rust-tokio-postgres |
Rust |
tokio-postgres |
python-psycopg3 |
Python |
psycopg3 |
python-asyncpg |
Python |
asyncpg |
typescript-pg |
TypeScript |
pg |
typescript-postgres |
TypeScript |
postgres.js |
go-pgx |
Go |
pgx v5 |
java-jdbc |
Java |
JDBC (Redshift JDBC driver) |
kotlin-jdbc |
Kotlin |
JDBC (Redshift JDBC driver) |
csharp-npgsql |
C# |
Npgsql |
elixir-postgrex |
Elixir |
Postgrex |
ruby-pg |
Ruby |
pg |
php-pdo |
PHP |
PDO |
Snowflake
| Backend |
Language |
Library |
python-snowflake |
Python |
snowflake-connector-python |
typescript-snowflake |
TypeScript |
snowflake-sdk |
go-gosnowflake |
Go |
gosnowflake |
java-jdbc |
Java |
JDBC (Snowflake JDBC driver) |
kotlin-jdbc |
Kotlin |
JDBC (Snowflake JDBC driver) |
csharp-snowflake |
C# |
Snowflake.Data |
php-pdo |
PHP |
PDO (Snowflake PDO driver) |
Language coverage summary
| Language |
PostgreSQL |
MySQL |
SQLite |
DuckDB |
CockroachDB |
MSSQL |
Oracle |
MariaDB |
Redshift |
Snowflake |
| Rust |
sqlx, tokio-postgres |
sqlx |
sqlx |
duckdb-rs |
sqlx |
tiberius |
sibyl |
sqlx |
sqlx |
-- |
| Python |
psycopg3, asyncpg |
aiomysql |
aiosqlite |
duckdb |
psycopg3 |
pyodbc |
oracledb |
aiomysql |
psycopg3 |
snowflake-connector |
| TypeScript |
postgres.js, pg |
mysql2 |
better-sqlite3 |
duckdb-node |
-- |
mssql |
oracledb |
mysql2 |
pg |
snowflake-sdk |
| Go |
pgx |
database/sql |
database/sql |
-- |
pgx |
go-mssqldb |
godror |
database/sql |
pgx |
gosnowflake |
| Java |
JDBC, R2DBC |
JDBC |
JDBC |
-- |
JDBC |
JDBC, R2DBC |
JDBC, R2DBC |
JDBC |
JDBC |
JDBC |
| Kotlin |
JDBC, R2DBC, Exposed |
JDBC |
JDBC |
-- |
JDBC |
JDBC, R2DBC |
JDBC, R2DBC |
JDBC |
JDBC |
JDBC |
| C# |
Npgsql |
MySqlConnector |
Microsoft.Data.Sqlite |
-- |
-- |
Microsoft.Data.SqlClient |
ODP.NET |
MySqlConnector |
Npgsql |
Snowflake.Data |
| Elixir |
Postgrex |
MyXQL |
Exqlite |
-- |
-- |
tds |
jamdb_oracle |
MyXQL |
Postgrex |
-- |
| Ruby |
pg |
mysql2 |
sqlite3 |
-- |
-- |
tiny_tds |
ruby-oci8 |
mysql2 |
pg |
-- |
| PHP |
PDO |
PDO |
PDO |
-- |
-- |
PDO |
PDO |
PDO |
PDO |
PDO |
Adding a new backend
- Create a manifest TOML with scalar/container type mappings.
- Add Jinja2 templates for row structs, query functions, enums, and composites.
- Implement the
CodegenBackend trait.
- Register the backend in the codegen module.
The CodegenBackend trait:
pub trait CodegenBackend: Send + Sync {
fn name(&self) -> &str;
fn manifest(&self) -> &BackendManifest;
fn generate_row_struct(&self, query_name: &str, columns: &[ResolvedColumn]) -> Result<String, ScytheError>;
fn generate_model_struct(&self, table_name: &str, columns: &[ResolvedColumn]) -> Result<String, ScytheError>;
fn generate_query_fn(&self, analyzed: &AnalyzedQuery, struct_name: &str, columns: &[ResolvedColumn], params: &[ResolvedParam]) -> Result<String, ScytheError>;
fn generate_enum_def(&self, enum_info: &EnumInfo) -> Result<String, ScytheError>;
fn generate_composite_def(&self, composite: &CompositeInfo) -> Result<String, ScytheError>;
fn file_header(&self) -> String;
fn file_footer(&self) -> String;
fn supported_engines(&self) -> &[&str];
}