Compatibilidade APIs Node.js - Url
O módulo url no Node.js fornece utilitários para resolução e análise de URLs. Ele permite que desenvolvedores trabalhem com URLs de forma estruturada, facilitando a manipulação e extração de informações delas. Este módulo é essencial para aplicações web que precisam lidar com URLs para roteamento, requisições de API e mais. Está disponível no Azion Runtime através da compatibilidade com Node.js, permitindo que functions analisem a URL da requisição recebida, leiam parâmetros de consulta e construam novas URLs ao fazer proxy ou redirecionar tráfego.
Exemplo: Análise e formatação de URL
O exemplo abaixo mostra como usar o módulo url em uma 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;Exemplo: Análise de URL de requisição
Analise URLs de requisições recebidas para extrair informações de roteamento:
// 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;Exemplo: Construindo URLs dinamicamente
Construa URLs para chamadas de API e redirecionamentos:
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;Exemplo: Manipulação de URLSearchParams
Trabalhe com parâmetros de query string de forma eficiente:
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;Exemplo: Validação e normalização de URL
Valide e normalize URLs fornecidas pelo usuário:
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;Exemplo: Roteamento de URL e correspondência de caminhos
Use análise de URL para roteamento de requisições:
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;APIs com suporte
| API | Status |
|---|---|
URL (global) | 🟢 Com suporte |
URLSearchParams | 🟢 Com suporte |
url.URL | 🟢 Com suporte |
url.URLSearchParams | 🟢 Com suporte |
url.format() | 🟢 Com suporte |
url.parse() | 🟡 Depreciado (use new URL()) |
url.resolve() | 🟡 Depreciado (use new URL(relative, base)) |
url.domainToASCII() | 🟡 Parcialmente suportado |
url.domainToUnicode() | 🟡 Parcialmente suportado |
url.fileURLToPath() | 🟡 Parcialmente suportado |
url.pathToFileURL() | 🟡 Parcialmente suportado |
url.urlToHttpOptions() | 🟡 Parcialmente suportado |