October 2025 was a watershed month for the Node.js ecosystem. Between NodeConf EU and several high-profile virtual summits, the message from the core team and community leaders was unified: Simplification.
We are moving away from heavy build chains and complex scaffolding. The focus has shifted toward leveraging the platform’s native capabilities—features that have graduated from “experimental” to “stable” throughout late 2024 and 2025.
If you missed the livestreams, don’t worry. As a developer, you don’t need a trip report; you need to know how these changes affect your codebase today. In this article, we will bypass the fluff and implement the three biggest technical themes from October: Native TypeScript execution, the standardized SQLite module, and the new era of the Native Test Runner.
Prerequisites and Environment #
To follow this tutorial, you need to be on the cutting edge of the Long Term Support (LTS) or Current release lines. We are assuming a modern late-2025 environment.
- Node.js: Version 23.x or higher (we are using
v23.5.0for this demo). - Package Manager: npm (v11+) or pnpm.
- IDE: VS Code with the latest ESLint configuration.
We will create a small API service that demonstrates these features without a tsconfig.json build step and without third-party database drivers.
Setup #
Initialize a new project folder:
mkdir node-oct-2025-recap
cd node-oct-2025-recap
npm init -y
# We will use Fastify for the HTTP layer, but everything else will be native
npm install fastify1. The “No-Build” TypeScript Workflow #
One of the loudest rounds of applause in October came during the sessions discussing the stabilization of --strip-types. For years, running TypeScript meant ts-node, tsx, or a complex build pipeline involving tsc, swc, or esbuild.
In late 2025, for many backend microservices, you no longer need a transpiler. Node.js can now execute .ts files by stripping type annotations on the fly. This significantly reduces dev-dependencies and Docker image sizes.
The Code: Type-Safe Server without the Build #
Create a file named server.ts. Note the extension. We are writing TypeScript, but we won’t be compiling it.
// server.ts
import Fastify, { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import { DatabaseService } from './db.ts';
// Define an interface for our payload
interface UserPayload {
username: string;
email: string;
}
const app: FastifyInstance = Fastify({ logger: true });
const db = new DatabaseService();
app.post('/users', async (request: FastifyRequest<{ Body: UserPayload }>, reply: FastifyReply) => {
const { username, email } = request.body;
if (!username || !email) {
return reply.status(400).send({ error: 'Missing fields' });
}
try {
const id = db.createUser(username, email);
return reply.status(201).send({ id, username, status: 'created' });
} catch (err) {
request.log.error(err);
return reply.status(500).send({ error: 'Database error' });
}
});
const start = async () => {
try {
await app.listen({ port: 3000 });
} catch (err) {
app.log.error(err);
process.exit(1);
}
};
start();How to Run It #
In your package.json, update your scripts. Note the usage of the flag (depending on your specific minor version of Node 23, this might be enabled by default, but explicit is better for clarity):
"scripts": {
"start": "node --experimental-strip-types server.ts"
}This workflow removes the “compile” step entirely during development and simple deployments.
2. Native SQLite: The New Standard for Edge and Microservices #
Another major highlight from the October events was the maturity of node:sqlite. For years, better-sqlite3 was the gold standard. However, now that a synchronous SQLite driver is built directly into the Node.js binary, external C++ binding dependencies are becoming obsolete for many use cases.
This is massive for performance and security. It eliminates compilation errors during npm install on different architectures (like Apple Silicon vs. Linux Alpine).
The Code: Native Database Implementation #
Create a file named db.ts.
// db.ts
import { DatabaseSync } from 'node:sqlite';
export class DatabaseService {
private db: DatabaseSync;
constructor() {
// Open an in-memory database for this demo
// In production, you would point to a file: 'production.db'
this.db = new DatabaseSync(':memory:');
this.initialize();
}
private initialize() {
this.db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE,
email TEXT,
created_at INTEGER
)
`);
}
createUser(username: string, email: string): number | bigint {
const stmt = this.db.prepare('INSERT INTO users (username, email, created_at) VALUES (?, ?, ?)');
// Synchronous execution is incredibly fast for SQLite
const info = stmt.run(username, email, Date.now());
return info.lastInsertRowid;
}
getUser(id: number) {
const stmt = this.db.prepare('SELECT * FROM users WHERE id = ?');
return stmt.get(id);
}
}Performance Comparison #
Why switch to the native module? The speakers in October emphasized stability and install speed over raw throughput, although performance is comparable.
| Feature | node:sqlite (Native) |
better-sqlite3 |
sqlite3 (Legacy) |
|---|---|---|---|
| Installation | Instant (Built-in) | Requires node-gyp/prebuilds | Requires node-gyp |
| API Style | Synchronous | Synchronous | Asynchronous (Callback hell) |
| Dependencies | 0 | Low | Medium |
| Binary Size | Included in Node | Adds to node_modules | Adds to node_modules |
| Best Use Case | Microservices, Tooling, Edge | High-load Legacy Apps | Legacy Maintenance |
3. Visualizing the Architecture #
Before we move to testing, let’s visualize the simplified architecture we are building. The modern Node.js stack in late 2025 is remarkably flat.
4. The Native Test Runner: Goodbye Jest? #
The final key takeaway from October was the overwhelming adoption of node:test. The ecosystem is fatigued by complex Jest configurations. The native runner now supports mocking, code coverage, and reporters that rival third-party libraries.
We can now test our TypeScript application using the native runner, seamlessly integrated with the --experimental-strip-types flag.
The Code: Native Unit Tests #
Create test/app.test.ts.
// test/app.test.ts
import { test, describe, it, before, after } from 'node:test';
import assert from 'node:assert';
import { DatabaseService } from '../db.ts';
describe('Database Service Tests', () => {
let db: DatabaseService;
before(() => {
// Setup runs before tests
db = new DatabaseService();
});
it('should successfully create a user', () => {
const username = 'october_dev';
const email = '[email protected]';
const id = db.createUser(username, email);
// Check if ID is returned (SQLite returns number or bigint)
assert.ok(id, 'ID should be returned');
});
it('should retrieve the created user', () => {
const username = 'test_user_2';
const email = '[email protected]';
const id = db.createUser(username, email);
// Explicit casting for TS handling if needed, usually auto-inferred
const user = db.getUser(Number(id)) as { username: string };
assert.strictEqual(user.username, username);
});
// Example of using native mocking capabilities
it('should handle mock scenarios', (t) => {
const mockFn = t.mock.fn(() => {
return 100;
});
const result = mockFn();
assert.strictEqual(result, 100);
assert.strictEqual(mockFn.mock.callCount(), 1);
});
});To run this, update your package.json:
"scripts": {
"start": "node --experimental-strip-types server.ts",
"test": "node --experimental-strip-types --test test/app.test.ts"
}Running npm test will now execute your TypeScript tests immediately. No ts-jest, no Babel configuration, no waiting for compilation.
Production Considerations #
While the “October Hype” is real, as responsible engineers, we must look at the production readiness of these features.
- Strip Types is NOT Type Checking: Remember,
node --experimental-strip-types(or--strip-types) only removes the types so Node can run the code. It does not check for type errors. You still need to runtsc --noEmitin your CI/CD pipeline to catch type errors. - SQLite Concurrency: While
node:sqliteis fast, SQLite essentially locks the database during write operations. For read-heavy microservices, it is excellent. For write-heavy distributed systems, stick to Postgres. - Observability: Ensure you are using OpenTelemetry. The native runner and HTTP modules in Node 23+ have improved hooks for tracing, making it easier to debug these new patterns.
Conclusion #
The events of October 2025 have solidified a “Back to Basics” trend in the Node.js world. By leveraging Native TypeScript Support, node:sqlite, and node:test, we reduced our project’s boilerplate significantly.
We removed:
ts-node/tscbuild step for runtime.better-sqlite3/node-gypdependencies.jest/ts-jestand their config files.
The result is a cleaner, faster, and more maintainable codebase. As we head further into 2026, expect these native tools to become the default starting point for new Node.js projects.
Further Reading:
- Node.js Official Documentation on
node:sqlite - The
type-strippingRFC discussion on GitHub - Fastify TypeScript documentation