≡ Menu

Building an e-commerce application is the ultimate “level-up” project for any web developer.

It moves you beyond static pages and into the world of state management, persistent data, and user experience.

In today’s guide, we aren’t just slapping together a list of items; we’re building a scalable architecture.

We’ll start with a clean Vanilla JS frontend and structure our logic so that it’s ready for a professional backend—ideal if you’re planning to scale into a secure environment using a private database.

Whether you’re looking to launch your own side hustle or sharpen your skills for a portfolio, this tutorial will give you the foundational “hooks” you need to go from a blank index.html to a fully functioning shop.

Part 1: Architecture & Data Fetching

In a professional setup, you don’t hardcode products. You fetch them. We’ll use an async function to simulate a call to your future backend.

The “API” Layer

// This will eventually point to your private IP address
const API_URL = "https://api.example.com/products"; 

async function fetchProducts() {
    try {
        // For now, we simulate a network delay
        const response = await fetch('https://fakestoreapi.com/products'); 
        const data = await response.json();
        return data;
    } catch (error) {
        console.error("Database connection failed:", error);
    }
}

Part 2: Building a “Smart” Product Grid

Instead of just showing names, let’s add Category Filtering. Users shouldn’t have to scroll through 100 items to find a mouse.

Updated HTML (Search & Filter)

 

<section class="controls">
    <input type="text" id="search-bar" placeholder="Search products...">
    <select id="category-filter">
        <option value="all">All Categories</option>
        <option value="electronics">Electronics</option>
        <option value="jewelery">Jewelery</option>
    </select>
</section>

The Logic: Filtering and Rendering

let allProducts = []; // Global store for filtering

async function init() {
    allProducts = await fetchProducts();
    renderProducts(allProducts);
}

function renderProducts(productsToDisplay) {
    const grid = document.getElementById('product-grid');
    grid.innerHTML = productsToDisplay.map(product => `
        <div class="product-card">
            <img src="${product.image}" alt="${product.title}" style="width:100px">
            <h3>${product.title}</h3>
            <p class="price">$${product.price}</p>
            <button onclick="addToCart(${product.id})">Add to Cart</button>
        </div>
    `).join('');
}

// Search Event
document.getElementById('search-bar').addEventListener('input', (e) => {
    const filtered = allProducts.filter(p => 
        p.title.toLowerCase().includes(e.target.value.toLowerCase())
    );
    renderProducts(filtered);
});

Part 3: Persistent Shopping Cart (LocalStorage)

The biggest mistake beginners make is losing the cart data when the user refreshes the page.

We solve this using localStorage.

Enhanced Cart Logic:

// Load cart from memory on startup
let cart = JSON.parse(localStorage.getItem('user_cart')) || [];

function addToCart(id) {
    const product = allProducts.find(p => p.id === id);
    
    // Check if item already exists to increment quantity instead of duplicating
    const existingItem = cart.find(item => item.id === id);
    if (existingItem) {
        existingItem.quantity += 1;
    } else {
        cart.push({ ...product, quantity: 1 });
    }
    
    saveAndSync();
}

function saveAndSync() {
    localStorage.setItem('user_cart', JSON.stringify(cart));
    updateCartUI();
}

Part 4: The Checkout Math (Advanced)

A professional cart needs to handle quantities and the dreaded “Remove Item” button. We’ll use the reduce method to calculate totals dynamically.

Professional UI Update:

 

function updateCartUI() {
    const cartContainer = document.getElementById('cart-items');
    
    cartContainer.innerHTML = cart.map(item => `
        <div class="cart-item">
            <span>${item.title} (x${item.quantity})</span>
            <span>$${(item.price * item.quantity).toFixed(2)}</span>
            <button onclick="removeFromCart(${item.id})">×</button>
        </div>
    `).join('');

    const total = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
    document.getElementById('cart-total').innerText = total.toFixed(2);
}

function removeFromCart(id) {
    cart = cart.filter(item => item.id !== id);
    saveAndSync();
}

Summary Checklist

Feature Implementation
Data Fetched via async/await from an API.
State Managed in a cart array and synced to localStorage.
UI Built with CSS Grid and dynamic template literals.
UX Includes search, category filtering, and item removal.

Next Steps and Scaling Up

Congratulations!

You now have a working e-commerce engine running entirely on JavaScript, HTML, and CSS.

By implementing localStorage and dynamic rendering, you’ve ensured that your users’ shopping experience is seamless and persistent.

Where do you go from here?

While our frontend is solid, the next big leap is moving your product data from a static file to a live database.

As you begin setting up your production environment—perhaps utilizing that private IP for your database to keep your inventory and customer data secure—you can simply swap out our fetch URLs for your own API endpoints.

Don’t stop here: try adding a “Checkout” button that integrates with Stripe, or implement a “Favorites” list using the same logic we used for the cart.

The sky is the limit when you control the code!

Useful links below:

Let me & my team build you a money making website/blog for your business https://bit.ly/tnrwebsite_service

Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2

Best email marketing automation solution on the market! http://www.aweber.com/?373860

Build high converting sales funnels with a few simple clicks of your mouse! https://bit.ly/484YV29

Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff

Buy me a coffee ☕️https://buymeacoffee.com/tyronneratcliff

{ 0 comments }

I’m currently in the middle of a high-stakes e-commerce project.

Our tech stack is a modern powerhouse: Next.js for the frontend to ensure lightning-fast SEO and performance, NestJS on the backend for a structured, enterprise-grade API, and Google Cloud SQL as our data backbone.

The “killer feature” we’re building?

A fleet of AI agents that don’t just chat, but actually act—checking real-time stock, updating user preferences, and even predicting restocking needs.

But giving an AI agent “action” capabilities means giving it a path to the database.

To sleep at night, I’ve had to implement a “Zero-Trust” architecture, starting with a private IP for our database.

The Problem: The “Public IP” Liability

In the early days of GenAI, developers often connected their LLM wrappers to public database endpoints using standard credentials.

In 2026, that is an architectural cardinal sin.

If an agent is successfully targeted by a prompt injection attack, and your database is sitting on a public IP, you have essentially given an attacker a direct line to your most sensitive customer data.

By moving to a private-only infrastructure, we ensure that even if an agent’s logic is subverted, the data has no physical “exit” to the public internet.



Phase 1: The Networking Backbone (Google Cloud)

To secure my NestJS backend’s connection to Google Cloud SQL, I had to move away from the standard Public IP approach.

  1. VPC Peering: I established a private services access connection. This allows my VPC network and the Google-managed network (where Cloud SQL lives) to communicate internally.

  2. Private Service Connect (PSC): For our 2026 stack, we’re using PSC to map a specific private IP within our subnet directly to the Cloud SQL instance. This makes the database look like a local resource at an address like 10.128.0.5.

  3. No External Egress: Our database instance has “Public IP” disabled entirely. It doesn’t even have a gateway to the outside world.

Phase 2: Connecting NestJS via the Node.js Connector

In a NestJS environment, hardcoding IPs is brittle. I’m using the @google-cloud/cloud-sql-connector library.

It’s the “2026 way” to handle IAM-based authentication without managing static database passwords.

// Example snippet for our NestJS Database Module
const connector = new Connector();
const clientOpts = await connector.getOptions({
  instanceConnectionName: 'my-ecom-project:us-central1:my-db',
  ipType: 'PRIVATE', // This is the crucial flag!
});

This ensures that the NestJS service, running in its own private subnet, authenticates using its Service Account identity rather than a vulnerable .env password.

Phase 3: Agent Orchestration and Tool-Calling

The real magic happens when the AI agents need to query the e-commerce data.

We use a Tool-Calling pattern where the agent doesn’t write SQL itself—it calls a “Tool” (a NestJS endpoint) that executes a pre-defined, sanitized query.

  • Sandboxed Execution: Each agent request is wrapped in a transient session.

  • The Egress Filter: Even though the agent is “private,” we use a NAT Gateway with strict logging.

  • This ensures that if the agent tries to reach an unauthorized external API, the request is dropped and an alert is triggered in our SecOps dashboard.

Why This Matters for E-commerce in 2026

Customers in 2026 are highly aware of “AI Privacy.” By documenting that your agents operate within a Private Subnet and interact with a Private IP Database, you aren’t just building a secure app—you’re building a brand based on trust.

The Next.js frontend never talks to the database. The NestJS backend only talks to the database over a private wire.

And the AI agents?

They are the “orchestrators” trapped in a high-security vault, allowed only to perform the tasks we’ve explicitly permitted.


What’s your take?

Building secure AI systems is a moving target.

I’m curious—are you still using public endpoints for your development environments, or have you made the switch to full VPC isolation?

Useful links below:

Let me & my team build you a money making website/blog for your business https://bit.ly/tnrwebsite_service

Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2

Best email marketing automation solution on the market! http://www.aweber.com/?373860

Build high converting sales funnels with a few simple clicks of your mouse! https://bit.ly/484YV29

Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff

Buy me a coffee ☕️https://buymeacoffee.com/tyronneratcliff

{ 0 comments }