Clojure developers now have a more robust way to handle SQL data access with the release of Bisql 0.4.0. The latest version integrates Malli validation into generated SQL templates, combining schema-driven type safety with automatic query construction. This update eliminates repetitive CRUD operations while maintaining the clarity and consistency of raw SQL-first development.
SQL-first development without the tedium
Many Clojure SQL libraries force a trade-off between consistency and convenience. Some libraries rely entirely on SQL templates, which can become monotonous for routine CRUD operations. Others introduce query builders, but this often leads to inconsistent codebases where simple queries are written as templates while complex ones use builders. Over time, this drift erodes readability and makes database interactions harder to audit.
Bisql takes a different route: it generates a full suite of CRUD operations automatically by inspecting your database schema. It connects to your database, analyzes tables and indexes, and produces SQL templates optimized for performance. These templates are then converted into Clojure functions using the defquery macro, ensuring every query remains a concrete SQL file. No hidden generators or opaque builders—just SQL you can read, modify, and version.
Malli validation: type safety built in
Version 0.4.0 extends Bisql’s templating system to support Malli schemas for both input parameters and output rows. Each generated query now includes :malli/in and :malli/out metadata, enabling runtime validation without extra boilerplate.
For example, a get-by-id query might look like this in a .sql template:
/*:name crud.get-by-id */
/*:cardinality :one */
/*:malli/in [:map {:closed true} [:id int?]] */
/*:malli/out [:maybe sql.postgresql.public.users.schema/row] */
SELECT * FROM users WHERE id = /*$id*/1The corresponding Malli schema file for the users table is generated automatically:
(ns sql.postgresql.public.users.schema
(:require [bisql.schema :as bisql.schema]))
def insert
[:map {:closed true}
[:id [:or int? bisql.schema/malli-default-sentinel]]
[:email string?]
[:display-name string?]
[:status [:or string? bisql.schema/malli-default-sentinel]]
[:created-at [:or [:fn bisql.schema/offset-date-time?] bisql.schema/malli-default-sentinel]]]
def update
(bisql.schema/malli-map-all-entries-optional insert)
def row
(bisql.schema/malli-map-all-entries-strip-default-sentinel insert)With these schemas in place, Bisql can validate query inputs and outputs during execution, catching type mismatches early. The validation behavior is configurable, so teams can enforce strict schemas in production while relaxing them during development.
Conditional logic with inline expressions
Bisql 0.4.0 also introduces a lightweight expression language for conditional logic within SQL templates. This allows developers to include if conditions directly in their queries without resorting to separate builders or template engines. For instance, you can now write:
/*:name crud.list-active-users */
SELECT * FROM users
WHERE status = 'active'
/*?if {:feature-flags/enabled? :premium-users}*/
AND subscription_tier >= 2
/*?endif*/This keeps queries self-contained and avoids the cognitive overhead of managing multiple layers of abstraction.
The road ahead
Bisql’s design philosophy remains unchanged: prioritize SQL-first clarity while automating the repetitive parts. The addition of Malli validation and inline expressions in 0.4.0 further reduces boilerplate without sacrificing control. As the library matures, expect deeper integrations with Clojure’s tooling ecosystem and expanded support for edge-case query patterns.
For teams already using Bisql, upgrading is straightforward—just update the dependency and regenerate your schemas. The library handles the rest, leaving you to focus on building reliable, type-safe database interactions.
AI summary
Bisql 0.4.0 now supports Malli schema validation for Clojure SQL templates, automating CRUD query generation while ensuring type safety. Discover how to streamline database access.