SQLite Forum

How to integrate with Node.js?
Login

How to integrate with Node.js?

(1) By benjamingr on 2024-06-19 13:15:50 [source]

Hey,

First of all thank you for all your amazing work that's making my life better.

I'm Benjamin, I'm one of the people who works on Node.js (https://github.com/nodejs/node). Node.js is a JavaScript runtime.

Note that while I'm in a leadership position in the project - I'm speaking for myself rather than the whole project. I'm a volunteer and not representing any company.

We're looking to integrate SQLite directly into Node.js and are considering what API to expose. We've love to get your feedback and guidance regarding that.

Our options considered so far:

  • A synchronous API (like better-sqlite3) leveraging the fact sqlite runs in process.
  • An asynchronous API like node-sqlite3 which would let users do other work while SQLite blocks on I/O (for example if the file system is slow or there is a lot of contention).

Our execution model is generally non-blocking I/O (epoll / io_uring / iocp) using libuv (https://github.com/libuv/libuv) with a threadpool for APIs where we can't perform actions in a truly nonblocking matter.

The ask here is for guidance/advice - given SQLite's current execution model what API if any should we expose?

(2) By Stephan Beal (stephan) on 2024-06-19 17:28:33 in reply to 1 [link] [source]

I'm Benjamin, I'm one of the people who works on Node.js

And i'll be your counterpart here, as this project's "JS/WASM Guy," with the caveat that our JS focus is 100% browser-side. Our only use of node is indirect, it being an internal component of the Emscripten tool chain.

I'm speaking for myself rather than the whole project.

Similarly, this post will clearly differentiate between my personal opinions and those which we have internally discussed and come to a consensus on.

We're looking to integrate SQLite directly into Node.js and are considering what API to expose.

FWIW, when it comes to our own bindings, e.g. JavaScript and Java, it's our collective opinion that there neither can be, nor should be, One True Wrapper, for at least the following reasons:

  • Every sqlite wrapper makes trade-offs for flexibility and ease of use. For every 10 programmers, there are going to be 11 preferences with regards to such trade-offs.

  • Every level of abstraction added on top of the C API invariably cuts off access to one or more low-level features. This is often done to account for differing language semantics or to "round off the rough corners" to keep users from cutting themselves. Whatever the justifications, there will always be some percentage of users who bemoan the lack of any given capability which the C API provides.

Having multiple wrappers is, in our considered opinions, healthy for the ecosystem and provides the most all-around flexibility when selecting which set of trade-offs (compared to the C API) a project wants to use.

Anecdote: when constructing our JS and Java wrappers, step one was to create a "non-wrapper," i.e. a binding which is as close to the original C API as we can get it, given the semantic differences of each language. The differences between C and the idiomatic/popular usage of the target language play no role whatsoever in how the "non-wrapper" bindings are architected. The result of those efforts are APIs which are admittedly awkward to use, and highly non-idiomatic, for the target languages, but provide future higher-level wrappers full access to very nearly every feature of the C API.

An asynchronous API...

It is my personal, stubbornly-unwavering, and very likely unpopular opinion that attempting to force any 100% synchronous API to pretend to behave as if it's asynchronous is misled. Not only is it conceptually shoehorning a square peg into a round hole, but it's been my highly unscientific observation that users will, nine times of ten, "await" on such calls, making all of that async-related wrapping moot while also adding unnecessary noise to the client-level code.

As this project has not a single asynchronous API, we don't collectively have a well-considered stance on how such APIs "should" look for sqlite. If sqlite were to be rearchitected for its next hypothetical major evolutionary step, how it might be reshaped to directly support async communication would most definitely be considered, but that's a big hypothetical and not part of any roadmap.

Our execution model is generally non-blocking I/O (epoll / io_uring / iocp)...

Conversely, SQLite is patient, in that it wants I/O to complete before returning control to the user.

The ask here is for guidance/advice - given SQLite's current execution model what API if any should we expose?

Speaking solely for myself, i'm entirely partial to the "non-wrapper" approach, but will freely admit that only a diminishingly tiny percentage of JS programmers are going to want the freedom to, e.g., allocate and deallocate all of their memory themselves. However, there are node aficionados who could take a "non-wrapper" and build something entirely node-idiomatic on top of it, resulting in an API which that larger majority of the user base would find comfortable to work with.

With such a basis in place, when the JS ecosystem changes direction in 57 days (that's the average, isn't it? ;)), the ground work is there for The Next Wrapper without having to start from the ground up.

Given such a basis, your question of "which one" can arguably be answered with "why not both (built on top of a common layer)?"

That said: though we do provide high-level JS wrappers in our WASM pieces, we most definitely do not consider them to be The Solution to that problem, and it is our hope that folks will use the lower-level bindings to create high-level ones which better suit their preferences, vision, and project requirements.

(4) By benjamingr on 2024-06-20 11:40:18 in reply to 2 [link] [source]

Thanks for your detailed response <3 I've shared it with the rest of the project.

Note our goal isn't for the built in sqlite wrapper to be the "one and true" wrapper and I feel there is space for better-sqlite3 node-sqlite3 and other wrappers even once Node lands a sqlite3 binding.

I'm leaning towards:

  • Allow native addons to link against SQLite
  • Expose sync and async APIs (like we do for fs)

(3) By punkish on 2024-06-20 07:05:09 in reply to 1 [link] [source]

you probably already know how bun:sqlite https://bun.sh is doing it… might be lessons there.

fwiw, I have been using better-sqlite3 for a few years now, and am a very happy non-technical (aka non-knowledgable-about-c-and-other-internal-implementation) user

(5) By anonymous on 2024-06-20 19:09:11 in reply to 1 [link] [source]

What I had done is to call the SQLite command shell from a JavaScript code, and to do:

  • Implement sqlite3_complete in JavaScript (so that the SQLite command shell will agree with JavaScript about completed statements).

  • Set the output mode to the insert mode, and write JavaScript code to parse it.

  • Parse the SQL code to find host parameters, and replace them with the SQL representation of the values before sending them to SQLite.

Integrating SQLite into Node.js would avoid having to do stuff like that.

(6) By Joshua Wise (wisej12) on 2024-06-23 17:55:33 in reply to 1 [link] [source]

Hi there, I am the author of the "better-sqlite3" library (popular SQLite bindings for Node.js). I laid out some of my thoughts on GitHub, so I figured I'd link them here as well.

https://github.com/nodejs/node/issues/53264#issuecomment-2185223336

(7) By punkish on 2024-06-23 20:31:42 in reply to 6 [link] [source]

just wanted to say, thanks Joshua Wise @wisej12 for making an interface to SQLite that is just as delightful to use as SQLite. I hope the future node-integrated SQLite is also just as easy and a joy