DEV Community

Cover image for 31 npm packages you can replace with Node.js APIs
David Turnbull for Lingo.dev

Posted on

31 npm packages you can replace with Node.js APIs

Introduction

When I started working at Lingo.dev, I noticed that some our dependencies were redundant and could be replaced with native Node.js APIs.

This made me wonder what other packages might not serve much of a purpose in modern codebases, so I've compiled this big ol' list of packages you may not need.

Caveat: The native APIs are not always direct replacements for existing packages, so as with any technical decision, the right choice depends on the context.

1. chalk (319.8M weekly downloads)

Node's util.styleText() adds colors and styles to console output. It supports standard ANSI styles like bold, underline, and colors. The API is simple and dependency-free.

import { styleText } from 'node:util';

console.log(styleText('green', '✔ done'));
console.error(styleText('bold', styleText('red', 'error')));
Enter fullscreen mode Exit fullscreen mode

To learn more, see util.styleText() (nodejs.org).

2. readable-stream (185.6M weekly downloads)

The core stream module is stable and feature-complete. It supports async iterators and includes Promise-based helpers in stream/promises. Most stream compatibility issues are resolved.

import { pipeline } from 'node:stream/promises';
import { createReadStream, createWriteStream } from 'node:fs';

await pipeline(
  createReadStream('in.txt'),
  createWriteStream('out.txt')
);
Enter fullscreen mode Exit fullscreen mode

To learn more, see Node.js Streams (nodejs.org).

3. safe-buffer (164.1M weekly downloads)

Modern Node uses safe buffer constructors by default. Buffer.from() and Buffer.alloc() prevent common security issues. The unsafe new Buffer() is deprecated.

const buf = Buffer.from('hello', 'utf8');
Enter fullscreen mode Exit fullscreen mode

To learn more, see Buffer (nodejs.org).

4. isarray (132.0M weekly downloads)

Array.isArray() is the standard way to check for arrays. It's been part of JavaScript since ES5.

if (Array.isArray(value)) {
  // handle array
}
Enter fullscreen mode Exit fullscreen mode

To learn more, see Array.isArray() (developer.mozilla.org).

5. fs-extra (113.3M weekly downloads)

Core fs now includes the convenience methods that fs-extra popularized. fs.cp handles recursive copies, fs.rm does robust removal, and fs.mkdir creates parent directories. Promise variants live in node:fs/promises.

import { cp, rm, mkdir } from 'node:fs/promises';

await mkdir('dist', { recursive: true });
await cp('assets', 'dist/assets', { recursive: true });
await rm('dist/tmp', { recursive: true, force: true });
Enter fullscreen mode Exit fullscreen mode

To learn more, see File System (nodejs.org).

6. uuid (161.3M weekly downloads)

crypto.randomUUID() generates RFC-4122 version 4 UUIDs. The implementation is cryptographically secure and requires no dependencies.

import { randomUUID } from 'node:crypto';

const id = randomUUID();
Enter fullscreen mode Exit fullscreen mode

To learn more, see crypto.randomUUID() (nodejs.org).

7. glob (208.5M weekly downloads)

Node includes fs.glob() for pattern matching in the filesystem. It returns an async iterator that yields matching paths. The API supports standard glob patterns.

import { glob } from 'node:fs/promises';

for await (const file of glob('src/**/*.ts')) {
  console.log(file);
}
Enter fullscreen mode Exit fullscreen mode

To learn more, see fsPromises.glob() (nodejs.org).

8. ws (117.3M weekly downloads)

WebSocket client support is built into Node as a global. The API is browser-compatible, so you can connect to WS and WSS endpoints without additional packages.

const ws = new WebSocket('wss://echo.websocket.events');

ws.onopen = () => ws.send('hello');
ws.onmessage = (ev) => console.log('echo:', ev.data);
Enter fullscreen mode Exit fullscreen mode

To learn more, see WebSocket (nodejs.org).

9. qs (100.7M weekly downloads)

WHATWG URLSearchParams constructs and serializes query strings safely. It pairs naturally with the URL constructor and fetch().

const params = new URLSearchParams({ q: 'node', page: '1' });
const url = new URL('https://api.example.com/search');
url.search = params.toString();

const res = await fetch(url);
Enter fullscreen mode Exit fullscreen mode

To learn more, see URLSearchParams (nodejs.org).

10. rimraf (97.9M weekly downloads)

Use fs.rm with { recursive: true, force: true } for cross-platform directory removal. It handles non-empty directories and missing paths gracefully.

import { rm } from 'node:fs/promises';

await rm('build', { recursive: true, force: true });
Enter fullscreen mode Exit fullscreen mode

To learn more, see fs.rm() (nodejs.org).

11. mkdirp (103.2M weekly downloads)

The fs.mkdir method accepts { recursive: true } to create parent folders. This works in both callback and Promise forms.

import { mkdir } from 'node:fs/promises';

await mkdir('var/log/app', { recursive: true });
Enter fullscreen mode Exit fullscreen mode

To learn more, see fs.mkdir() (nodejs.org).

12. pify (81.6M weekly downloads)

util.promisify() converts callback-based functions to Promises. It handles Node's callback convention automatically.

import { promisify } from 'node:util';
import { stat } from 'node:fs';

const statAsync = promisify(stat);
console.log(await statAsync('package.json'));
Enter fullscreen mode Exit fullscreen mode

To learn more, see util.promisify() (nodejs.org).

13. axios (65.8M weekly downloads)

Native fetch() covers most axios use cases. It handles JSON requests and responses automatically. Timeouts work through AbortController with abort signals.

const ac = new AbortController();
const t = setTimeout(() => ac.abort(), 8_000);

try {
  const res = await fetch('https://example.com/users', { signal: ac.signal });
  const users = await res.json();
  console.log(users);
} finally {
  clearTimeout(t);
}
Enter fullscreen mode Exit fullscreen mode

To learn more, see fetch() (nodejs.org).

14. async (67.0M weekly downloads)

JavaScript's async/await and Promise utilities eliminate most async flow-control needs. Methods like Promise.all() and Promise.race() handle concurrency patterns. The language has first-class support for asynchronous operations.

async function loadAll(urls) {
  const resps = await Promise.all(urls.map((u) => fetch(u)));
  return Promise.all(resps.map((r) => r.json()));
}
Enter fullscreen mode Exit fullscreen mode

To learn more, see Async functions (developer.mozilla.org).

15. node-fetch (74.3M weekly downloads)

The Fetch API is now global in Node.js. You get the same Web-standard API that browsers use. JSON parsing, streaming bodies, and AbortController work out of the box.

const res = await fetch('https://api.example.com/data');
if (!res.ok) throw new Error(res.statusText);
const json = await res.json();
Enter fullscreen mode Exit fullscreen mode

To learn more, see fetch() (nodejs.org).

16. object-assign (64.2M weekly downloads)

Object.assign() and spread syntax handle shallow object merging. Both are part of the JavaScript standard.

const target = { a: 1 };
Object.assign(target, { b: 2 });
// or: const merged = { ...objA, ...objB };
Enter fullscreen mode Exit fullscreen mode

To learn more, see Object.assign() (developer.mozilla.org).

17. performance-now (23.5M weekly downloads)

Node's perf_hooks.performance.now() provides high‑resolution timing. The API matches the Web Performance API and reports time in milliseconds with sub‑millisecond precision.

import { performance } from 'node:perf_hooks';

const t0 = performance.now();
// work
console.log('elapsed ms:', performance.now() - t0);
Enter fullscreen mode Exit fullscreen mode

To learn more, see performance.now() (nodejs.org).

18. array-flatten (37.0M weekly downloads)

Array.prototype.flat() flattens nested arrays to any depth. The flatMap() method combines mapping and flattening.

const nested = [1, [2, [3]]];
const flat = nested.flat(Infinity); // [1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

To learn more, see Array.flat() (developer.mozilla.org).

19. bluebird (31.9M weekly downloads)

Native Promises combined with util.promisify() replace custom promise libraries. Node's filesystem and stream APIs already offer Promise-based versions.

import { promisify } from 'node:util';
import { readFile } from 'node:fs';

const readFileAsync = promisify(readFile);
const txt = await readFileAsync('README.md', 'utf8');
Enter fullscreen mode Exit fullscreen mode

To learn more, see util.promisify() (nodejs.org).

20. url-parse (26.8M weekly downloads)

The WHATWG URL class handles URL parsing and manipulation. It's spec-compliant and includes URLSearchParams for query strings.

const url = new URL('https://user:pass@host:8080/path?a=1#frag');
url.searchParams.set('page', '2');
console.log(url.toString());
Enter fullscreen mode Exit fullscreen mode

To learn more, see URL class (nodejs.org).

21. abort-controller (25.2M weekly downloads)

AbortController and AbortSignal are Node globals. They work across multiple APIs including fetch, fs, and timers. This provides a unified cancellation mechanism.

const ac = new AbortController();
setTimeout(() => ac.abort('too slow'), 2000);

try {
  const res = await fetch('https://example.com/slow', { signal: ac.signal });
  console.log(await res.text());
} catch (err) {
  console.error('aborted:', err.name);
}
Enter fullscreen mode Exit fullscreen mode

To learn more, see AbortController (nodejs.org).

22. request (13.4M weekly downloads)

The deprecated request package is fully replaced by native fetch(). The Fetch API provides a cleaner, Promise-based interface for HTTP requests.

const res = await fetch('https://example.com/login', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ email, password })
});
const data = await res.json();
Enter fullscreen mode Exit fullscreen mode

To learn more, see fetch() (nodejs.org).

23. cross-fetch (20.0M weekly downloads)

Since fetch() is global in Node, cross-environment polyfills are unnecessary. The same code works in browsers and Node.

const res = await fetch('https://example.com/ping');
console.log(await res.text());
Enter fullscreen mode Exit fullscreen mode

To learn more, see fetch() (nodejs.org).

24. dotenv (63.3M weekly downloads)

Node now loads environment variables from .env files without external packages. The --env-file CLI flag and process.loadEnvFile() method handle this natively. This eliminates a dependency that appears in almost every Node.js project.

// programmatic
import process from 'node:process';

process.loadEnvFile('.env');
console.log(process.env.API_KEY);

// CLI
// $ node --env-file=.env app.mjs
Enter fullscreen mode Exit fullscreen mode

To learn more, see process.loadEnvFile() (nodejs.org).

25. form-data (100.9M weekly downloads)

The Web-standard FormData API is global in Node. It integrates directly with fetch() for multipart uploads. File attachments work through the Blob API.

const form = new FormData();
form.append('name', 'Ada');
form.append('file', new Blob(['hello']), 'greeting.txt');

await fetch('https://example.com/upload', { method: 'POST', body: form });
Enter fullscreen mode Exit fullscreen mode

To learn more, see FormData (nodejs.org).

26. minimist (80.5M weekly downloads)

Command-line argument parsing is built into Node through util.parseArgs(). It supports typed flags, short aliases, and positional arguments. The API returns a clean object structure for easy handling.

import { parseArgs } from 'node:util';

const { values, positionals } = parseArgs({
  args: process.argv.slice(2),
  options: {
    port: { type: 'string', short: 'p' },
    verbose: { type: 'boolean', short: 'v' }
  }
});

console.log(values, positionals);
Enter fullscreen mode Exit fullscreen mode

To learn more, see util.parseArgs() (nodejs.org).

27. nodemon (7.8M weekly downloads)

Node's --watch flag restarts your process when files change. It integrates with the standard CLI and Node's test runner.

node --watch server.mjs
Enter fullscreen mode Exit fullscreen mode

To learn more, see --watch flag (nodejs.org).

28. worker-farm (3.6M weekly downloads)

The worker_threads module handles parallel CPU-bound tasks. It supports SharedArrayBuffer and structured cloning. Workers can share memory and transfer data efficiently.

import { Worker } from 'node:worker_threads';

const worker = new Worker(new URL('./worker.js', import.meta.url), { workerData: 42 });
worker.on('message', (msg) => console.log('result:', msg));
Enter fullscreen mode Exit fullscreen mode

To learn more, see worker_threads (nodejs.org).

29. esm (3.5M weekly downloads)

ECMAScript modules are fully supported in Node. Set "type": "module" in package.json or use .mjs files. CommonJS interop is supported.

// package.json: { "type": "module" }
import { readFile } from 'node:fs/promises';
console.log(await readFile('file.txt', 'utf8'));
Enter fullscreen mode Exit fullscreen mode

To learn more, see ES Modules (nodejs.org).

30. left-pad (1.2M weekly downloads)

String padding is a language feature through padStart() and padEnd(). These methods handle padding with any character to any length.

const invoiceNo = String(42).padStart(6, '0'); // "000042"
Enter fullscreen mode Exit fullscreen mode

To learn more, see String.padStart() (developer.mozilla.org).

31. sleep (134.3K weekly downloads)

The timers/promises API provides Promise-based delays. It integrates with async/await and supports AbortSignal for cancellation.

import { setTimeout as delay } from 'node:timers/promises';

await delay(500); // sleep 500ms
Enter fullscreen mode Exit fullscreen mode

To learn more, see timers/promises (nodejs.org).

Top comments (1)

Collapse
 
maxprilutskiy profile image
Max Prilutskiy Lingo.dev
import { setTimeout as delay } from 'node:timers/promises';

await delay(500); // sleep 500ms
Enter fullscreen mode Exit fullscreen mode

This one is gold - I was still using setTimeouts 🤓