Scythe
Polyglot SQL-to-code generator with built-in linting and formatting.
Write SQL. Get type-safe code. In any language.
Why Scythe
10 languages, 34 backend drivers -- Rust, Python, TypeScript, Go, Java, Kotlin, C#, Elixir, Ruby, PHP
5 databases -- PostgreSQL, MySQL, SQLite, DuckDB, CockroachDB -- all 10 languages supported on every engine
93 lint rules (22 custom + 71 sqruff) -- catch bugs before they ship
SQL formatting -- via sqruff integration
Smart type inference -- nullability from JOINs, COALESCE, window functions, aggregates
Configurable row types -- Pydantic, msgspec, Zod, or language defaults per backend
@optional parameters -- SQL rewriting for conditional filters without dynamic query building
Quick Install
cargo install scythe-cli
# or
brew install Goldziher/tap/scythe
30-Second Example
-- @name GetUserOrders
-- @returns :many
SELECT u . id , u . name , o . total , o . notes
FROM users u
LEFT JOIN orders o ON u . id = o . user_id
WHERE u . status = $ 1 ;
Scythe knows o.total and o.notes are nullable (right side of LEFT JOIN) and generates type-safe code:
Rust Python TypeScript Go Java Kotlin C# Elixir Ruby PHP
pub struct GetUserOrdersRow {
pub id : i32 ,
pub name : String ,
pub total : Option < rust_decimal :: Decimal > ,
pub notes : Option < String > ,
}
@dataclass
class GetUserOrdersRow :
id : int
name : str
total : decimal . Decimal | None
notes : str | None
interface GetUserOrdersRow {
id : number ;
name : string ;
total : string | null ;
notes : string | null ;
}
type GetUserOrdersRow struct {
ID int32 `json:"id"`
Name string `json:"name"`
Total * string `json:"total"`
Notes * string `json:"notes"`
}
public record GetUserOrdersRow (
int id ,
String name ,
@Nullable BigDecimal total ,
@Nullable String notes
) {}
data class GetUserOrdersRow (
val id : Int ,
val name : String ,
val total : java . math . BigDecimal ? ,
val notes : String? ,
)
public record GetUserOrdersRow (
int Id ,
string Name ,
decimal? Total ,
string? Notes
);
defmodule GetUserOrdersRow do
@type t :: % __MODULE__ {
id : integer (),
name : String . t (),
total : Decimal . t () | nil ,
notes : String . t () | nil
}
defstruct [ :id , :name , :total , :notes ]
end
module Queries
GetUserOrdersRow = Data . define (
:id , :name , :total , :notes
)
end
readonly class GetUserOrdersRow {
public function __construct(
public int $id,
public string $name,
public ?string $total,
public ?string $notes,
) {}
}
Learn More
Quickstart -- from zero to generated code in 5 minutes
Philosophy -- why compile SQL instead of using an ORM
Alternatives -- how scythe compares to sqlc, SQLDelight, jOOQ, and ORMs