Development April 1, 2025 12 min read

How to Build a Frontend Prototype Without a Backend

Alex Thompson

Alex Thompson

Full-Stack Developer at TechStart

Frontend development workspace

Waiting for backend APIs to be ready can significantly slow down frontend development. But what if you could build, test, and iterate on your frontend completely independently? In this guide, I'll show you how to use mock APIs to build a fully functional frontend prototype without writing a single line of backend code.

Why Build Frontend Prototypes with Mock APIs?

Building frontend prototypes with mock APIs offers several key advantages:

  • Parallel Development: Frontend and backend teams can work simultaneously without blocking each other
  • Faster Iteration: Make UI changes and see results immediately without waiting for API updates
  • Better Testing: Test edge cases, error scenarios, and different data states easily
  • Client Demos: Show working prototypes to stakeholders before backend is complete
  • Portfolio Projects: Build impressive projects without needing a backend infrastructure

Setting Up Your Mock API Environment

For this guide, we'll use mockly.me's free mock API endpoints. No signup required, no API keys needed—just start making requests.

Available Mock Endpoints

Mockly.me provides several ready-to-use endpoints:

  • /user - Random user profiles with avatars, contact info, and more
  • /product - E-commerce product data
  • /quotes/random - Inspirational quotes
  • /image - Placeholder images
  • /avatar - User avatars
  • And many more...

Building a User Dashboard Prototype

Let's build a complete user dashboard prototype that displays user information, recent activity, and statistics—all using mock APIs.

Step 1: Create the HTML Structure

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>User Dashboard</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      background: #f5f5f5;
      padding: 20px;
    }
    .dashboard {
      max-width: 1200px;
      margin: 0 auto;
    }
    .header {
      background: white;
      padding: 20px;
      border-radius: 8px;
      margin-bottom: 20px;
      display: flex;
      align-items: center;
      gap: 20px;
    }
    .avatar {
      width: 64px;
      height: 64px;
      border-radius: 50%;
    }
    .stats {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      gap: 20px;
      margin-bottom: 20px;
    }
    .stat-card {
      background: white;
      padding: 20px;
      border-radius: 8px;
      box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }
  </style>
</head>
<body>
  <div class="dashboard">
    <div class="header" id="user-header">
      <img id="user-avatar" class="avatar" src="" alt="User avatar">
      <div>
        <h1 id="user-name">Loading...</h1>
        <p id="user-email"></p>
      </div>
    </div>
    
    <div class="stats">
      <div class="stat-card">
        <h3>Total Orders</h3>
        <p id="total-orders">-</p>
      </div>
      <div class="stat-card">
        <h3>Account Status</h3>
        <p id="account-status">-</p>
      </div>
      <div class="stat-card">
        <h3>Member Since</h3>
        <p id="member-since">-</p>
      </div>
    </div>
  </div>
  
  <script src="app.js"></script>
</body>
</html>

Step 2: Fetch Data from Mock APIs

Create an app.js file to fetch and display data:

// Fetch user data from mockly.me
async function loadDashboard() {
  try {
    // Fetch user profile
    const userResponse = await fetch('https://mockly.me/user');
    const user = await userResponse.json();
    
    // Update header
    document.getElementById('user-avatar').src = user.avatar;
    document.getElementById('user-name').textContent = user.name;
    document.getElementById('user-email').textContent = user.email;
    
    // Update stats
    document.getElementById('total-orders').textContent = 
      Math.floor(Math.random() * 100); // Simulated order count
    document.getElementById('account-status').textContent = 
      user.subscription_status.charAt(0).toUpperCase() + 
      user.subscription_status.slice(1);
    document.getElementById('member-since').textContent = 
      new Date(user.registration_date).toLocaleDateString();
      
  } catch (error) {
    console.error('Error loading dashboard:', error);
  }
}

// Load dashboard when page loads
loadDashboard();

Pro Tip

You can refresh the page to get different user data each time, perfect for testing how your UI handles various data scenarios.

Building an E-commerce Product List

Let's create a product listing page that fetches multiple products and displays them in a grid.

Fetching Multiple Products

async function loadProducts() {
  const productsContainer = document.getElementById('products');
  productsContainer.innerHTML = 'Loading products...';
  
  try {
    // Fetch multiple products (call the endpoint multiple times)
    const productPromises = Array.from({ length: 6 }, () => 
      fetch('https://mockly.me/product').then(r => r.json())
    );
    
    const products = await Promise.all(productPromises);
    
    // Render products
    productsContainer.innerHTML = products.map(product => `
      <div class="product-card">
        <img src="https://mockly.me/image" alt="${product.name}">
        <h3>${product.name}</h3>
        <p class="price">$${product.price.toFixed(2)}</p>
        <p class="description">${product.description}</p>
        <button>Add to Cart</button>
      </div>
    `).join('');
    
  } catch (error) {
    productsContainer.innerHTML = '<p>Error loading products</p>';
    console.error('Error:', error);
  }
}

loadProducts();

Handling Loading and Error States

A good prototype should handle loading states and errors gracefully. Here's how to implement both:

async function fetchWithLoading(url, elementId) {
  const element = document.getElementById(elementId);
  
  // Show loading state
  element.innerHTML = '<div class="loading">Loading...</div>';
  
  try {
    const response = await fetch(url);
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    const data = await response.json();
    return data;
    
  } catch (error) {
    // Show error state
    element.innerHTML = `
      <div class="error">
        <p>Failed to load data</p>
        <button onclick="fetchWithLoading('${url}', '${elementId}')">
          Retry
        </button>
      </div>
    `;
    throw error;
  }
}

Testing Different API Scenarios

Mockly.me provides endpoints to test various scenarios:

Testing Error Responses

// Test 404 error
fetch('https://mockly.me/status/404')
  .then(response => {
    if (response.status === 404) {
      // Handle not found
      console.log('Resource not found');
    }
  });

// Test 500 error
fetch('https://mockly.me/status/500')
  .then(response => {
    if (response.status === 500) {
      // Handle server error
      console.log('Server error');
    }
  });

Testing Slow Responses

// Test with 2 second delay
fetch('https://mockly.me/delay/2000/user')
  .then(response => response.json())
  .then(data => {
    // This will take 2 seconds
    console.log('Data received after delay:', data);
  });

Creating Custom Mock Endpoints

For more specific data structures, you can create custom endpoints using mockly.me's Custom API Builder:

  1. Visit https://mockly.me/create-mock-api
  2. Define your JSON response structure
  3. Get a unique URL for your custom endpoint
  4. Use it in your frontend code

Example Custom Endpoint

{
  "notifications": [
    {
      "id": 1,
      "message": "New order received",
      "timestamp": "2025-04-01T10:00:00Z",
      "read": false
    }
  ],
  "unread_count": 5
}

Best Practices for Frontend Prototyping

1. Use Environment Variables

// config.js
const API_BASE_URL = process.env.NODE_ENV === 'production' 
  ? 'https://api.yourdomain.com'
  : 'https://mockly.me';

export default API_BASE_URL;

2. Create API Service Layer

// apiService.js
class ApiService {
  constructor(baseUrl) {
    this.baseUrl = baseUrl;
  }
  
  async getUser() {
    const response = await fetch(`${this.baseUrl}/user`);
    return response.json();
  }
  
  async getProduct(id) {
    const response = await fetch(`${this.baseUrl}/product`);
    return response.json();
  }
}

// Use with mock or real API
const api = new ApiService('https://mockly.me');
const user = await api.getUser();

3. Mock Data Structure Should Match Production

Design your mock data to match the structure you expect from your real API. This makes the transition seamless when switching to production endpoints.

Transitioning from Mock to Real APIs

When your backend is ready, transitioning is straightforward:

  1. Update your API base URL configuration
  2. Ensure your mock data structure matches the real API
  3. Test authentication if required
  4. Handle any differences in response formats

Success Story

By using mock APIs during frontend development, we were able to complete our UI two weeks before the backend was ready. When the real APIs launched, we simply updated the base URL and everything worked seamlessly.

Conclusion

Building frontend prototypes with mock APIs is a powerful approach that can significantly speed up your development process. With tools like mockly.me, you can:

  • Build complete frontend applications without backend dependencies
  • Test various scenarios and edge cases easily
  • Show working prototypes to stakeholders early
  • Develop frontend and backend in parallel

Start building your next frontend prototype today with mockly.me's free mock API endpoints. No signup required, no API keys needed—just start making requests and building amazing user interfaces.

Alex Thompson

About the Author

Alex Thompson is a Full-Stack Developer at TechStart with 7+ years of experience building web applications. He's passionate about developer productivity and loves sharing techniques that help teams build faster and better.