Live data from Hacker News

Litdb – type safe SQL for JavaScript/TS

litdb.dev

31–40 of 70 posts

Re: Litdb – type safe SQL for JavaScript/TS

#31

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.

With tests? Or just a schema definition?

Re: Litdb – type safe SQL for JavaScript/TS

#33

Earlier 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?

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

#34

Earlier 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.

[deleted]

Re: Litdb – type safe SQL for JavaScript/TS

#35
post #10

I'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.

Runtime validate at the database boundary? Isn't it better to just do runtime validation at the api boundary instead? Then any of the types that make it into the database should be mostly guaranteed by TS (and of course, whatever types are enforced by the database schema itself).

Re: Litdb – type safe SQL for JavaScript/TS

#36
This 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 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

#37
post #26
post #24

Earlier 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…

Type Converters lets you change the parameter value that's executed with the underlying provider (postgres driver uses postgres.js [1]) and what value is converted from the provider's resultset to your class property. So it would be up to whether the underlying provider can be configured to support the custom RDBMS type. If you leave a feature request [2] I can let you know when it's implemented or it's not possible with postgres.js when I get around to it.

[1] https://github.com/porsager/postgres

[2] https://github.com/litdb/litdb/discussions/categories/ideas

Re: Litdb – type safe SQL for JavaScript/TS

#38
post #36

This 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 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

#39
post #36

This 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…

That won't be possible without a lot of work. You'll need to tweak the TypeScript compiler to make the parser comprehend exported variables.

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

#40
post #38
post #36

This 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…

> 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 flexible to exec (non-litdb) SQL/Params or Query builders from a different library.

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).
Post reply on HN