Five Node.js Built-in APIs That Replace npm Packages
If you’re still installing npm packages for basic HTTP requests, file operations, or testing, you’re adding unnecessary dependencies to your Node.js projects. Modern Node.js (v20+) includes powerful built-in APIs that eliminate the need for many popular packages, reducing your bundle size, improving security, and simplifying maintenance.
Let’s explore five essential Node.js built-in APIs that can replace npm packages you’re probably using right now, with practical examples showing exactly how to make the switch.
Key Takeaways
- Native fetch API handles most HTTP requests without external dependencies since Node.js 18
- Built-in test runner provides zero-dependency testing solution for simple applications
- Modern fs methods support recursive operations and pattern matching natively
- crypto.randomUUID() generates secure UUIDs without additional packages
- WebSocket client support is available natively (experimental in Node.js 21+)
1. Global fetch() Replaces axios and node-fetch
The native fetch API, stable since Node.js 18, handles most HTTP requests without external dependencies. While packages like axios offer conveniences like interceptors and automatic retries, fetch() covers the majority of use cases.
Before (with axios):
const axios = require('axios');
const response = await axios.get('https://api.github.com/users/nodejs');
console.log(response.data);
After (native fetch):
const response = await fetch('https://api.github.com/users/nodejs');
const data = await response.json();
console.log(data);
For timeouts, use AbortController:
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);
const response = await fetch(url, { signal: controller.signal });
When to keep axios: Complex interceptor chains, progress tracking, or automatic request retries.
2. node:test Runner Replaces Jest and Mocha
The built-in test runner (stable and recommended in Node.js 20+) provides a zero-dependency testing solution perfect for libraries and simple applications.
Before (with Jest):
// Requires jest installation and configuration
describe('Math operations', () => {
test('adds numbers', () => {
expect(2 + 2).toBe(4);
});
});
After (native node:test):
import { test } from 'node:test';
import assert from 'node:assert';
test('adds numbers', () => {
assert.strictEqual(2 + 2, 4);
});
Run tests with: node --test or node --test --watch for automatic rerunning.
When to keep Jest: Snapshot testing, extensive mocking utilities, or code coverage reporting needs.
Discover how at OpenReplay.com.
3. File System Helpers Replace rimraf, mkdirp, and glob
Modern fs methods handle recursive operations and pattern matching natively.
Before (multiple packages):
const rimraf = require('rimraf');
const mkdirp = require('mkdirp');
const glob = require('glob');
await rimraf('dist');
await mkdirp('dist/assets');
const files = await glob('src/**/*.js');
After (native fs):
import { rm, mkdir } from 'node:fs/promises';
import { glob } from 'node:fs';
await rm('dist', { recursive: true, force: true });
await mkdir('dist/assets', { recursive: true });
// Note: glob() requires Node.js 22+
for await (const file of glob('src/**/*.js')) {
console.log(file);
}
These native Node.js features eliminate three dependencies with identical functionality.
4. crypto.randomUUID() Replaces uuid Package
Generate cryptographically secure UUIDs without the uuid package (161M weekly downloads).
Before:
const { v4: uuidv4 } = require('uuid');
const id = uuidv4();
After:
import { randomUUID } from 'node:crypto';
const id = randomUUID();
This built-in API generates RFC-4122 version 4 UUIDs with the same security guarantees as the uuid package.
When to keep uuid: Generating non-v4 UUID versions (v1, v3, v5).
5. WebSocket Client Replaces ws Package
Node.js includes a global WebSocket client (stable in Node.js 22+). It’s suitable for client use, but server-side WebSocket support still requires packages like ws.
Before (with ws):
const WebSocket = require('ws');
const ws = new WebSocket('wss://echo.websocket.org');
ws.on('open', () => ws.send('Hello'));
After (native WebSocket):
const ws = new WebSocket('wss://echo.websocket.org');
ws.onopen = () => ws.send('Hello');
ws.onmessage = (event) => console.log(event.data);
When to keep ws: Server-side WebSocket implementations or production use until the API stabilizes.
Making the Switch: Practical Considerations
Before replacing npm packages with native APIs, consider:
- Node.js version requirements: Ensure your production environment runs Node.js 20+ (ideally 22 LTS)
- Feature parity: Native APIs may lack advanced features (axios interceptors, Jest snapshots)
- Team familiarity: Factor in learning curves for different APIs
Start by replacing simple utilities like randomUUID() and file system operations. These offer immediate benefits with minimal risk. For more complex replacements like testing frameworks or HTTP clients, evaluate whether the native features meet your specific requirements.
Conclusion
These five Node.js built-in APIs demonstrate how the platform has evolved to reduce dependency on external packages. By leveraging native fetch, node:test, modern fs methods, crypto utilities, and WebSocket support, you can significantly reduce your project’s dependency footprint while maintaining—or even improving—functionality.
The key is knowing when native APIs suffice and when specialized npm packages still provide value. Start small, test thoroughly, and gradually migrate to native solutions where they make sense for your use case.
FAQs
Most features require Node.js 18 or newer. The fetch API needs v18+, node:test needs v20+, and glob requires v22+. Check your production environment's Node.js version before migrating from npm packages.
Not if you migrate carefully. Native APIs have different syntax than npm packages. Test thoroughly in development, update your code to match the new API patterns, and consider keeping packages for advanced features native APIs don't support.
Native APIs often perform better due to C++ implementations and no abstraction overhead. However, specialized packages may optimize specific use cases better. Benchmark your actual workload to make informed decisions about performance-critical code.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.