Skip to content

Modules and npm

Scope: sharing code between files, package.json, and installing libraries with npm. Builds on Your First Program.

Exporting and importing (ES modules)

// math.mjs
export function add(a, b) {
  return a + b;
}
export const PI = 3.14159;
// main.mjs
import { add, PI } from "./math.mjs";
console.log(add(2, 3), PI);
  • export makes names available; import pulls them in.
  • Relative imports need the ./ prefix and the file extension (./math.mjs, not ./math) — a common stumbling block coming from other ecosystems.

A module can also have one default export:

// greeter.mjs
export default function greet(name) {
  return `Hello, ${name}!`;
}

// main.mjs
import greet from "./greeter.mjs";  // no braces for default

package.json: your project's ID card

npm init -y

Creates package.json:

{
  "name": "my-project",
  "version": "1.0.0",
  "type": "module",
  "dependencies": {}
}

"type": "module" is the magic line: it makes plain .js files behave as ES modules, so you can stop writing .mjs. (Without it, .js means CommonJS — the ambiguity from Your First Program.)

Installing libraries

npm install lodash

This:

  1. Downloads lodash into node_modules/
  2. Records it in package.json under dependencies
  3. Records exact versions in package-lock.json

Then use it:

import _ from "lodash";
console.log(_.chunk([1, 2, 3, 4], 2));  // [[1, 2], [3, 4]]

Rules of the road:

  • Never hand-edit node_modules/ — it's a cache, regenerated by npm install. (And never commit it to git — add it to .gitignore.)
  • Commit package-lock.json — it pins exact versions so every install is identical.
  • Check what you're installing. npm is enormous and minimally curated; typosquatting (malicious packages with near-miss names) is real. Prefer well-known packages with lots of weekly downloads.

npm scripts

{
  "scripts": {
    "start": "node main.js",
    "test": "node --test"
  }
}
npm start
npm test

Scripts are how Node projects define their standard commands. node --test runs the built-in test runner — no library needed for basic tests.

require() when you meet it

Older code uses CommonJS:

const { add } = require("./math.cjs");
module.exports = { add };

You don't need to write this — but you'll read it in older libraries, Stack Overflow answers, and Node's own docs history. require is synchronous and returns the exports object.

Check

  • [ ] You can export/import with named and default exports
  • [ ] You know what package.json, node_modules, and package-lock.json are each for
  • [ ] You can install a library and name two safety rules

Next: Error Handling.