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:
[
{
"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:
https://apis.getforgeapi.com/YOUR_PROJECT_KEY/productsNeed 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 API3. 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:
NEXT_PUBLIC_API_BASE_URL=https://apis.getforgeapi.com/YOUR_PROJECT_KEYThe 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.
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:
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.
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.
"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:
NEXT_PUBLIC_API_BASE_URL=https://api.example.comIf 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.