The advantage to your approach (I guess) is increased type safety for complex queries, trading off the loss of "fundamental" types with, say, branded ID types? I guess the two approaches are quite complementary, perhaps I should try that.
1. https://github.com/manifold-systems/manifold/blob/master/man...
`UPDATE t SET x=:x WHERE 1` `{x:42}`
I found that the original node-mysql didn't even allow this, so I wrote my own parser on top of it. But I don't see this style of binding used in examples very often. Is it frowned upon for some reason?
In another style, postgres.js uses calls such as sql`select * from t where id = ${variable}` (which is safe because it's a tagged template, not string interpolation).
I.e.
Query("select * from MyTable where Id = @Id", myEntity)
The idiom is to use anonymous objects which you can new up inline like “new { id, age = 5 }”, where id is an existing variable that will automatically lend its name. So it’s pretty concise.
The syntax is Sql Server native (which supports named params at the protocol level), but the Npgsql dat provider converts it to PG’s positional system automatically.
I've used kysely before creating pg-typesafe, and came to the conclusion that writing SQL directly is more convenient.
A query builder works well for simple cases (db.selectFrom("t").where("id","=","1") looks a lot like the equivalent SQL), however, for more complicated queries it all falls apart. I often had to look at the docs to find how to translate some predicate from SQL to the required idiom. Also, I don't think kysely can automatically infer the return type of PostgreSQL functions, while pg-typed does (it asks PostgreSQL for it).
https://learn.microsoft.com/en-us/dotnet/fsharp/tutorials/ty...
SQLProvider is probably the most well known one: https://fsprojects.github.io/SQLProvider/
It’s really beautiful. You get type safety with SQL. If your code compiles, you guaranteed to have valid executable both F# code and SQL. Also you get to create composable queries.
And there are other, thinner/leaner type providers as well. My favourite Postgres one is: https://github.com/Zaid-Ajaj/Npgsql.FSharp
This simple sample executes query and read results as table then map the results
open Npgsql.FSharp
type User = { Id: int FirstName: string LastName: string }
let getAllUsers (connectionString: string) : User list = connectionString |> Sql.connect |> Sql.query "SELECT * FROM users" |> Sql.execute (fun read -> { Id = read.int "user_id" FirstName = read.text "first_name" LastName = read.text "last_name" })
I see they use the same global approach as pg-typed (asking for a ParameterDescription / RowDescription, which aren't usually exposed by the PG drivers), but there are interesting differences in the details. Also this made me realise that I could also type enums automatically.
It would be quite easy to extract the queries to compute the types, but TypeScript doesn't handle tagged template literals well enough to link the query passed to the sql`` template to the return type.
I'm curious if there are any good patterns for dealing with dynamic query building or composing queries?
const { date, name, status } = args.filter;
await em.find(Employee, { date, name, employer: { status } });
Where the "shape" of the query is static, but `em.find` will drop/prune any filters/joins that are set to `undefined`.
So you get this nice "declarative / static structure" that gets "dynamically pruned to only what's applicable for the current query", instead of trying to jump through "how do I string together knex .orWhere clauses for this?" hoops.
For now, I type these manually, which is acceptable for my usage as they are pretty rare compared to static queries.
Regarding sqlc in general, it is focused on having the SQL queries in .sql files, while pg-typed is focused on having the queries inline (though I plan to add .sql file support). I like the latter approach better, as for small queries used in only one place, it is a little cumbersome to add them to a different file and find a name for them.
The queries look a but more clumsy then but you won’t cause problems when spelling the column names.
const [domain] = await db
.select()
.from(cmsDomains)
.where(eq(cmsDomains.id, domainId))
.limit(1); const { rows } = client.query(
"select id, name, last_modified from tbl where id = $1",
[42],
);
instead of const { rows } = client.query(
"select id, name, last_modified from tbl where id = :id",
{ id: 42 },
);