AL+

Language: RO (română) EN (English)

Email us: paul@alplustech.com

SQLite in Node is a database decision before a module decision

Choose SQLite for the application's write pattern and operating model, then assess Node's binding separately, including backups, blocking work and recovery.

Citiți articolul în română

An embedded database can be a sensible first choice for a business application running on one server. The deciding questions concern concurrent writes, deployment and recovery. Whether the runtime ships a binding is a separate decision. SQLite's suitability does not begin or end with the stability label on node:sqlite.

Node 22.5.0 made that separation worth discussing on 17 July 2024. Looking back from November 2026, the useful response is to choose a database for the application's operating model and then choose a supported way to access it. A standard-library import is not a production acceptance test.

What actually arrived in July 2024

The Node 22.5.0 release notes record the addition of node:sqlite. It was experimental and required --experimental-sqlite, documented in the version-specific command-line reference. This was a Current release, not an announcement that every supported Node installation already included a stable binding.

The 22.5.0 API reference exposes DatabaseSync and StatementSync. Their synchronous behavior matters in a request-serving process: database work occupies the calling thread. Do not read the module's presence as a promise that every query can run without delaying other work on that thread.

Later documentation has different status labels. The pinned 24.11.0 reference says active development; the living API page retrieved for this article says release candidate and records flag removal in later versions. Neither label makes the original July API stable. For an actual deployment, check the documentation shipped for the exact runtime you will operate.

Count writers and deployment boundaries

SQLite's own appropriate-use guidance distinguishes embedded local storage from a client/server database. It supports many web application uses while identifying high write concurrency and direct access from multiple computers as reasons to consider a server database. A small customer count alone cannot answer those questions.

Consider an illustrative internal application that records appointments on one server. Short transactions with modest overlapping writes may fit an embedded database well. A different application with several workers continuously modifying the same records may face a very different constraint even if fewer people use its interface. These examples illustrate the decision, not measured capacity limits.

SQLite permits one writer at a time. Its write-ahead logging documentation explains how WAL lets readers and a writer proceed concurrently while retaining that single-writer limit. WAL also requires processes to be on the same host; it is not a way to turn a shared network file into a general multi-server database.

Examine how long transactions hold the write path and what callers do when it is busy. Keep remote API calls outside transactions where the business operation permits. Decide whether the application needs a queue, a shorter transaction or a client/server database. “SQLite scales” and “SQLite cannot scale” are both too broad to substitute for the actual contention pattern.

Built-in and external bindings have different trade-offs

A built-in module can reduce an external installation dependency. It also ties API availability to the runtime versions you support. An established external binding may already meet the application's requirements and its release policy. There is no reason to replace a working binding merely to shorten the dependency list.

For example, better-sqlite3's own repository describes a synchronous API and worker-thread support for large or slow queries. That is relevant context, not a benchmark proving it better than the built-in module. Compare required capabilities, installation targets and the maintenance policy for the versions under consideration.

For either synchronous interface, review expensive queries and where they execute. A worker can move work off the request thread, but introduces its own communication and connection design. No binding removes the need to inspect query plans, transaction length and application behavior under overlapping requests.

Keep migrations explicit too. A database file on disk still has a schema shared by application versions. Decide which version may change it and whether an older application can run after that change. Rolling the application back is insufficient if the schema no longer supports it.

The simpler database still needs an operator

A single file is easy to recognize and easy to misunderstand. In WAL mode, committed state can involve the accompanying WAL file. Copying only the main database while it is active is not a backup procedure to assume safe. SQLite provides an online backup API for consistent snapshots; choose a method compatible with the application's actual binding and deployment.

Specify how much recent work the business can lose and how long restoration may take. Store recoverable copies outside the failed server's storage boundary, protect their access and rehearse restoration into a separate instance. Verify the application can use the restored schema and records. The fact that a copy operation completed is less useful than a demonstrated recovery.

The strongest argument for PostgreSQL is operational fit. A team may already have a managed service with backups, access controls and recovery procedures it understands. Several application servers or sustained write contention may make it the simpler overall choice. Avoid paying for a future migration in advance when SQLite fits, but do not introduce an unfamiliar operating model solely to avoid running a database service.

No load test or binding comparison was performed for this article. There is no universal user threshold at which its recommendation changes. Put the deployment diagram, expected overlapping writes and restoration requirements into a custom software brief. Choose SQLite when those conditions fit, and assess the binding against that decision rather than treating its import path as the architecture.