iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Your First Script

Writing your first useful Node script: shebang, parsing args, reading files, writing output, ESM and TypeScript options.

Node — first script

EXAMPLE
// ===== A minimal script (CommonJS) =====
// hello.js
console.log('hello,', process.argv[2] ?? 'world');

node hello.js Alex
// hello, Alex

// ===== ESM (the default in 2026) =====
// package.json: { "type": "module" }
// hello.mjs (or .js with type:module)
import { readFile } from 'node:fs/promises';
const text = await readFile('package.json', 'utf8');
console.log('size:', text.length);

// ===== Top-level await + parseArgs =====
// list.js
import { parseArgs } from 'node:util';
import { readdir } from 'node:fs/promises';

const { values, positionals } = parseArgs({
  args: process.argv.slice(2),
  options: {
    dir: { type: 'string', short: 'd', default: '.' },
    long: { type: 'boolean', short: 'l' },
  },
  allowPositionals: true,
});

const entries = await readdir(values.dir, { withFileTypes: true });
for (const e of entries) {
  console.log(values.long ? \`${e.isDirectory() ? 'd' : 'f'} ${e.name}\` : e.name);
}

// node list.js -d src -l

// ===== Shebang (run without 'node' prefix) =====
// At the top of the file:
#!/usr/bin/env node

// Make executable:
chmod +x list.js
./list.js -d src

// ===== Read stdin =====
// upper.js — reads stdin, upper-cases, writes stdout
import { stdin, stdout } from 'node:process';
let data = '';
for await (const chunk of stdin) data += chunk;
stdout.write(data.toUpperCase());

// echo 'hello' | node upper.js

// ===== Fetch (built-in since 18) =====
const res = await fetch('https://api.github.com/repos/nodejs/node');
const json = await res.json();
console.log(json.stargazers_count);

// ===== Spawn a child process =====
import { spawn } from 'node:child_process';
const git = spawn('git', ['status', '--short']);
git.stdout.pipe(process.stdout);
git.on('close', (code) => console.log('exit', code));

// ===== TypeScript option =====
// tsx (recommended) — run TS directly:
npm i -D tsx
npx tsx list.ts

// Node 22+ has a flag-gated --experimental-strip-types to run plain TS too.

// ===== Patterns to internalise =====
// - parseArgs over yargs / commander for small scripts
// - Top-level await with ESM keeps scripts flat
// - process.exit(1) on failures so shells see the error
// - Streams for big data; never read 10GB files into memory

// ===== Pitfalls =====
// - Mixing require + import in one file
// - Forgetting 'await' on async calls -> empty output
// - No error handling -> unhandled rejection warnings
// - Writing to stdout from logs -> mix with piped output; use stderr

Why it matters

A useful script is parseArgs + a fetch or fs call + a clear exit code. ESM + top-level await keeps the code flat; tsx adds TypeScript with zero ceremony. Scripts under 100 lines are where Node truly shines; promote to a real project only when complexity demands.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// hello.js
console.log('Hello, Node.js!');
// Run: node hello.js
Try it Yourself »

Discussion

Loading…