Node.js · Topic 1 of 8

Runtime and modules

Card 1 of 13

What you will learn

Node.js runs JavaScript outside the browser using Google's V8 engine plus an event loop for non-blocking I/O. Understanding its module systems (CommonJS and ES modules) and the order in which code runs explains most beginner surprises.

The runtime and its globals

V8 is the engine that compiles and runs your JavaScript. Node adds APIs the browser lacks, like the global process object. process.argv holds the node path, your script path, then extra arguments. process.env.PORT reads the PORT environment variable.

process.exit(1) stops immediately with a failure code, possibly cutting off pending async work. Setting process.exitCode = 1 lets the event loop finish first.

Example

The runtime and its globals

// node app.js hello world
console.log(process.argv.slice(2)); // ['hello', 'world']
const port = process.env.PORT || 3000;

process.exitCode = 1; // exit with failure after pending work finishes

CommonJS modules

require('./utils') runs the file once and returns its module.exports; later calls return the cached value, so state is shared. exports is just a shortcut to module.exports, so reassigning exports = {...} breaks the link. Assign to module.exports instead.

Each file is wrapped in a function giving you require, module, __filename and __dirname (the file's absolute directory). Top-level this is module.exports and a top-level return is legal. With circular requires, the second file sees the first one's partial exports.

Example

CommonJS modules

// counter.js
let count = 0;
module.exports = () => ++count; // shared via the module cache

// WRONG: exports = { add };   // does not change module.exports
module.exports = { add: (a, b) => a + b }; // correct

ES modules

A file is an ES module if it ends in .mjs or package.json has "type": "module". Use import and export; require throws ReferenceError there. Relative imports need the file extension, or you get ERR_MODULE_NOT_FOUND.

Top-level this is undefined; use import.meta.dirname (Node 20.11+) instead of __dirname. Exports are live bindings, so importers see later changes. Importing CommonJS gives its module.exports as the default. In CommonJS, import() returns a promise.

Example

ES modules

// package.json: { "type": "module" }
import { helper } from './util.js'; // extension required
import cfg from './config.cjs';      // default = module.exports
console.log(import.meta.dirname);

// In CommonJS:
const m = await import('./x.mjs');   // inside an async function

The event loop and execution order

Node runs your JavaScript on one thread; the event loop runs callbacks when async work finishes. A long synchronous loop blocks everything, including timers.

Order: sync code, then process.nextTick, then promise callbacks, then timers and I/O. Inside an I/O callback, setImmediate runs before setTimeout. Endless recursive nextTick calls delay timers until they stop.

Example

The event loop and execution order

console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
process.nextTick(() => console.log('T'));
console.log('D');
// A D T C B

Exit and errors

The 'exit' event is synchronous, so timers scheduled in it never fire. Unhandled promise rejections crash the process with a non-zero exit code (Node 15+), so catch errors.

Key points

  • V8 executes JavaScript; the event loop runs callbacks so Node never blocks while waiting.
  • require returns module.exports, cached after the first load; exports is only a shortcut to it.
  • ES modules need "type": "module" or .mjs, and relative imports need file extensions.
  • Run order: sync code, then nextTick, then promises, then timers and I/O.
  • process.exit() stops at once; process.exitCode lets pending work finish.
  • Unhandled promise rejections crash the process by default.

Common mistakes

  • Writing exports = {...} and getting an empty object from require.
  • Using require or __dirname inside an ES module.
  • Omitting the file extension in relative ESM imports.
  • Running long synchronous loops, which block timers and all other callbacks.

Lesson complete

Ready to check what you learned?

Swipe left or right, or use the buttons.