HDP115

Storage y Cookies

Aprende a persistir datos en el navegador utilizando diferentes mecanismos de almacenamiento del lado del cliente.

CE

Cristian Escalante

Última actualización: 23 de abril de 2025

javascript
programación web
desarrollo frontend

Storage y Cookies

Persiste datos en el navegador:

  • LocalStorage y SessionStorage
  • Almacenamiento y recuperación de datos
  • Cookies: creación, lectura y gestión
  • Limitaciones de cada método de almacenamiento
  • Seguridad en el almacenamiento del lado del cliente
  • IndexedDB para almacenamiento complejo
  • Cache API
  • Sincronización con servidores

Introducción al almacenamiento en el navegador

El almacenamiento en el navegador permite persistir datos del lado del cliente, lo que posibilita:

  • Guardar preferencias del usuario
  • Almacenar datos de sesión
  • Mejorar el rendimiento al cachear datos
  • Habilitar funcionalidades offline
  • Reducir peticiones al servidor

Existen diferentes mecanismos con distintos propósitos, capacidades y limitaciones:

// Ejemplo simple de almacenamiento
localStorage.setItem('preferencia', 'modo-oscuro');
const preferencia = localStorage.getItem('preferencia');
console.log(`Preferencia guardada: ${preferencia}`); // "Preferencia guardada: modo-oscuro"

Web Storage API

La API Web Storage proporciona dos mecanismos principales para almacenar datos en el navegador:

LocalStorage

Almacena datos sin fecha de expiración que persisten incluso después de cerrar el navegador.

// Guardar datos
localStorage.setItem('usuario', 'Juan');
localStorage.setItem('config', JSON.stringify({tema: 'oscuro', notificaciones: true}));

// Leer datos
const usuario = localStorage.getItem('usuario');
const config = JSON.parse(localStorage.getItem('config'));

// Eliminar un item específico
localStorage.removeItem('usuario');

// Eliminar todos los datos
localStorage.clear();

// Obtener el número de elementos
const numItems = localStorage.length;

// Acceder por índice
const clave = localStorage.key(0);
const valor = localStorage.getItem(clave);

SessionStorage

Similar a localStorage, pero los datos solo persisten durante la sesión actual del navegador.

// Guardar datos temporales de sesión
sessionStorage.setItem('carrito', JSON.stringify([{id: 1, nombre: 'Producto 1'}]));

// Leer datos
const carrito = JSON.parse(sessionStorage.getItem('carrito'));

// Los datos se eliminan automáticamente al cerrar la pestaña o ventana

Limitaciones de Web Storage

  • Capacidad limitada (generalmente 5-10MB por dominio)
  • Solo almacena strings (objetos y arrays deben ser convertidos con JSON)
  • API síncrona (puede bloquear el hilo principal)
  • No es adecuado para grandes cantidades de datos
  • Sin soporte para búsquedas complejas

Cookies

Las cookies son pequeños fragmentos de datos enviados desde un sitio web y almacenados en el navegador del usuario.

Creación y gestión de cookies

// Crear una cookie básica
document.cookie = "usuario=Juan";

// Crear una cookie con opciones adicionales
document.cookie = "preferencia=oscuro; expires=Fri, 31 Dec 2023 23:59:59 GMT; path=/; secure; samesite=strict";

// Leer todas las cookies
const todasLasCookies = document.cookie;
console.log(todasLasCookies); // "usuario=Juan; preferencia=oscuro"

// Función para crear cookies con facilidad
function setCookie(nombre, valor, diasExpiracion, path = '/') {
  const d = new Date();
  d.setTime(d.getTime() + (diasExpiracion * 24 * 60 * 60 * 1000));
  const expires = `expires=${d.toUTCString()}`;
  document.cookie = `${nombre}=${valor}; ${expires}; path=${path}; secure; samesite=strict`;
}

// Función para obtener el valor de una cookie específica
function getCookie(nombre) {
  const cookieArr = document.cookie.split(';');
  for(let i = 0; i < cookieArr.length; i++) {
    const cookiePair = cookieArr[i].split('=');
    const cookieName = cookiePair[0].trim();
    if(cookieName === nombre) {
      return decodeURIComponent(cookiePair[1]);
    }
  }
  return null;
}

// Función para eliminar una cookie
function deleteCookie(nombre) {
  document.cookie = `${nombre}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
}

Opciones de cookies

  • expires/max-age: Define cuando expira la cookie
  • path: Especifica la ruta donde la cookie es válida
  • domain: Especifica el dominio donde la cookie es válida
  • secure: La cookie solo se envía en conexiones HTTPS
  • samesite: Controla si la cookie se envía con solicitudes entre sitios
  • httpOnly: Evita acceso mediante JavaScript (solo configurable desde el servidor)

Limitaciones de cookies

  • Tamaño máximo de 4KB por cookie
  • Número limitado de cookies por dominio (generalmente 50-60)
  • Se envían en cada solicitud HTTP al servidor (overhead)
  • Problemas de privacidad y regulaciones (GDPR, ePrivacy, etc.)

IndexedDB

IndexedDB es una base de datos NoSQL orientada a objetos que permite almacenar grandes cantidades de datos estructurados, incluyendo archivos y blobs.

Características principales

  • API asíncrona (no bloquea el hilo principal)
  • Transaccional
  • Basada en objetos (no solo strings)
  • Soporta índices para búsquedas rápidas
  • Mayor capacidad de almacenamiento

Uso básico de IndexedDB

// Abrir o crear una base de datos
const request = indexedDB.open('miBaseDeDatos', 1);

// Manejar la creación/actualización de la estructura
request.onupgradeneeded = function(event) {
  const db = event.target.result;
  
  // Crear un almacén de objetos (similar a una tabla)
  const usuariosStore = db.createObjectStore('usuarios', { keyPath: 'id' });
  
  // Crear índices para búsquedas rápidas
  usuariosStore.createIndex('por_nombre', 'nombre', { unique: false });
  usuariosStore.createIndex('por_email', 'email', { unique: true });
};

// Manejar errores
request.onerror = function(event) {
  console.error('Error al abrir la base de datos:', event.target.error);
};

// Éxito al abrir la base de datos
request.onsuccess = function(event) {
  const db = event.target.result;
  
  // Agregar datos
  function agregarUsuario(usuario) {
    const transaction = db.transaction(['usuarios'], 'readwrite');
    const store = transaction.objectStore('usuarios');
    const request = store.add(usuario);
    
    request.onsuccess = function() {
      console.log('Usuario agregado con ID:', request.result);
    };
    
    request.onerror = function() {
      console.error('Error al agregar usuario:', request.error);
    };
  }
  
  // Leer datos
  function obtenerUsuario(id) {
    const transaction = db.transaction(['usuarios']);
    const store = transaction.objectStore('usuarios');
    const request = store.get(id);
    
    request.onsuccess = function() {
      if (request.result) {
        console.log('Usuario encontrado:', request.result);
      } else {
        console.log('Usuario no encontrado');
      }
    };
  }
  
  // Actualizar datos
  function actualizarUsuario(usuario) {
    const transaction = db.transaction(['usuarios'], 'readwrite');
    const store = transaction.objectStore('usuarios');
    const request = store.put(usuario);
    
    request.onsuccess = function() {
      console.log('Usuario actualizado');
    };
  }
  
  // Eliminar datos
  function eliminarUsuario(id) {
    const transaction = db.transaction(['usuarios'], 'readwrite');
    const store = transaction.objectStore('usuarios');
    const request = store.delete(id);
    
    request.onsuccess = function() {
      console.log('Usuario eliminado');
    };
  }
  
  // Ejemplo de uso
  agregarUsuario({ id: 1, nombre: 'Juan Pérez', email: 'juan@ejemplo.com', edad: 30 });
  obtenerUsuario(1);
};

Cache API

La Cache API es parte de los Service Workers y permite almacenar respuestas HTTP para su uso offline.

// Abrir o crear una caché
caches.open('v1').then(cache => {
  // Agregar recursos a la caché
  cache.add('/index.html');
  
  // Agregar múltiples recursos
  cache.addAll([
    '/styles/main.css',
    '/scripts/app.js',
    '/images/logo.png'
  ]);
  
  // Agregar una respuesta personalizada
  cache.put('/api/data', new Response(JSON.stringify({ data: 'valor' }), {
    headers: { 'Content-Type': 'application/json' }
  }));
});

// Buscar en la caché
caches.match('/index.html').then(response => {
  if (response) {
    console.log('Recurso encontrado en caché');
    return response;
  } else {
    console.log('Recurso no encontrado en caché');
    return fetch('/index.html');
  }
});

// Eliminar recursos de la caché
caches.open('v1').then(cache => {
  cache.delete('/index.html');
});

// Eliminar una caché completa
caches.delete('v1');

Comparación de mecanismos de almacenamiento

MecanismoCapacidadPersistenciaAPIUso ideal
LocalStorage5-10MBPermanenteSíncronaPreferencias, configuraciones
SessionStorage5-10MBSesiónSíncronaDatos temporales de sesión
Cookies4KBConfigurableSíncronaAutenticación, tracking
IndexedDB>50MBPermanenteAsíncronaGrandes conjuntos de datos estructurados
Cache APISegún dispositivoPermanenteAsíncronaRecursos HTTP para uso offline

Seguridad en el almacenamiento del lado del cliente

Mejores prácticas de seguridad

  1. No almacenar datos sensibles
    • Evitar contraseñas, tokens de larga duración, información personal sensible
  2. Validar datos almacenados antes de usarlos
    try {
      const datos = JSON.parse(localStorage.getItem('datos'));
      // Validar datos antes de usarlos
    } catch (e) {
      console.error('Datos inválidos en localStorage');
      localStorage.removeItem('datos');
    }
    
  3. Usar flags de seguridad en cookies
    • Secure: Solo HTTPS
    • HttpOnly: No accesible por JavaScript (solo desde servidor)
    • SameSite: Controla envío en peticiones cross-site
  4. Sanitizar datos antes de almacenarlos
    // Sanitizar datos antes de almacenar
    function sanitizarDatos(datos) {
      // Implementar lógica de sanitización
      return datosLimpios;
    }
    
  5. Cifrar datos sensibles si es necesario almacenarlos
    // Usando SubtleCrypto API (Web Crypto)
    async function cifrarDatos(datos, clave) {
      const encoded = new TextEncoder().encode(datos);
      const buffer = await crypto.subtle.encrypt(
        { name: 'AES-GCM', iv: window.crypto.getRandomValues(new Uint8Array(12)) },
        clave,
        encoded
      );
      return buffer;
    }
    

Sincronización con servidores

Estrategias de sincronización

  1. Sincronización bajo demanda
    async function sincronizarDatos() {
      const datosLocales = JSON.parse(localStorage.getItem('datos'));
      
      try {
        const respuesta = await fetch('/api/sincronizar', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(datosLocales)
        });
        
        const datosActualizados = await respuesta.json();
        localStorage.setItem('datos', JSON.stringify(datosActualizados));
        localStorage.setItem('ultimaSincronizacion', Date.now());
        
        return datosActualizados;
      } catch (error) {
        console.error('Error al sincronizar:', error);
        return null;
      }
    }
    
  2. Sincronización periódica
    function configurarSincronizacionPeriodica(intervaloMinutos) {
      // Sincronizar inmediatamente
      sincronizarDatos();
      
      // Configurar sincronización periódica
      setInterval(sincronizarDatos, intervaloMinutos * 60 * 1000);
    }
    
  3. Sincronización en segundo plano con Service Workers
    // En el Service Worker
    self.addEventListener('sync', event => {
      if (event.tag === 'sincronizar-datos') {
        event.waitUntil(sincronizarDatosEnSegundoPlano());
      }
    });
    
    // En la aplicación principal
    navigator.serviceWorker.ready.then(registration => {
      registration.sync.register('sincronizar-datos');
    });
    

Patrones de uso comunes

Almacenamiento en caché de datos de API

async function obtenerDatos(url) {
  // Verificar si hay datos en caché y si son recientes
  const cachedData = localStorage.getItem(url);
  const cachedTime = localStorage.getItem(`${url}_timestamp`);
  
  const ahora = Date.now();
  const expiracion = 60 * 60 * 1000; // 1 hora en milisegundos
  
  // Si hay datos en caché y no han expirado
  if (cachedData && cachedTime && (ahora - cachedTime < expiracion)) {
    return JSON.parse(cachedData);
  }
  
  // Si no hay datos en caché o han expirado, hacer petición
  try {
    const response = await fetch(url);
    const data = await response.json();
    
    // Guardar en caché
    localStorage.setItem(url, JSON.stringify(data));
    localStorage.setItem(`${url}_timestamp`, ahora);
    
    return data;
  } catch (error) {
    // Si hay error pero tenemos datos en caché, usarlos aunque estén expirados
    if (cachedData) {
      console.warn('Usando datos en caché expirados debido a error de red');
      return JSON.parse(cachedData);
    }
    throw error;
  }
}

Persistencia de estado de aplicación

// Gestor de estado simple
class EstadoApp {
  constructor(clave = 'app_estado') {
    this.clave = clave;
    this.estado = this.cargar();
  }
  
  cargar() {
    try {
      const estadoGuardado = localStorage.getItem(this.clave);
      return estadoGuardado ? JSON.parse(estadoGuardado) : {};
    } catch (e) {
      console.error('Error al cargar estado:', e);
      return {};
    }
  }
  
  guardar() {
    try {
      localStorage.setItem(this.clave, JSON.stringify(this.estado));
    } catch (e) {
      console.error('Error al guardar estado:', e);
    }
  }
  
  actualizar(cambios) {
    this.estado = { ...this.estado, ...cambios };
    this.guardar();
    return this.estado;
  }
  
  obtener(clave) {
    return clave ? this.estado[clave] : this.estado;
  }
  
  limpiar() {
    this.estado = {};
    localStorage.removeItem(this.clave);
  }
}

// Uso
const appEstado = new EstadoApp();
appEstado.actualizar({ tema: 'oscuro', idioma: 'es' });
console.log(appEstado.obtener('tema')); // 'oscuro'
Manipulación del DOM Avanzada
Técnicas sofisticadas para interactuar con el DOM.
Patrones de Diseño en JavaScript
Aprende sobre los patrones de diseño más utilizados en JavaS...

Conceptos Básicos de HTML

Aprende los conceptos básicos de HTML

Conceptos Básicos de CSS

Aprende los conceptos básicos de CSS

Conceptos Básicos SQL

Aprende los conceptos básicos de SQL

Conceptos Básicos de GIT

Aprende los conceptos básicos de GIT

Conceptos Básicos de Python

Aprende los conceptos básicos de Python

Conceptos Básicos de UML

Aprende los conceptos básicos de UML

Refuerzo Academico de Herramientas de Productividad 2025