Understanding Cookies in JavaScript

A Deep Dive into Browser Cookies: From Basics to Advanced Usage

What Are Cookies? A Real-World Analogy

Imagine you're a regular at your local café. Each time you visit, the barista hands you a loyalty card that keeps track of your favorite drinks and how many visits you've made. When you return, you show this card, and the barista knows exactly how to serve you. This is exactly how cookies work in web browsers!

Just like that loyalty card, cookies are small pieces of information that websites store on your computer. They help websites remember who you are and what you prefer, making your browsing experience more personal and convenient. Let's explore how they work and how to use them effectively.

Real-World Applications of Cookies

Let's explore some common scenarios where cookies prove invaluable:

// User Preferences Manager
class UserPreferencesManager {
    constructor() {
        this.cookieManager = CookieManager;
    }

    // Save user's theme preference
    setThemePreference(theme) {
        this.cookieManager.setCookie('userTheme', theme, 30); // 30 days
        this.applyTheme(theme);
    }

    // Get and apply saved theme
    loadThemePreference() {
        const savedTheme = this.cookieManager.getCookie('userTheme');
        if (savedTheme) {
            this.applyTheme(savedTheme);
        }
        return savedTheme || 'light'; // Default theme
    }

    // Apply theme to document
    applyTheme(theme) {
        document.body.className = theme;
        console.log('Applied theme:', theme);
    }
}

// Shopping Cart Manager using Session Cookies
class ShoppingCartManager {
    constructor() {
        this.cookieManager = CookieManager;
    }

    // Add item to cart
    addToCart(item) {
        const cart = this.getCart();
        cart.push(item);
        
        // Save as session cookie (no expiration)
        document.cookie = `cart=${JSON.stringify(cart)}; path=/`;
        
        console.log('Added to cart:', item);
    }

    // Get current cart contents
    getCart() {
        const cartData = this.cookieManager.getCookie('cart');
        return cartData ? JSON.parse(cartData) : [];
    }

    // Clear the cart
    clearCart() {
        this.cookieManager.deleteCookie('cart');
        console.log('Cart cleared');
    }
}

Advanced Cookie Techniques

Let's explore some sophisticated cookie management patterns:

// Enhanced Cookie Manager with Security Features
class SecureCookieManager extends CookieManager {
    static setSecureCookie(name, value, options = {}) {
        const {
            days = 7,
            path = '/',
            secure = true,
            sameSite = 'Strict'
        } = options;

        // Calculate expiration
        const expires = new Date(
            Date.now() + days * 24 * 60 * 60 * 1000
        ).toUTCString();

        // Build cookie string with security attributes
        const cookieString = [
            `${name}=${encodeURIComponent(value)}`,
            `expires=${expires}`,
            `path=${path}`,
            'SameSite=' + sameSite
        ];

        // Add secure flag for HTTPS
        if (secure && window.location.protocol === 'https:') {
            cookieString.push('Secure');
        }

        // Set the cookie
        document.cookie = cookieString.join('; ');
    }

    // Validate cookie values before setting
    static validateAndSetCookie(name, value, validator) {
        if (validator(value)) {
            this.setSecureCookie(name, value);
            return true;
        }
        console.error('Invalid cookie value:', value);
        return false;
    }

    // Get all cookies as an object
    static getAllCookies() {
        const cookiesObj = {};
        document.cookie.split(';').forEach(cookie => {
            const [name, value] = cookie.trim().split('=');
            cookiesObj[name] = decodeURIComponent(value);
        });
        return cookiesObj;
    }
}

// Example Usage with Authentication
class AuthManager {
    constructor() {
        this.cookieManager = SecureCookieManager;
    }

    setAuthToken(token) {
        this.cookieManager.setSecureCookie('authToken', token, {
            days: 1,  // Short expiration for security
            secure: true,
            sameSite: 'Strict'
        });
    }

    getAuthToken() {
        return this.cookieManager.getCookie('authToken');
    }

    logout() {
        this.cookieManager.deleteCookie('authToken');
    }

    // Validate token format before setting
    validateToken(token) {
        return /^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/.test(token);
    }
}

Debugging and Developer Tools

Understanding how to inspect and debug cookies is crucial for development:

// Cookie Debugging Utility
class CookieDebugger {
    static inspectCookie(name) {
        const cookie = document.cookie
            .split(';')
            .find(c => c.trim().startsWith(name + '='));
            
        if (cookie) {
            console.group(`Cookie: ${name}`);
            console.log('Raw value:', cookie);
            console.log('Decoded value:', 
                decodeURIComponent(cookie.split('=')[1]));
            console.groupEnd();
        } else {
            console.log(`Cookie "${name}" not found`);
        }
    }

    static listAllCookies() {
        console.group('All Cookies');
        document.cookie.split(';').forEach(cookie => {
            const [name, value] = cookie.trim().split('=');
            console.log(`${name}:`, decodeURIComponent(value));
        });
        console.groupEnd();
    }

    static monitorCookieChanges() {
        const originalCookie = document.cookie;
        
        setInterval(() => {
            if (document.cookie !== originalCookie) {
                console.log('Cookie changed!');
                this.listAllCookies();
            }
        }, 1000);
    }
}