Node.js

Node

Tutorial Node: Módulo url

Aprende a usar el módulo URL de Node.js para parsing de URLs y manipulación de parámetros con URLSearchParams en aplicaciones web.

Aprende Node y certifícate

Parsing de URLs

El parsing de URLs es una operación fundamental en el desarrollo de aplicaciones web con Node.js. Cuando trabajamos con servidores HTTP, necesitamos extraer y analizar los diferentes componentes de las URLs que recibimos en las peticiones para poder procesarlas correctamente.

Node.js proporciona el módulo url integrado que nos permite descomponer URLs complejas en sus partes constituyentes de manera sencilla y eficiente. Este módulo es especialmente útil cuando necesitamos acceder a elementos específicos como el protocolo, el host, la ruta, los parámetros de consulta o el fragmento.

Importación y uso básico

Para utilizar el módulo url, simplemente lo importamos usando la sintaxis estándar de Node.js:

import { URL } from 'url';

La clase URL es el constructor principal que utilizaremos para crear objetos URL y acceder a sus propiedades. Veamos un ejemplo básico:

import { URL } from 'url';

const miUrl = new URL('https://api.ejemplo.com/usuarios?activo=true&limite=10#seccion1');

console.log(miUrl.protocol); // 'https:'
console.log(miUrl.hostname); // 'api.ejemplo.com'
console.log(miUrl.pathname); // '/usuarios'
console.log(miUrl.search);   // '?activo=true&limite=10'
console.log(miUrl.hash);     // '#seccion1'

Propiedades principales del objeto URL

El objeto URL nos proporciona acceso directo a todas las partes componentes de una URL a través de propiedades específicas:

  • protocol: El esquema de la URL (http:, https:, ftp:, etc.)
  • hostname: El nombre del host sin el puerto
  • host: El nombre del host incluyendo el puerto si está presente
  • port: El número de puerto como string
  • pathname: La ruta del recurso
  • search: La cadena de consulta completa incluyendo el signo de interrogación
  • hash: El fragmento de la URL incluyendo el símbolo #
import { URL } from 'url';

const urlCompleta = new URL('https://tienda.com:8080/productos/electronicos?categoria=moviles&precio=500#reviews');

console.log('Protocolo:', urlCompleta.protocol);     // 'https:'
console.log('Host completo:', urlCompleta.host);     // 'tienda.com:8080'
console.log('Solo hostname:', urlCompleta.hostname); // 'tienda.com'
console.log('Puerto:', urlCompleta.port);            // '8080'
console.log('Ruta:', urlCompleta.pathname);          // '/productos/electronicos'
console.log('Consulta:', urlCompleta.search);        // '?categoria=moviles&precio=500'
console.log('Fragmento:', urlCompleta.hash);         // '#reviews'

Parsing en aplicaciones HTTP

En el contexto de un servidor HTTP, el parsing de URLs es especialmente útil para analizar las peticiones entrantes. Podemos combinar el módulo url con el módulo http para extraer información de las URLs de las peticiones:

import http from 'http';
import { URL } from 'url';

const servidor = http.createServer((peticion, respuesta) => {
  // Construimos la URL completa usando la URL base del servidor
  const urlCompleta = new URL(peticion.url, `http://${peticion.headers.host}`);
  
  console.log('Ruta solicitada:', urlCompleta.pathname);
  console.log('Parámetros de consulta:', urlCompleta.search);
  
  // Lógica de enrutamiento basada en la ruta
  if (urlCompleta.pathname === '/usuarios') {
    respuesta.writeHead(200, { 'Content-Type': 'application/json' });
    respuesta.end(JSON.stringify({ mensaje: 'Lista de usuarios' }));
  } else if (urlCompleta.pathname === '/productos') {
    respuesta.writeHead(200, { 'Content-Type': 'application/json' });
    respuesta.end(JSON.stringify({ mensaje: 'Lista de productos' }));
  } else {
    respuesta.writeHead(404, { 'Content-Type': 'text/plain' });
    respuesta.end('Recurso no encontrado');
  }
});

servidor.listen(3000, () => {
  console.log('Servidor ejecutándose en puerto 3000');
});

Manejo de URLs relativas

El constructor URL también puede manejar URLs relativas cuando proporcionamos una URL base como segundo parámetro. Esto es útil cuando trabajamos con rutas relativas en nuestras aplicaciones:

import { URL } from 'url';

const urlBase = 'https://api.miapp.com/v1/';

// URLs relativas se resuelven contra la base
const url1 = new URL('usuarios', urlBase);
const url2 = new URL('../admin/configuracion', urlBase);
const url3 = new URL('/publico/imagenes/logo.png', urlBase);

console.log(url1.href); // 'https://api.miapp.com/v1/usuarios'
console.log(url2.href); // 'https://api.miapp.com/admin/configuracion'
console.log(url3.href); // 'https://api.miapp.com/publico/imagenes/logo.png'

Validación de URLs

Una ventaja importante del constructor URL es que valida automáticamente la sintaxis de las URLs. Si intentamos crear un objeto URL con una cadena malformada, se lanzará una excepción:

import { URL } from 'url';

try {
  const urlValida = new URL('https://ejemplo.com/ruta');
  console.log('URL válida:', urlValida.href);
} catch (error) {
  console.error('URL inválida:', error.message);
}

try {
  const urlInvalida = new URL('esto-no-es-una-url-valida');
  console.log('Esta línea no se ejecutará');
} catch (error) {
  console.error('Error capturado:', error.message);
  // Error capturado: Invalid URL: esto-no-es-una-url-valida
}

Esta validación automática nos ayuda a detectar problemas en las URLs antes de procesarlas, evitando errores inesperados en nuestra aplicación.

Modificación de componentes URL

Los objetos URL son mutables, lo que significa que podemos modificar sus propiedades después de la creación. Esto es útil para construir URLs dinámicamente:

import { URL } from 'url';

const miUrl = new URL('https://api.ejemplo.com/usuarios');

// Modificamos diferentes componentes
miUrl.pathname = '/productos';
miUrl.search = '?categoria=electronica&disponible=true';
miUrl.hash = '#resultados';

console.log(miUrl.href);
// 'https://api.ejemplo.com/productos?categoria=electronica&disponible=true#resultados'

// También podemos cambiar el protocolo y el host
miUrl.protocol = 'http:';
miUrl.hostname = 'localhost';
miUrl.port = '3000';

console.log(miUrl.href);
// 'http://localhost:3000/productos?categoria=electronica&disponible=true#resultados'

URLSearchParams

Los parámetros de consulta en las URLs son elementos clave para transmitir información entre el cliente y el servidor. Node.js proporciona la clase URLSearchParams que nos permite manipular estos parámetros de forma intuitiva y eficiente, sin necesidad de realizar parsing manual de cadenas.

La clase URLSearchParams está diseñada específicamente para trabajar con la parte de consulta de las URLs (la sección que aparece después del signo ?). Esta herramienta nos permite agregar, modificar, eliminar y iterar sobre parámetros de consulta de manera programática.

Creación y uso básico

Podemos crear una instancia de URLSearchParams de varias formas diferentes:

import { URLSearchParams } from 'url';

// Desde una cadena de consulta
const params1 = new URLSearchParams('nombre=Juan&edad=25&activo=true');

// Desde un objeto
const params2 = new URLSearchParams({
  producto: 'laptop',
  marca: 'Dell',
  precio: '899'
});

// Vacío para construir dinámicamente
const params3 = new URLSearchParams();

Métodos principales para manipular parámetros

La clase URLSearchParams ofrece métodos intuitivos para gestionar parámetros de consulta:

Agregar parámetros:

import { URLSearchParams } from 'url';

const parametros = new URLSearchParams();

parametros.append('categoria', 'electronica');
parametros.append('disponible', 'true');
parametros.append('precio_min', '100');
parametros.append('precio_max', '500');

console.log(parametros.toString());
// 'categoria=electronica&disponible=true&precio_min=100&precio_max=500'

Obtener valores:

import { URLSearchParams } from 'url';

const consulta = new URLSearchParams('usuario=admin&rol=editor&activo=true&rol=viewer');

// Obtener un valor específico
console.log(consulta.get('usuario')); // 'admin'
console.log(consulta.get('activo'));  // 'true'

// Obtener todos los valores de un parámetro (útil para parámetros repetidos)
console.log(consulta.getAll('rol')); // ['editor', 'viewer']

// Verificar si existe un parámetro
console.log(consulta.has('usuario')); // true
console.log(consulta.has('email'));   // false

Modificar y eliminar parámetros:

import { URLSearchParams } from 'url';

const filtros = new URLSearchParams('categoria=ropa&talla=M&color=azul&descuento=10');

// Modificar un valor existente
filtros.set('descuento', '20');
filtros.set('categoria', 'zapatos');

// Eliminar parámetros
filtros.delete('color');

console.log(filtros.toString());
// 'categoria=zapatos&talla=M&descuento=20'

Integración con objetos URL

La combinación de URL y URLSearchParams nos proporciona un control completo sobre las URLs y sus parámetros de consulta:

import { URL } from 'url';

const miUrl = new URL('https://tienda.com/buscar?q=telefono');

// Acceder a los parámetros existentes
console.log(miUrl.searchParams.get('q')); // 'telefono'

// Agregar nuevos parámetros
miUrl.searchParams.append('marca', 'Samsung');
miUrl.searchParams.append('precio_max', '800');
miUrl.searchParams.set('ordenar', 'precio_asc');

console.log(miUrl.href);
// 'https://tienda.com/buscar?q=telefono&marca=Samsung&precio_max=800&ordenar=precio_asc'

// Limpiar todos los parámetros
miUrl.searchParams.delete('q');
miUrl.searchParams.delete('marca');

console.log(miUrl.href);
// 'https://tienda.com/buscar?precio_max=800&ordenar=precio_asc'

Procesamiento de parámetros en servidores HTTP

En aplicaciones web reales, frecuentemente necesitamos extraer y procesar los parámetros de consulta de las peticiones HTTP entrantes:

import http from 'http';
import { URL } from 'url';

const servidor = http.createServer((peticion, respuesta) => {
  const urlCompleta = new URL(peticion.url, `http://${peticion.headers.host}`);
  const parametros = urlCompleta.searchParams;
  
  if (urlCompleta.pathname === '/productos') {
    // Extraer parámetros de filtrado
    const categoria = parametros.get('categoria') || 'todas';
    const limite = parseInt(parametros.get('limite')) || 10;
    const ordenar = parametros.get('ordenar') || 'nombre';
    
    // Simular respuesta basada en parámetros
    const respuestaData = {
      categoria: categoria,
      limite: limite,
      ordenamiento: ordenar,
      total: 150,
      mensaje: `Mostrando ${limite} productos de la categoría '${categoria}'`
    };
    
    respuesta.writeHead(200, { 'Content-Type': 'application/json' });
    respuesta.end(JSON.stringify(respuestaData, null, 2));
    
  } else if (urlCompleta.pathname === '/buscar') {
    const termino = parametros.get('q');
    
    if (!termino) {
      respuesta.writeHead(400, { 'Content-Type': 'application/json' });
      respuesta.end(JSON.stringify({ error: 'Parámetro de búsqueda requerido' }));
      return;
    }
    
    respuesta.writeHead(200, { 'Content-Type': 'application/json' });
    respuesta.end(JSON.stringify({ 
      busqueda: termino,
      resultados: `Resultados para: ${termino}`
    }));
  }
});

servidor.listen(3000, () => {
  console.log('Servidor ejecutándose en puerto 3000');
  console.log('Prueba: http://localhost:3000/productos?categoria=electronica&limite=5&ordenar=precio');
  console.log('Prueba: http://localhost:3000/buscar?q=laptop');
});

Iteración sobre parámetros

URLSearchParams implementa el protocolo de iteración de JavaScript, lo que nos permite recorrer todos los parámetros de diferentes maneras:

import { URLSearchParams } from 'url';

const parametros = new URLSearchParams('nombre=Ana&edad=30&ciudad=Madrid&hobby=lectura&hobby=natacion');

// Iterar sobre todas las entradas clave-valor
console.log('=== Todas las entradas ===');
for (const [clave, valor] of parametros) {
  console.log(`${clave}: ${valor}`);
}

// Iterar solo sobre las claves
console.log('\n=== Solo claves ===');
for (const clave of parametros.keys()) {
  console.log(clave);
}

// Iterar solo sobre los valores
console.log('\n=== Solo valores ===');
for (const valor of parametros.values()) {
  console.log(valor);
}

// Usar forEach
console.log('\n=== Usando forEach ===');
parametros.forEach((valor, clave) => {
  console.log(`Parámetro ${clave} tiene el valor: ${valor}`);
});

Manejo de parámetros con valores múltiples

Algunos parámetros pueden aparecer múltiples veces en una URL. URLSearchParams maneja esta situación de forma elegante:

import { URLSearchParams } from 'url';

const filtros = new URLSearchParams();

// Agregar múltiples valores para el mismo parámetro
filtros.append('color', 'rojo');
filtros.append('color', 'azul');
filtros.append('color', 'verde');
filtros.append('talla', 'M');
filtros.append('talla', 'L');

console.log('Cadena completa:', filtros.toString());
// 'color=rojo&color=azul&color=verde&talla=M&talla=L'

// Obtener el primer valor
console.log('Primer color:', filtros.get('color')); // 'rojo'

// Obtener todos los valores
console.log('Todos los colores:', filtros.getAll('color')); // ['rojo', 'azul', 'verde']
console.log('Todas las tallas:', filtros.getAll('talla')); // ['M', 'L']

// Reemplazar todos los valores con set()
filtros.set('color', 'negro'); // Elimina todos los colores anteriores
console.log('Después de set:', filtros.getAll('color')); // ['negro']

Construcción dinámica de URLs con parámetros

Una aplicación práctica común es construir URLs dinámicamente basándose en condiciones o datos del usuario:

import { URL } from 'url';

function construirUrlBusqueda(baseUrl, criterios) {
  const url = new URL(baseUrl);
  
  // Agregar parámetros solo si tienen valor
  if (criterios.termino) {
    url.searchParams.set('q', criterios.termino);
  }
  
  if (criterios.categoria && criterios.categoria !== 'todas') {
    url.searchParams.set('categoria', criterios.categoria);
  }
  
  if (criterios.precioMin) {
    url.searchParams.set('precio_min', criterios.precioMin.toString());
  }
  
  if (criterios.precioMax) {
    url.searchParams.set('precio_max', criterios.precioMax.toString());
  }
  
  if (criterios.etiquetas && criterios.etiquetas.length > 0) {
    criterios.etiquetas.forEach(etiqueta => {
      url.searchParams.append('etiqueta', etiqueta);
    });
  }
  
  return url.href;
}

// Ejemplo de uso
const criteriosBusqueda = {
  termino: 'laptop gaming',
  categoria: 'electronica',
  precioMin: 500,
  precioMax: 1500,
  etiquetas: ['alta-gama', 'portatil', 'gaming']
};

const urlFinal = construirUrlBusqueda('https://tienda.com/buscar', criteriosBusqueda);
console.log(urlFinal);
// 'https://tienda.com/buscar?q=laptop+gaming&categoria=electronica&precio_min=500&precio_max=1500&etiqueta=alta-gama&etiqueta=portatil&etiqueta=gaming'
Aprende Node online

Otras lecciones de Node

Accede a todas las lecciones de Node y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a Node y certifícate

Ejercicios de programación de Node

Evalúa tus conocimientos de esta lección Módulo url con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.