AlgoInsight

Q&A

How to list files present in a directory using Node.js

By AlgoInsight Team2026-06-07concurrencynodejsrecursionjavascript

Overview

Reading a directory is a common file-system task - building file explorers, processing uploads, scanning for config files. Node's fs module offers callback, promise, and sync variants. Modern code uses the promise API with async/await. The key refinements are distinguishing files from directories and recursing into subfolders.

graph TD
    A[List a directory] --> B["fs.promises.readdir(dir) - preferred async"]
    A --> C["fs.readdir(dir, cb) - callback"]
    A --> D["fs.readdirSync(dir) - blocking"]
    B --> E["{ withFileTypes: true } → Dirent: isFile()/isDirectory()"]
    E --> F[Recurse into subdirectories]
    style B fill:#1e1b4b,color:#fff

Promise API (recommended)

const fs = require("fs/promises");

async function list(dir) {
  const entries = await fs.readdir(dir);   // array of names (strings)
  console.log(entries);                     // ['a.txt', 'images', 'b.js']
}
list("./mydir");

Clean with async/await and non-blocking. (import { readdir } from "fs/promises" in ESM.)

Callback API

const fs = require("fs");
fs.readdir("./mydir", (err, files) => {
  if (err) throw err;
  console.log(files);
});

Synchronous API (scripts/startup only)

const fs = require("fs");
const files = fs.readdirSync("./mydir");   // blocks the event loop

Fine for CLI scripts or startup config, but avoid in servers - it blocks all other work while reading.

Files vs Directories: withFileTypes

By default you get just names. Pass { withFileTypes: true } to get Dirent objects that distinguish entry types:

const fs = require("fs/promises");

async function listDetailed(dir) {
  const entries = await fs.readdir(dir, { withFileTypes: true });
  for (const entry of entries) {
    if (entry.isDirectory())      console.log("DIR :", entry.name);
    else if (entry.isFile())      console.log("FILE:", entry.name);
  }
}

This avoids a separate fs.stat() call per entry, so it's more efficient.

Full Paths with path.join

readdir returns names relative to the directory, not full paths. Build them with path:

const path = require("path");
const fs = require("fs/promises");

const dir = "./mydir";
const names = await fs.readdir(dir);
const fullPaths = names.map(name => path.join(dir, name));

Recursive Listing (walk subdirectories)

const fs = require("fs/promises");
const path = require("path");

async function walk(dir) {
  const entries = await fs.readdir(dir, { withFileTypes: true });
  const files = [];
  for (const entry of entries) {
    const full = path.join(dir, entry.name);
    if (entry.isDirectory()) files.push(...await walk(full));  // recurse
    else files.push(full);
  }
  return files;
}

Modern Node (v18.17+/v20+) also supports a built-in recursive option: fs.readdir(dir, { recursive: true }).

Filtering

const jsFiles = (await fs.readdir(dir)).filter(f => f.endsWith(".js"));

For glob-style matching across trees, the glob package is common.

Why This Matters in Interviews

Node.js interviews use this to check fs familiarity and async discipline. Key points: prefer the promise API (fs/promises + await) over callbacks, and avoid readdirSync on servers (it blocks the event loop); use { withFileTypes: true } to distinguish files from directories without extra stat calls; build full paths with path.join (names are relative); and recurse (or use the built-in recursive option) for nested trees. Error handling (try/catch or callback err) is expected.

Common Pitfalls

References

More answers