Skip to content
Laviyx Learn

Try

Node.js and Express course menu

Node.js path and URL Modules: Working with File Paths and URLs

Build file paths safely with path.join and path.resolve, take paths apart with basename, extname and dirname, use import.meta.dirname for files next to your code, understand Windows and POSIX separators, and parse URLs and query strings with the URL class.

Beginner7 min readUpdated
On this page

File paths look different on each operating system (C:\shop\data\orders.json on Windows, /home/asha/shop/data/orders.json on macOS and Linux), so building them by gluing strings together breaks easily. The path module builds and takes apart paths correctly for the system your code runs on.

Building paths#

JavaScript
import path from "node:path";

console.log(path.join("data", "orders", "2026.json"));
console.log(path.join("data", "orders", "..", "users.json"));    // ".." goes up a level
console.log(path.sep);
Output
data\orders\2026.json
data\users.json
\

path.join inserts the right separator and tidies up .. and repeated separators. path.resolve produces an absolute path, starting from the current working directory unless you give an absolute part:

JavaScript
import path from "node:path";

console.log(path.isAbsolute(path.resolve("data", "orders.json")));
console.log(path.resolve("/srv/app", "public", "index.html"));
Output
true
C:\srv\app\public\index.html

On Windows, a path starting with / means the root of the current drive, which is why C: appears; on macOS and Linux the result is /srv/app/public/index.html.

Taking paths apart#

JavaScript
import path from "node:path";

const file = path.join("uploads", "invoices", "invoice-2026-10.pdf");
console.log(path.basename(file));            // file name
console.log(path.basename(file, ".pdf"));    // without the extension
console.log(path.extname(file));             // extension
console.log(path.dirname(file));             // folder
console.log(path.parse(file).name);
Output
invoice-2026-10.pdf
invoice-2026-10
.pdf
uploads\invoices
invoice-2026-10

path.extname is handy for choosing a content type or rejecting uploads with unexpected extensions.

Files next to your code#

Relative paths in readFile are resolved from the current working directory (where you ran node), which may not be the script's folder. To read a file that sits beside the script, build the path from import.meta.dirname:

src/config.js (JavaScript)
import path from "node:path";

const configPath = path.join(import.meta.dirname, "config.json");
console.log(path.basename(path.dirname(configPath)), "/", path.basename(configPath));
console.log("Working directory is the script folder?", process.cwd() === import.meta.dirname);
Terminal
node src/config.js
Output
src / config.json
Working directory is the script folder? false

The script lives in src/, but node was started from the project folder, so the two differ. Building paths from import.meta.dirname works no matter where node is started. (In CommonJS, the equivalent is __dirname.)

Cross-platform paths#

  • Always use path.join / path.resolve instead of "data/" + name or hard-coded \.
  • In URLs, imports and package.json scripts, use /: they are not file-system paths.
  • path.posix and path.win32 force one style when you need it, for example to build URL-like paths on Windows:
JavaScript
import path from "node:path";

console.log(path.posix.join("images", "products", "lamp.jpg"));
console.log(path.win32.join("images", "products", "lamp.jpg"));
Output
images/products/lamp.jpg
images\products\lamp.jpg

URLs#

The global URL class parses web addresses into their parts:

JavaScript
const url = new URL("https://shop.example.com:8080/products/search?q=lamp&page=2#reviews");

console.log(url.protocol, url.hostname, url.port);
console.log(url.pathname);
console.log(url.searchParams.get("q"), url.searchParams.get("page"));
console.log(url.hash);
Output
https: shop.example.com 8080
/products/search
lamp 2
#reviews

searchParams reads and builds query strings, taking care of encoding special characters:

JavaScript
const url = new URL("https://shop.example.com/search");
url.searchParams.set("q", "steel bottle & cup");
url.searchParams.set("sort", "price");
url.searchParams.append("tag", "eco");
url.searchParams.append("tag", "kitchen");

console.log(url.href);
console.log(url.searchParams.getAll("tag"));
console.log(Object.fromEntries(new URLSearchParams("a=1&b=2")));
Output
https://shop.example.com/search?q=steel+bottle+%26+cup&sort=price&tag=eco&tag=kitchen
[ 'eco', 'kitchen' ]
{ a: '1', b: '2' }

Values from searchParams are always strings (or null if missing): convert numbers with Number(...).

Common mistakes#

Practice#

Practice exercise

Given the uploaded file names ["photo.JPG", "report.pdf", "notes.txt", "selfie.png"], print only the images (.jpg, .jpeg, .png, in any letter case) with their names lower-cased, and the URL https://cdn.example.com/uploads/<name> for each.

Show hint

Check path.extname(name).toLowerCase() against a Set of allowed extensions, and build the URL with new URL("uploads/" + name, "https://cdn.example.com/").

Show solution
JavaScript
import path from "node:path";

const images = new Set([".jpg", ".jpeg", ".png"]);
for (const name of ["photo.JPG", "report.pdf", "notes.txt", "selfie.png"]) {
  if (!images.has(path.extname(name).toLowerCase())) continue;
  const lower = name.toLowerCase();
  console.log(lower, new URL(`uploads/${lower}`, "https://cdn.example.com/").href);
}
Output
photo.jpg https://cdn.example.com/uploads/photo.jpg
selfie.png https://cdn.example.com/uploads/selfie.png

Key takeaway#

Next: Process, Arguments and Environment Variables.

Finished this lesson?