Quantcast
Channel: ReScript Forum - Latest posts
Viewing all articles
Browse latest Browse all 2592

Ergonomic bindings to database drivers are so frustrating

$
0
0

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)


Viewing all articles
Browse latest Browse all 2592

Trending Articles