I sort of see the value here, but isn't SQL already mostly "type safe"? Isn't following a repository pattern or something sufficient to ensure this behavior? My IDE already cries from how slow tsc / the ts language server is.
Your repository return type may say what it returns, but how would you statically prove the structure of the raw SQL string? Most of the detection today is at runtime perhaps building a class or validating the data.
Litdb – type safe SQL for JavaScript/TS
31–40 of 70 posts
Re: Litdb – type safe SQL for JavaScript/TS
#32EDIT: They don't even support, not to not mention, CTE! Come on!
Re: Litdb – type safe SQL for JavaScript/TS
#33Earlier quoted context omitted.
Your repository return type may say what it returns, but how would you statically prove the structure of the raw SQL string? Most of the detection today is at runtime perhaps building a class or validating the data.
With tests? Or just a schema definition?
This supports static verification of queries, your code doesn't need to run to validate the type is correct.
I don't care to argue either way for tests vs static types (I write both frankly), just want the distinction to be clear.
Re: Litdb – type safe SQL for JavaScript/TS
#34Earlier quoted context omitted.
With tests? Or just a schema definition?
Tests are a type of runtime validation, they require literal execution of your code to perform the validation. This supports static verification of queries, your code doesn't need to run to validate the type is correct. I don't care to argue either way for tests vs static types (I write both frankly), just want the distinction to be clear.
Re: Litdb – type safe SQL for JavaScript/TS
#35I'm sure a lot of effort went into this, but I would rather raw dog my database layer with node-postgres/postgres.js and zod. The decorator heavy implementation is visually very busy. Also, I would not consider any SQL toolkit for serious use unless it comes with a sufficiently competent code generation accompaniment.
My feelings are similar and I cringe every time I see a "typesafe" db interface that doesn't run time validate the data.
Re: Litdb – type safe SQL for JavaScript/TS
#36But in my view there's still some scope for improvement in expressive-ness (compared to something like LINQ). A future area of exploration could be to add a parser to the mix, so that we can write:
const jane = db.contacts.one(c => c.email === janeEmail);
instead of: const jane = db.one($.from(Contact).where(c => $`${c.email} = ${janeEmail}`))!
The former is so naturally typesafe, without having to resort to advanced typescript tricks. I got half way there [1], before dropping the project due to work pressure (but many years back, so the code is quite stale). Parsers were viable with caching back then. These days, performance is not going to be problem whatever you do. If anyone is interested, happy to exchange notes.[1]: https://github.com/isotropy/isotropy-db/blob/master/src/test...
Re: Litdb – type safe SQL for JavaScript/TS
#37Earlier quoted context omitted.
litdb does include support for registering TypeConverters for mapping custom RDBMS types [1]. The drivers doesn't include any converters for custom RDBMS-specific types yet, but you should be able to register your own in your App or even better submit a PR to the postgres driver [2] so it'll work OOB (tho it'll be dependent on whatever postgres.js can be configured to support). [1] https://litdb.dev/customize#type-co…
Correct me if I'm misunderstanding, but this would allow me to register the desired conversion type for a basic postgres type, to and from JavaScript but not for the return value of a specific function or even a specific invocation of a function. PostGIS uses a lot of functions like ST_AsEWKT, ST_AsMVT or ST_AsGeoJSON [1] to marshal data. While ST_AsGeoJSON will always return "text", ideally you'd want an invocation…
[1] https://github.com/porsager/postgres
[2] https://github.com/litdb/litdb/discussions/categories/ideas
Re: Litdb – type safe SQL for JavaScript/TS
#38This looks great. But in my view there's still some scope for improvement in expressive-ness (compared to something like LINQ). A future area of exploration could be to add a parser to the mix, so that we can write: const jane = db.contacts.one(c => c.email === janeEmail); instead of: const jane = db.one ($.from(Contact).where(c => $`${c.email} = ${janeEmail}`))! The former is so naturally typesafe, without having to…
Your proposed API would require augmenting the driver with an application schema which would couple drivers to both litdb implementations and an application's schema where it would no longer be suitable for querying outside of your Application schema. Also not a fan of global Application schemas, i.e. litdb classes are self encapsulating so you could create a class at runtime and execute it without having to register it with a global application schema.
With that said you could implement a similar API to what you want with something like:
class AppDb {
constructor(public $:ReturnType) {}
contacts() { return this.$.from(Contact) }
}
const app = new AppDb($)
Which you could then use like: db.one(app.contacts.where(c => $`${c.email} = ${janeEmail}`))
I also prefer APIs to be SQL-like (i.e. instead of inventing a custom object model) where APIs and Typed Query Builders designed around SQL so it's intuitive how to construct queries and what SQL a query would execute. At the moment there's a clear separation of Query Builders which generates SQL + Params and the drivers which just executes them.BTW you don't need the generic type if you only have "1 table without a custom select", i.e. this will implicitly return a Contact instance:
const jane = db.one($.from(Contact).where(c => $`${c.email} = ${janeEmail}`))
Specifying `one` generic type is similar to `as Contact` inert type hint, i.e. it informs TS what the shape of the returned object is but doesn't change behavior, it's preferable to use `.into(Contact)` which explicitly returns a `Contact` instance. const jane = db.one($.from(Contact).where(c => $`${c.email} = ${janeEmail}`).into(Contact))Re: Litdb – type safe SQL for JavaScript/TS
#39This looks great. But in my view there's still some scope for improvement in expressive-ness (compared to something like LINQ). A future area of exploration could be to add a parser to the mix, so that we can write: const jane = db.contacts.one(c => c.email === janeEmail); instead of: const jane = db.one ($.from(Contact).where(c => $`${c.email} = ${janeEmail}`))! The former is so naturally typesafe, without having to…
For example,
import { contacts } from "db"
const jane = contacts.one(c => c.email === janeEmail)
Marco-like magic can be confusing, as we learned from Svelte. Especially when it's half-baked like the one you are suggesting.Another drawback is that you'll need to compile the code twice to run it: once with the custom parser and once with the TypeScript compiler.
Re: Litdb – type safe SQL for JavaScript/TS
#40This looks great. But in my view there's still some scope for improvement in expressive-ness (compared to something like LINQ). A future area of exploration could be to add a parser to the mix, so that we can write: const jane = db.contacts.one(c => c.email === janeEmail); instead of: const jane = db.one ($.from(Contact).where(c => $`${c.email} = ${janeEmail}`))! The former is so naturally typesafe, without having to…
It's an intentional decision that drivers are decoupled from the Application and query builder it executes, i.e. all driver APIs provide different ways of executing SQL + Params or a function that returns SQL + Params (Query Builder). This allows drivers to remain flexible to executing (non-litdb) SQL/Params or Query builders from a different library. Your proposed API would require augmenting the driver with an appl…
But my suggestion doesn't couple the driver and the Application/query builder. Here's a simplified example.
const db = {
// table() is a factory that returns (for example) PostgresTable
// or MySQLTable or even MongoTable based on config
customers: table()
}
This has some other interesting properties - for example, you could stub these tables with pure JS lists for tests. The broad applicability of this approach has been proven to work by EF being the defacto DB access method in the .Net world (over 15 years now).