agreed this is a good solution… however, you have to immediately use query3(…) and cant put layers between, afaik, for example, what I have now is
super simple bindings layer
@unboxed type paramTypes = I(int) | S(string) | @as(undefined) Null
@send external query: (Pool.t, string, array<paramTypes>) => 'a = "query"
next is “API” layer, for dealing with how annoying the mysql API is. Inserts returns [resultSetHeader, metadata],
select return [rows, metadata] (even if its a limit 1). selectAll should always return an array, however selectFirst should return an Option, I can hide all that in this layer.
let insert = async (~db: Pool.t, ~sql: string, ~args: array<paramTypes>) => {
let rows = await query(db, sql, args)
let result: resultSetHeader = Array.getUnsafe(rows, 0)
result
}
let selectFirst = async (~db: Pool.t, ~sql: string, ~args: array<paramTypes>) => {
let rows = await query(db, sql, args)
let result = Array.getUnsafe(rows, 0)
result[0]
}
let selectAll = async (~db: Pool.t, ~sql: string, ~args: array<paramTypes>) => {
let rows = await query(db, sql, args)
let result = Array.getUnsafe(rows, 0)
result
}
let update = async (~db: Pool.t, ~sql: string, ~args: array<paramTypes>) => {
let rows = await query(db, sql, args)
let result: resultSetHeader = Array.getUnsafe(rows, 0)
result
}
At the top we have the “Data Access Layer” (which will have many more functions than the above API layer), I can do things like this without worrying about meta data, etc
let find = async (~db: Mysql.Pool.t, ~userId: int, ~id: int): option<t> => {
let sql = `select * from folders where userId=? and id=? limit 1`
let args = [Mysql.I(userId), Mysql.I(id)]
let maybeFolder = await Mysql.selectFirst(~db, ~sql, ~args)
maybeFolder
}
let findAll = async (~db: Mysql.Pool.t, ~userId: int): array<t> => {
let sql = `select * from folders where userId=?`
let args = [Mysql.I(userId)]
await Mysql.selectAll(~db, ~sql, ~args)
}
I expect 50+ DAL layers like above. I think its a benefit to put most of the complicated driver nonsense (I’ve never used that metadata from a SQL select once in my life). The API layer so far only needs insert, selectAll, selectFirst, update, delete and exists ( and “delete” is just an alias for update)
I suppose I could do variations like findAll3(~db, ~sql, ~userId)
, which would be a lot of boilerplate, or even pass the queryX function ( I think this is allowed, haven’t tried it ) findAll(~db, ~sql, ~userId, ~query=query3)