File Stats & Metadata
Beyond a file's contents, the filesystem tracks metadata: its size, type (file/directory/symlink), timestamps, and permissions. fs.stat retrieves all of it as a Stats object. This is how you answer "how big is it?", "is this a directory?", and "when was it last modified?" — and how you check existence without the racy exists.
Getting stats
import { stat } from 'node:fs/promises'
const s = await stat('report.pdf')
s.size // bytes
s.isFile() // true
s.isDirectory() // false
s.mtime // Date — last modified
s.birthtime // Date — createdThe Stats object
Member | Meaning |
|---|---|
| File size in bytes |
| Regular file? |
| Directory? |
| Symlink? (only via |
| Last content modification (Date / epoch ms) |
| Last access time |
| Last inode change (permissions, rename) — not creation |
| Creation time (where the OS supports it) |
| Permission bits (see below) |
stat vs lstat vs fstat
Function | Follows symlinks? | Use for |
|---|---|---|
| Yes — stats the target | Normal case |
| No — stats the link itself | Detecting symlinks |
| N/A — stats an open file descriptor | After you opened a file |
Existence check — the right way
There's no good "does it exist?" boolean — and that's intentional. Use access (or just attempt your real operation and catch ENOENT):
import { access, constants } from 'node:fs/promises'
async function exists(path) {
try { await access(path); return true }
catch { return false }
}
// Check specific permissions:
await access('script.sh', constants.R_OK | constants.X_OK) // readable & executable?Reading permissions from mode
The mode field packs the Unix permission bits. Mask and format them to the familiar octal:
import { stat } from 'node:fs/promises'
const s = await stat('script.sh')
const octal = (s.mode & 0o777).toString(8)
console.log(octal) // e.g. '755'755
A practical example: human-readable size
import { stat } from 'node:fs/promises'
function formatBytes(n) {
const units = ['B', 'KB', 'MB', 'GB', 'TB']
let i = 0
while (n >= 1024 && i < units.length - 1) { n /= 1024; i++ }
return `${n.toFixed(1)} ${units[i]}`
}
const { size } = await stat('video.mp4')
console.log(formatBytes(size)) // e.g. '847.3 MB'