From API to UI: a simple Next.js + Express blueprint

September 2, 2025 (1w ago)

Shipping small features fast is about having a repeatable path from data to pixels. Here’s a minimal, battle‑tested setup I use to go from an Express API to a typed Next.js UI.

1) Build a tiny Express endpoint

// api/index.ts
import express from "express";
const app = express();
 
type Todo = { id: number; title: string; done: boolean };
const todos: Todo[] = [
  { id: 1, title: "Wire data model", done: true },
  { id: 2, title: "Render UI", done: false },
];
 
app.get("/api/todos", (_req, res) => {
  res.json(todos);
});
 
app.listen(3001, () => console.log("API on http://localhost:3001"));

Notes:

2) Fetch it in Next.js with types and caching

// src/lib/api.ts
export type Todo = { id: number; title: string; done: boolean };
 
export async function getTodos(): Promise<Todo[]> {
  const res = await fetch("http://localhost:3001/api/todos", {
    // Cache on server for 30s; great default for dashboards
    next: { revalidate: 30 },
  });
  if (!res.ok) throw new Error("Failed to fetch todos");
  return res.json();
}

3) Render in a simple component

// src/app/examples/todos/page.tsx
import { getTodos } from "@/lib/api";
 
export default async function TodosPage() {
  const todos = await getTodos();
  return (
    <main className="mx-auto max-w-md space-y-4 p-6">
      <h1 className="text-2xl font-bold">Todos</h1>
      <ul className="divide-y">
        {todos.map((t) => (
          <li key={t.id} className="flex items-center justify-between py-2">
            <span>{t.title}</span>
            <span className={t.done ? "text-green-600" : "text-yellow-600"}>
              {t.done ? "done" : "pending"}
            </span>
          </li>
        ))}
      </ul>
    </main>
  );
}

4) Add a database later (when needed)

Start with an in‑memory array to move fast. When it’s worth it, swap to any DB:

The important bit: keep your types stable and your fetch function as the integration point. Everything else can change under the hood.

Pitfalls to avoid

Wrap‑up

You now have a clean path: Express API → typed fetch → fast Next.js UI. Duplicate this pattern for new features and you’ll ship faster with fewer bugs.