Forge API

Next.js Tutorial

How to Use a Mock API in Next.js While Your Backend Is Being Built

Connect a Next.js application to Forge API, keep the API base URL in an environment variable, and switch to your production backend later without rewriting your data layer.

Next.js applications often need API data before the production backend is finished. In this tutorial, you will connect a Next.js app to a hosted Forge API endpoint, keep the API location in an environment variable, fetch product data, and keep your application ready for the real backend later.

What we are building

Next.js page or component

getApiRes()

NEXT_PUBLIC_API_BASE_URL

Forge API

1. Why use a mock API with Next.js?

A mock API lets you continue building pages, components and data-driven UI while your production backend is still under development. Because Forge API gives you a hosted URL, your Next.js application can make normal HTTP requests without running a local mock server.

Keep the backend replaceable

Your pages and components should depend on the API contract, not on a specific backend host. Keep the API base URL configurable and your application becomes much easier to move from mock data to production.

2. Create a Forge API endpoint

Create a Forge API project and add a /products endpoint. Use a JSON response such as:

products responsejson
[
  {
    "id": 1,
    "name": "Wireless Headphones",
    "price": 79.99,
    "inStock": true
  },
  {
    "id": 2,
    "name": "Mechanical Keyboard",
    "price": 109.99,
    "inStock": true
  },
  {
    "id": 3,
    "name": "USB-C Hub",
    "price": 49.99,
    "inStock": false
  }
]

Your hosted endpoint will look similar to:

text
https://apis.getforgeapi.com/YOUR_PROJECT_KEY/products

Need an endpoint for this tutorial?

Create a Forge API project, add the products JSON above, and use the generated URL in your Next.js application.

Create your mock API

3. Store the base URL in an environment variable

Next.js makes environment variables a natural place to store the API host. Add this to your local environment file:

.env.localenv
NEXT_PUBLIC_API_BASE_URL=https://apis.getforgeapi.com/YOUR_PROJECT_KEY

The base URL stops at your Forge API project key. Individual requests append their own endpoint names.

Why NEXT_PUBLIC_?

Use the NEXT_PUBLIC_ prefix only when the value needs to be available in browser-side code. If your API calls happen only in Server Components or server-side code, you can keep the variable server-only instead.

4. Create a reusable API helper

Keep the request-building and error handling in one place rather than repeating it throughout your application.

src/lib/api.tsts
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;

if (!API_BASE_URL) {
  throw new Error("NEXT_PUBLIC_API_BASE_URL is not configured");
}

export async function getApiRes(endpoint: string) {
  const path = endpoint.replace(/^\/+/, "");
  const response = await fetch(`${API_BASE_URL}/${path}`);

  if (!response.ok) {
    throw new Error(`API request failed: ${response.status}`);
  }

  return response.json();
}

You can now call the same helper for different endpoints:

Example usagets
await getApiRes("products");
await getApiRes("users");
await getApiRes("orders");

5. Fetch data from a Server Component

With the App Router, a page can fetch data directly on the server. This is often the simplest option when the data is needed for the initial page render.

src/app/products/page.tsxtsx
import { getApiRes } from "@/lib/api";

type Product = {
  id: number;
  name: string;
  price: number;
  inStock: boolean;
};

export default async function ProductsPage() {
  const products: Product[] = await getApiRes("products");

  return (
    <main>
      <h1>Products</h1>

      <ul>
        {products.map((product) => (
          <li key={product.id}>
            <strong>{product.name}</strong>
            <span> — ${product.price}</span>
          </li>
        ))}
      </ul>
    </main>
  );
}

Server Components are a good default

If the data is only needed to render the page and does not require browser-side interaction, fetching in a Server Component can keep the client bundle smaller.

6. Fetch from a Client Component when needed

Some screens need browser-side loading, refreshing, filters or other interactive behaviour. In those cases, you can use the same API helper from a Client Component.

src/components/ProductsClient.tsxtsx
"use client";

import { useEffect, useState } from "react";
import { getApiRes } from "@/lib/api";

type Product = {
  id: number;
  name: string;
  price: number;
  inStock: boolean;
};

export default function ProductsClient() {
  const [products, setProducts] = useState<Product[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState("");

  useEffect(() => {
    async function loadProducts() {
      try {
        const data = await getApiRes("products");
        setProducts(data);
      } catch {
        setError("Could not load products.");
      } finally {
        setLoading(false);
      }
    }

    loadProducts();
  }, []);

  if (loading) {
    return <p>Loading products...</p>;
  }

  if (error) {
    return <p>{error}</p>;
  }

  return (
    <ul>
      {products.map((product) => (
        <li key={product.id}>
          {product.name} — ${product.price}
        </li>
      ))}
    </ul>
  );
}

The important part is that both approaches use the same API base URL and the same endpoint contract.

7. Switch to your production backend later

When your real backend becomes available, update the environment variable:

.env.localenv
NEXT_PUBLIC_API_BASE_URL=https://api.example.com

If your production API keeps the same routes and JSON response shapes, your page components and reusable API helper can remain unchanged.

During development

https://apis.getforgeapi.com/YOUR_PROJECT_KEY

Production

https://api.example.com

Only the configuration changes

This is the benefit of keeping the API location separate from your application logic. Your Next.js code can continue using the same helper and routes after the backend changes.

Next steps

You can use the same pattern for users, orders, posts, dashboards and other endpoints. Keep the API base URL configurable, keep request logic centralized, and make your mock API match the contract you expect from production.