languages
October 09, 2026 · 6 min read · 0 views

Node.js 23: Native SQLite Support, HTTP/2 Push Deprecation, and V8 13.0

Node.js 23 introduces native SQLite support, deprecates HTTP/2 Server Push, and upgrades to V8 13.0. Learn what changed and how to migrate your applications.

Node.js 23: What’s New in the Latest Release

Node.js 23.0.0 was released on October 17, 2024, bringing several significant features and breaking changes that every Node.js developer should understand. This release represents a critical evolution in the Node.js ecosystem, particularly around database support and performance improvements.

Native SQLite Support via node:sqlite

One of the most anticipated features in Node.js 23 is the introduction of a built-in SQLite module. This is a game-changer for developers who have previously relied on third-party packages like better-sqlite3 or sqlite3.

Why Built-in SQLite Matters

Previously, using SQLite in Node.js required managing native dependencies, which could be problematic in containerized environments, CI/CD pipelines, or edge deployments. The built-in node:sqlite module eliminates this friction by providing a stable, maintained interface directly from the Node.js runtime.

Getting Started with node:sqlite

The module is still experimental but can be enabled with the --experimental-sqlite flag:

node --experimental-sqlite app.js

Here’s a basic example:

import { DatabaseSync } from 'node:sqlite';

// Open or create a database
const db = new DatabaseSync(':memory:');

// Create a table
db.exec(`
  CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    email TEXT UNIQUE NOT NULL
  )
`);

// Insert data with a prepared statement
const insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
insert.run('Alice', '[email protected]');
insert.run('Bob', '[email protected]');

// Query data
const select = db.prepare('SELECT * FROM users WHERE id = ?');
const user = select.get(1);
console.log(user); // { id: 1, name: 'Alice', email: '[email protected]' }

// Bulk operations
const all = db.prepare('SELECT * FROM users');
const users = all.all();
console.log(users);

The synchronous API makes it straightforward to work with SQLite without callback hell or promise chains. However, for high-concurrency scenarios, consider offloading database operations to a worker thread:

import { Worker } from 'worker_threads';
import { DatabaseSync } from 'node:sqlite';

const worker = new Worker('./db-worker.js');

worker.on('message', (result) => {
  console.log('Result from worker:', result);
});

worker.postMessage({ query: 'SELECT * FROM users' });

In db-worker.js:

import { DatabaseSync } from 'node:sqlite';
import { parentPort } from 'worker_threads';

const db = new DatabaseSync('./app.db');

parentPort.on('message', (msg) => {
  const result = db.prepare(msg.query).all();
  parentPort.postMessage(result);
});

HTTP/2 Server Push Deprecation

Node.js 23 marks HTTP/2 Server Push as deprecated. Server Push was intended to allow servers to proactively send resources to clients before they are requested, reducing latency.

Why the Deprecation?

In practice, HTTP/2 Server Push has proven ineffective:

  • Cache state uncertainty: Servers don’t reliably know what clients already have cached.
  • Performance degradation: Pushing unnecessary resources can waste bandwidth and actually slow down page loads.
  • Modern alternatives: HTTP/3 with QUIC and HTTP Preload headers are more effective.

What This Means for Your Code

If you’re using pushStream() in your HTTP/2 server, you’ll see deprecation warnings:

const http2 = require('http2');
const fs = require('fs');

const server = http2.createSecureServer({
  key: fs.readFileSync('./server.key'),
  cert: fs.readFileSync('./server.cert')
});

server.on('stream', (stream, headers) => {
  // This will now trigger a deprecation warning
  if (headers[':path'] === '/') {
    stream.pushStream({ ':path': '/style.css' }, (err, pushStream) => {
      if (err) throw err;
      pushStream.respondWithFile('./style.css');
    });
  }

  stream.respondWithFile('./index.html');
});

server.listen(3000);

Alternative approach using Link headers (recommended):

server.on('stream', (stream, headers) => {
  if (headers[':path'] === '/') {
    stream.respond({
      ':status': 200,
      'link': '</style.css>; rel=preload; as=style',
      'content-type': 'text/html'
    });
    stream.end(fs.readFileSync('./index.html'));
  }
});

The Link header approach is more flexible, works across HTTP versions, and gives clients control over when to fetch resources.

V8 13.0 Upgrade: Performance and Memory Improvements

Node.js 23 bundles V8 13.0, the latest version of the JavaScript engine, which brings numerous optimizations.

Key V8 13.0 Improvements

1. Improved Garbage Collection

V8 13.0 introduces concurrent marking and sweeping, which reduces full garbage collection pause times. For applications processing large datasets or handling real-time workloads, this can provide measurable latency improvements.

2. Better Inlining Heuristics

The TurboFan compiler now makes smarter decisions about which functions to inline, leading to better code generation without increasing compilation time.

3. Reduced Memory Footprint

By default, V8 uses less memory for internal data structures, which is particularly noticeable in containerized or serverless environments.

Benchmarking the Improvements

Here’s a simple benchmark to compare performance across Node versions:

const crypto = require('crypto');

function benchmark(name, fn, iterations = 10000) {
  const start = performance.now();
  for (let i = 0; i < iterations; i++) {
    fn();
  }
  const end = performance.now();
  console.log(`${name}: ${(end - start).toFixed(2)}ms`);
}

benchmark('SHA-256 hashing', () => {
  crypto.createHash('sha256').update('test data').digest('hex');
});

benchmark('JSON parsing', () => {
  JSON.parse('{"name":"test","value":123}');
});

benchmark('Array operations', () => {
  const arr = Array.from({ length: 100 }, (_, i) => i);
  arr.map(x => x * 2).filter(x => x > 50);
});

Run this on both Node.js 22 and 23 to observe improvements.

Step-by-Step: Upgrading to Node.js 23

1. Check Your Current Version

node --version

2. Update Node.js

Using nvm (Node Version Manager):

nvm install 23
nvm use 23

Using Homebrew (macOS):

brew upgrade node

3. Update Dependencies

npm update
# or
yarn upgrade

4. Test Your Application

Run your test suite to catch any breaking changes:

npm test

5. Check for Deprecation Warnings

Run your app and look for deprecation warnings:

node --trace-deprecation app.js

Common Pitfalls and How to Avoid Them

Pitfall 1: Assuming node:sqlite Works in All Environments

The native SQLite module uses bindings that may not be available in all environments (e.g., some edge runtimes). Always have a fallback strategy or document environment requirements.

Pitfall 2: Ignoring Deprecation Warnings

HTTP/2 Server Push deprecation is a clear signal to migrate away. Set up CI/CD checks that fail on deprecation warnings:

node --trace-deprecation app.js 2>&1 | grep -i deprecation && exit 1

Pitfall 3: Performance Assumptions Without Testing

While V8 13.0 improves performance, your specific workload may benefit differently. Always profile before and after upgrades using tools like:

node --prof app.js
node --prof-process isolate-*.log > profile.txt

Pitfall 4: Breaking Changes in Dependencies

Some npm packages may have issues with Node.js 23. Check package changelogs and GitHub issues before upgrading in production.

Working with Kloubot Tools for Node.js Development

When building Node.js applications, especially with new features like node:sqlite, you’ll frequently work with configuration and data formats. The JSON Formatter helps validate database schemas and configuration files. If you’re working with environment variables or authentication tokens, the JWT Decoder is invaluable for debugging token issues.

For applications handling HTTP requests and webhooks, the Webhook Tester lets you capture and inspect HTTP payloads. When building APIs, the API Request Builder simplifies testing endpoints before deploying.

Why It Matters

Node.js 23 represents a maturation of the ecosystem. Native SQLite support reduces dependency complexity, the HTTP/2 deprecation guides developers toward better practices, and V8 13.0 ensures your applications run faster and use less memory. Together, these changes make Node.js more production-ready and cost-effective, particularly for cloud-based deployments.

The built-in SQLite module is particularly significant for edge computing and serverless environments where managing native dependencies is challenging. For most developers, this alone justifies an upgrade path.

Conclusion

Node.js 23 is a solid release worth upgrading to, especially if you’re building database-heavy applications or deploying to resource-constrained environments. Start by testing in development, address any deprecation warnings, and plan your migration carefully. The performance improvements and new features will pay dividends over time.

This post was generated with AI assistance and reviewed for accuracy.