# Node.js API compatibility - Path

The `path` module in Node.js provides utilities for working with file and directory paths. It offers a set of functions that help developers manipulate and normalize paths, making it easier to handle file system operations across different operating systems. This module is available in the Azion Runtime through Node.js compatibility, and it's commonly used in functions to build or normalize paths, for example when routing requests or composing asset references.

---

## Example: Basic path operations

The example below shows how to use the `path` module in a function:

```javascript
/**
 * An example of using the Node.js Path API in an Azion Function.
 * Support:
 * - Partially supported (Extended by library `path-browserify`)
 * @module runtime-apis/nodejs/path/main
 * @example
 * // Execute with Azion Bundler:
 * npx edge-functions build
 * npx edge-functions dev
 */
import path from "node:path";

/**
 * An example of using the Node.js Path API in an Azion Function.
 * @param {*} event
 * @returns {Promise<Response>}
 */
const main = async (event) => {
  const pathName = path.join("/", "images", "image.jpg");
  console.log("Path", pathName);
  return new Response(`Path: ${pathName}`);
};

export default main;
```

---

## Example: URL routing with path

Use path methods to parse and construct URL paths for routing:

```javascript
import path from "node:path";

const main = async (event) => {
  const requestUrl = new URL(event.request.url);
  const pathname = requestUrl.pathname;

  // Extract path components
  const dirname = path.dirname(pathname);
  const basename = path.basename(pathname);
  const extname = path.extname(pathname);

  console.log("Directory:", dirname);
  console.log("Filename:", basename);
  console.log("Extension:", extname);

  // Build a new path for routing
  const apiPath = path.join("/api", "v1", "users", basename);
  console.log("API path:", apiPath);

  // Note: path.resolve() works for path string manipulation in edge runtimes,
  // but doesn't interact with an actual filesystem
  const absolutePath = path.resolve("/app", "public", "images", "logo.png");
  console.log("Absolute path:", absolutePath);

  return new Response(JSON.stringify({
    original: pathname,
    dirname,
    basename,
    extname,
    apiPath,
    absolutePath
  }), {
    headers: { "Content-Type": "application/json" }
  });
};

export default main;
```

---

## Example: Path normalization and parsing

Normalize paths and extract detailed path information:

```javascript
import path from "node:path";

const main = async (event) => {
  // Normalize messy paths
  const messyPath = "/users/../public/./images//photo.jpg";
  const normalizedPath = path.normalize(messyPath);
  console.log("Normalized:", normalizedPath);
  // /public/images/photo.jpg

  // Parse path into components (using edge-runtime-neutral paths)
  const filePath = "/assets/css/styles.css";
  const parsed = path.parse(filePath);
  console.log("Parsed:", parsed);
  // { root: '/', dir: '/assets/css', base: 'styles.css', ext: '.css', name: 'styles' }

  // Format path from object
  const formatted = path.format({
    root: "/",
    dir: "/data/documents",
    base: "report.pdf"
  });
  console.log("Formatted:", formatted);

  // Check if path is absolute
  const isAbs1 = path.isAbsolute("/assets/app.js");
  const isAbs2 = path.isAbsolute("../relative/path");
  console.log("Is absolute:", isAbs1, isAbs2);

  return new Response(JSON.stringify({
    normalizedPath,
    parsed,
    formatted,
    isAbsolute: { absolute: isAbs1, relative: isAbs2 }
  }), {
    headers: { "Content-Type": "application/json" }
  });
};

export default main;
```

---

## Example: Path joining for static assets

Build paths for static assets and content delivery:

```javascript
import path from "node:path";

const main = async (event) => {
  const request = event.request;
  const url = new URL(request.url);

  // Base directory for static files
  const staticBase = "/static";

  // Get requested file path
  const requestedPath = url.pathname.replace(/^\/static/, "");

  // Build full path
  const fullPath = path.join(staticBase, requestedPath);
  console.log("Full path:", fullPath);

  // Security: Validate that the resolved path stays within staticBase
  // This prevents path traversal attacks (e.g., /static/../sensitive/file)
  const resolvedPath = path.resolve(staticBase, requestedPath);
  if (!resolvedPath.startsWith(staticBase)) {
    return new Response("Forbidden", { status: 403 });
  }

  // Determine content type based on extension
  const ext = path.extname(fullPath).toLowerCase();
  const contentTypes = {
    ".html": "text/html",
    ".css": "text/css",
    ".js": "application/javascript",
    ".json": "application/json",
    ".png": "image/png",
    ".jpg": "image/jpeg",
    ".svg": "image/svg+xml"
  };

  const contentType = contentTypes[ext] || "application/octet-stream";
  console.log("Content-Type:", contentType);

  // Build relative path from current location
  const relativePath = path.relative("/static/images", fullPath);
  console.log("Relative path:", relativePath);

  return new Response(JSON.stringify({
    fullPath,
    resolvedPath,
    extension: ext,
    contentType,
    relativePath
  }), {
    headers: { "Content-Type": "application/json" }
  });
};

export default main;
```

---

## Example: Cross-platform path handling

Handle paths consistently across different environments:

```javascript
import path from "node:path";

const main = async (event) => {
  // Default path methods (platform-specific)
  const defaultJoin = path.join("folder", "subfolder", "file.txt");
  const defaultSep = path.sep;

  // Force POSIX-style paths (forward slashes)
  // Note: path.posix and path.win32 are properties of the main module
  const posixJoin = path.posix.join("folder", "subfolder", "file.txt");
  const posixSep = path.posix.sep;

  // Force Windows-style paths (backslashes)
  const win32Join = path.win32.join("folder", "subfolder", "file.txt");
  const win32Sep = path.win32.sep;

  console.log("Default join:", defaultJoin);
  console.log("POSIX join:", posixJoin);
  console.log("Windows join:", win32Join);

  // Get delimiter for PATH environment variable
  const delimiter = path.delimiter;
  console.log("Path delimiter:", delimiter);

  return new Response(JSON.stringify({
    default: { join: defaultJoin, sep: defaultSep },
    posix: { join: posixJoin, sep: posixSep },
    win32: { join: win32Join, sep: win32Sep },
    delimiter
  }), {
    headers: { "Content-Type": "application/json" }
  });
};

export default main;
```

---

## Supported APIs

| API | Status |
|-----|--------|
| `path.basename()` | 🟢 Supported |
| `path.dirname()` | 🟢 Supported |
| `path.extname()` | 🟢 Supported |
| `path.format()` | 🟢 Supported |
| `path.isAbsolute()` | 🟢 Supported |
| `path.join()` | 🟢 Supported |
| `path.normalize()` | 🟢 Supported |
| `path.parse()` | 🟢 Supported |
| `path.relative()` | 🟢 Supported |
| `path.resolve()` | 🟢 Supported |
| `path.sep` | 🟢 Supported |
| `path.delimiter` | 🟢 Supported |
| `path.posix` | 🟢 Supported |
| `path.win32` | 🟢 Supported |

:::note
While all `path` APIs are supported, they operate on path strings only. Azion Runtime doesn't provide access to a real filesystem, so methods like `path.resolve()` perform string manipulation without filesystem interaction. For URL path handling, consider using the Web `URL` API alongside `node:path`.
:::

---

## Related resources

- [Node.js `path` documentation](https://nodejs.org/api/path.html)