Plain SQL
No ORM, no query DSL, no ppx.
You write the SQL. You declare its parameters and rows once, and that one declaration binds the values going in and decodes the rows coming out.
opam repo add hypha https://github.com/hyphatech/opam-repository.gitopam install rowtype rowtype-postgres rowtype-migratemodule S = Rowtype
type user = { id : int; name : string }
let user = S.map (S.t2 S.int S.text) ~parse:(fun (id, name) -> { id; name }) ~print:(fun u -> (u.id, u.name))
let add_user = S.find ~params:S.text ~row:S.int "insert into users (name) values ($1) returning id"
let find_user = S.find_opt ~params:S.int ~row:user "select id, name from users where id = $1"No ORM, no query DSL, no ppx.
Every statement described by the database from a test, without running it, so a renamed column fails a test instead of a request.
The same statements run on both.
Plain SQL files and one command for every dialect, a library’s own migrations included.
A refused write names its constraint, and a lost write is told from one never sent.
Rows read in the memory of one, and loads through COPY.