Node.js API compatibility - Url
The url module in Node.js provides utilities for URL resolution and parsing. It allows developers to work with URLs in a structured way, making it easier to manipulate and extract information from them. This module is essential for web applications that need to handle URLs for routing, API requests, and more. It’s available in the Azion Runtime through Node.js compatibility, so functions can parse the incoming request URL, read query parameters, and build new URLs when proxying or redirecting traffic.
Example: URL parsing and formatting
The example below shows how to use the url module in a function:
/** * An example of using Node.js URL API in an Azion Function. * Support: * - Partially supported (Extended by library `url`) * @module runtime-apis/nodejs/url/main * @example * // Execute with Azion Bundler: * npx edge-functions build * npx edge-functions dev */import url from "node:url";/** * An example of using the Node.js URL API in an Azion Function. * @param {*} event * @returns {Promise<Response>} */const main = async (event) => { /** * URL globalThis object * https://developer.mozilla.org/en-US/docs/Web/API/URL * if use URL in the browser, don't need to import */ const newUrl = new URL("https://example.com/some/path?format=json&page=1"); console.log("newURL", newUrl);
const urlFormated = url.format({ protocol: "https", hostname: "example.com", pathname: "/some/path", query: { page: 1, format: "json", }, });
console.log(url.parse(urlFormated));
return new Response("Done!", { status: 200 });};
export default main;Example: Request URL parsing
Parse incoming request URLs to extract routing information:
// url module imported for legacy API demonstration// new URL() and URLSearchParams are available globallyimport url from "node:url";
const main = async (event) => { const request = event.request; const requestUrl = new URL(request.url);
// Extract URL components const urlInfo = { href: requestUrl.href, origin: requestUrl.origin, protocol: requestUrl.protocol, hostname: requestUrl.hostname, port: requestUrl.port, pathname: requestUrl.pathname, search: requestUrl.search, hash: requestUrl.hash };
// Parse query parameters const searchParams = requestUrl.searchParams; const queryParams = {}; for (const [key, value] of searchParams) { queryParams[key] = value; }
// ⚠️ url.parse() is deprecated since Node.js v11.0.0 // Prefer the WHATWG URL API (new URL()) shown above const parsedUrl = url.parse(request.url, true); console.log("Parsed URL:", parsedUrl);
// Get path segments const segments = requestUrl.pathname.split("/").filter(Boolean);
return new Response(JSON.stringify({ urlInfo, queryParams, segments, parsedQuery: parsedUrl.query }), { headers: { "Content-Type": "application/json" } });};
export default main;Example: Building URLs dynamically
Construct URLs for API calls and redirects:
import url from "node:url";
const main = async (event) => { // Build API URL with query parameters const apiUrl = new URL("https://api.example.com/v1/users"); apiUrl.searchParams.set("page", "1"); apiUrl.searchParams.set("limit", "10"); apiUrl.searchParams.set("sort", "name"); apiUrl.searchParams.set("active", "true");
console.log("API URL:", apiUrl.toString());
// Build redirect URL const redirectUrl = url.format({ protocol: "https", hostname: "app.example.com", pathname: "/dashboard", query: { ref: "login", session: "abc123" } });
// Resolve relative URL // ⚠️ url.resolve() is deprecated since Node.js v11.0.0 // Prefer: new URL(relativePath, baseUrl).href const baseUrl = "https://example.com/docs/"; const relativePath = "../api/reference"; const resolvedUrl = url.resolve(baseUrl, relativePath); console.log("Resolved URL (deprecated):", resolvedUrl);
// Modern alternative using URL constructor const modernResolvedUrl = new URL(relativePath, baseUrl).href; console.log("Resolved URL (modern):", modernResolvedUrl);
// Build URL from parts const parts = { protocol: "https:", hostname: "cdn.example.com", port: "443", pathname: "/assets/images/logo.png" }; const fullUrl = url.format(parts);
return new Response(JSON.stringify({ apiUrl: apiUrl.toString(), redirectUrl, resolvedUrl, builtUrl: fullUrl }), { headers: { "Content-Type": "application/json" } });};
export default main;Example: URLSearchParams manipulation
Work with query string parameters efficiently:
import url from "node:url";
const main = async (event) => { const requestUrl = new URL(event.request.url); const params = requestUrl.searchParams;
// Read parameters const page = params.get("page") || "1"; const limit = params.get("limit") || "10"; const sort = params.get("sort");
console.log("Page:", page, "Limit:", limit, "Sort:", sort);
// Check if parameter exists const hasFilter = params.has("filter"); console.log("Has filter:", hasFilter);
// Get all values for a parameter (multi-value) const tags = params.getAll("tag"); console.log("Tags:", tags);
// Modify parameters params.set("page", "2"); params.set("timestamp", Date.now().toString()); params.delete("debug");
// Iterate over parameters const allParams = []; for (const [key, value] of params) { allParams.push({ key, value }); }
// Sort parameters alphabetically params.sort();
// Convert to string const queryString = params.toString();
// Create new URLSearchParams from object const newParams = new URLSearchParams({ format: "json", version: "2", pretty: "true" });
return new Response(JSON.stringify({ originalParams: { page, limit, sort }, hasFilter, tags, modifiedParams: allParams, queryString, newParamsString: newParams.toString() }), { headers: { "Content-Type": "application/json" } });};
export default main;Example: URL validation and normalization
Validate and normalize user-provided URLs:
import url from "node:url";
const main = async (event) => { const request = event.request; // Note: In a real application, you might receive URLs from the request body // const { urls: customUrls } = await request.json().catch(() => ({}));
// URLs to validate const testUrls = [ "https://example.com/path", "http://localhost:3000/api?query=test", "HTTPS://EXAMPLE.COM/UPPERCASE", "https://example.com:443/standard-port", "https://user:pass@example.com/auth", "not-a-url", "ftp://files.example.com" ];
const results = testUrls.map(testUrl => { try { const parsed = new URL(testUrl); return { original: testUrl, valid: true, normalized: parsed.href, protocol: parsed.protocol, hostname: parsed.hostname, isHttps: parsed.protocol === "https:" }; } catch (error) { return { original: testUrl, valid: false, error: error.message }; } });
// Normalize a URL (lowercase hostname, default port removal) const normalizeUrl = (urlString) => { try { const parsed = new URL(urlString); parsed.hostname = parsed.hostname.toLowerCase();
// Remove default ports if (parsed.protocol === "https:" && parsed.port === "443") { parsed.port = ""; } if (parsed.protocol === "http:" && parsed.port === "80") { parsed.port = ""; }
return parsed.href; } catch { return null; } };
const normalizedUrls = testUrls.map(normalizeUrl);
return new Response(JSON.stringify({ validationResults: results, normalizedUrls }), { headers: { "Content-Type": "application/json" } });};
export default main;Example: URL routing and path matching
Use URL parsing for request routing:
import url from "node:url";
const main = async (event) => { const requestUrl = new URL(event.request.url); const pathname = requestUrl.pathname;
// Define routes const routes = { "/": { handler: "home", methods: ["GET"] }, "/api/users": { handler: "users", methods: ["GET", "POST"] }, "/api/users/:id": { handler: "userDetail", methods: ["GET", "PUT", "DELETE"] }, "/api/products": { handler: "products", methods: ["GET"] }, "/health": { handler: "health", methods: ["GET"] } };
// Match route const matchRoute = (path) => { // Exact match if (routes[path]) { return { ...routes[path], params: {} }; }
// Pattern match for :id style routes for (const [pattern, config] of Object.entries(routes)) { if (pattern.includes(":")) { const regex = new RegExp("^" + pattern.replace(/:\w+/g, "([^/]+)") + "$"); const match = path.match(regex); if (match) { const paramNames = pattern.match(/:\w+/g) || []; const params = {}; paramNames.forEach((name, i) => { params[name.slice(1)] = match[i + 1]; }); return { ...config, params }; } } }
return null; };
const matchedRoute = matchRoute(pathname); const method = event.request.method;
// Check if method is allowed const methodAllowed = matchedRoute?.methods?.includes(method);
// Build response const response = { pathname, method, matched: matchedRoute ? { handler: matchedRoute.handler, params: matchedRoute.params, methodAllowed } : null, query: Object.fromEntries(requestUrl.searchParams) };
return new Response(JSON.stringify(response, null, 2), { status: matchedRoute && methodAllowed ? 200 : matchedRoute ? 405 : 404, headers: { "Content-Type": "application/json" } });};
export default main;Supported APIs
| API | Status |
|---|---|
URL (global) | 🟢 Supported |
URLSearchParams | 🟢 Supported |
url.URL | 🟢 Supported |
url.URLSearchParams | 🟢 Supported |
url.format() | 🟢 Supported |
url.parse() | 🟡 Deprecated (use new URL()) |
url.resolve() | 🟡 Deprecated (use new URL(relative, base)) |
url.domainToASCII() | 🟡 Partially supported |
url.domainToUnicode() | 🟡 Partially supported |
url.fileURLToPath() | 🟡 Partially supported |
url.pathToFileURL() | 🟡 Partially supported |
url.urlToHttpOptions() | 🟡 Partially supported |