Type-safe SQL query builder with DBML schema validation.
Build queries as an AST, validate against your schema, render to parameterized SQL.
instance.T("users") // ✓ exists in schema
instance.T("uusers") // panic: table "uusers" not found
instance.F("email") // ✓ exists in schema
instance.F("emial") // panic: field "emial" not found
query := astql.Select(instance.T("users")).
Fields(instance.F("username"), instance.F("email")).
Where(instance.C(instance.F("active"), astql.EQ, instance.P("is_active")))
result, _ := query.Render(postgres.New())
// SELECT "username", "email" FROM "users" WHERE "active" = :is_activeTypos become compile-time failures, not runtime surprises. Values are parameterized. Identifiers are quoted. The schema is the source of truth.
Same query, different databases:
import (
"github.com/zoobz-io/astql/postgres"
"github.com/zoobz-io/astql/sqlite"
"github.com/zoobz-io/astql/mariadb"
"github.com/zoobz-io/astql/mssql"
)
result, _ := query.Render(postgres.New()) // "username", LIMIT 10
result, _ := query.Render(sqlite.New()) // "username", LIMIT 10
result, _ := query.Render(mariadb.New()) // `username`, LIMIT 10
result, _ := query.Render(mssql.New()) // [username], TOP 10One AST. Four dialects. Each renderer handles identifier quoting, pagination syntax, vendor-specific operators.
go get github.com/zoobz-io/astql
go get github.com/zoobz-io/dbmlRequires Go 1.24+.
package main
import (
"fmt"
"github.com/zoobz-io/astql"
"github.com/zoobz-io/astql/postgres"
"github.com/zoobz-io/dbml"
)
func main() {
// Define schema
project := dbml.NewProject("myapp")
users := dbml.NewTable("users")
users.AddColumn(dbml.NewColumn("id", "bigint"))
users.AddColumn(dbml.NewColumn("username", "varchar"))
users.AddColumn(dbml.NewColumn("email", "varchar"))
users.AddColumn(dbml.NewColumn("active", "boolean"))
project.AddTable(users)
// Create instance
instance, err := astql.NewFromDBML(project)
if err != nil {
panic(err)
}
// Build and render
result, err := astql.Select(instance.T("users")).
Fields(instance.F("username"), instance.F("email")).
Where(instance.C(instance.F("active"), astql.EQ, instance.P("is_active"))).
OrderBy(instance.F("username"), astql.ASC).
Limit(10).
Render(postgres.New())
if err != nil {
panic(err)
}
fmt.Println(result.SQL)
// SELECT "username", "email" FROM "users" WHERE "active" = :is_active ORDER BY "username" ASC LIMIT 10
fmt.Println(result.RequiredParams)
// [is_active]
}| Feature | Description | Docs |
|---|---|---|
| Schema Validation | Tables and fields checked against DBML at build time | Schema Validation |
| Multi-Dialect | PostgreSQL, SQLite, MariaDB, MSSQL from one AST | Architecture |
| Parameterized Values | Injection-resistant queries with named parameters | Conditions |
| Composable Queries | Subqueries, JOINs, aggregates, window functions | Joins |
| CASE Expressions | Conditional logic within queries | API |
- Schema-validated —
T("users")andF("email")checked against DBML at build time - Injection-resistant — parameterized values, quoted identifiers, no string concatenation
- Multi-dialect — one query, four databases
- Composable — subqueries, JOINs, aggregates, window functions, CASE expressions
ASTQL enables a pattern: define schema once in DBML, generate everything else.
Your DBML becomes the single source of truth. Downstream tools consume the schema to build:
- Type-safe repositories — generated data access layers with cereal
- Query builders — domain-specific methods that can't reference invalid columns
- Multi-database applications — same business logic, swappable storage backends
// Schema defines what's valid
project := dbml.ParseFile("schema.dbml")
instance, _ := astql.NewFromDBML(project)
// Queries are structurally correct by construction
users := instance.T("users")
query := astql.Select(users).
Fields(instance.F("id"), instance.F("email")).
Where(instance.C(instance.F("active"), astql.EQ, instance.P("active")))
// Render to any supported database
sql, _ := query.Render(postgres.New()) // production
sql, _ := query.Render(sqlite.New()) // testingThe schema guards the boundary. Queries inside the boundary are safe by construction.
- Overview — what astql does and why
- Quickstart — get started in minutes
- Concepts — tables, fields, params, conditions, builders
- Architecture — AST structure, render pipeline, security layers
- Schema Validation — DBML integration and validation
- Conditions — WHERE, AND/OR, subqueries, BETWEEN
- Joins — INNER, LEFT, RIGHT, CROSS joins
- Aggregates — GROUP BY, HAVING, window functions
- Testing — testing patterns for query builders
- Pagination — LIMIT/OFFSET and cursor patterns
- Vector Search — pgvector similarity queries
- Upserts — ON CONFLICT patterns
- ORM Foundation — building type-safe ORMs with cereal
See CONTRIBUTING.md for guidelines. For security issues, see SECURITY.md.
MIT — see LICENSE.