Data Fetching Architecture
Overview
This project uses a structured data fetching pattern that differs between server and client components.
Core Principles
-
Server components call server functions directly
-
Client components use TanStack Query to fetch from API routes
-
API routes wrap server functions using apiRouteWrapper
-
Mutations from client components call server functions directly
-
TMDB API uses auto-generated server functions from tmdb-server-functions.ts
Server Components
Server components can directly await server functions.
import { discoverMovies } from "@/utils/tmdb-server-functions";
function serverFunction() { return fetch(URL, headers); }
function ServerComponent() { const movies = await discoverMovies({ page: 1, with_genres: "28", });
const myResults = await serverFunction();
return <div>...</div>; }
Use await directly in server components. Call TMDB server functions from @/utils/tmdb-server-functions .
Client Components + API Routes
Client components use TanStack Query to call API routes, which wrap server functions.
Step 1: Create API Route
API routes use apiRouteWrapper to wrap server functions:
// src/app/api/tmdb/discover-movies/route.ts import { apiRouteWrapper } from "@/utils/api-route-wrapper"; import { discoverMovies } from "@/utils/tmdb-server-functions";
export const GET = apiRouteWrapper(discoverMovies);
For custom server functions:
// src/app/api/some-api-route/route.ts import { apiRouteWrapper } from "@/utils/api-route-wrapper"; import { myServerFunction } from "@/some-server-function";
export const GET = apiRouteWrapper(async (params) => { return myServerFunction(params); });
Step 2: Call from Client Component
Use TanStack Query with apiRequestWrapper :
"use client"; import { useSuspenseQuery } from "@tanstack/react-query"; import { apiRequestWrapper } from "@/utils/api-request-wrapper";
function ClientComponent() { const { data: movies } = useSuspenseQuery({ queryKey: [{ scope: "movies", page: 1, with_genres: "28" }], queryFn: () => apiRequestWrapper("/api/tmdb/movie-list", { page: 1, with_genres: "28", }), });
const { data: results } = useSuspenseQuery({ queryKey: [{ scope: "someApiRoute" }], queryFn: () => apiRequestWrapper("/api/some-api-route", { foo: "bar", }), });
return <div>...</div>; }
Mutations from Client Components
For mutations, client components call server functions directly (no API route needed).
"use client"; import { useMutation } from "@tanstack/react-query"; import { updateMovie } from "@/server-functions/movies";
function ClientComponent() { const mutation = useMutation({ mutationFn: updateMovie, });
const handleUpdate = () => { mutation.mutate({ id: 1, title: "New Title" }); };
return <button onClick={handleUpdate}>Update</button>; }
TMDB API Integration
TMDB server functions are auto-generated from src/_generated/tmdb-server-functions.ts .
Important: These are auto-generated - DO NOT edit manually. Use pnpm codegen:tmdb to regenerate.
// Import TMDB functions import { discoverMovies, getMovieDetails, searchMovies, } from "@/utils/tmdb-server-functions";
// Server component usage async function MovieList() { const movies = await discoverMovies({ page: 1, with_genres: "28", });
return <div>...</div>; }
// API route for client components // src/app/api/tmdb/discover-movies/route.ts export const GET = apiRouteWrapper(discoverMovies);
Pattern Summary
Server Component Data Flow
Server Component → Server Function → External API/Database
Client Component Data Flow (Queries)
Client Component → TanStack Query → API Route → Server Function → External API/Database
Client Component Data Flow (Mutations)
Client Component → TanStack Query Mutation → Server Function → External API/Database
Best Practices
-
Server components: Call server functions directly with await
-
Client queries: Use TanStack Query + API routes
-
Client mutations: Call server functions directly
-
TMDB: Use auto-generated functions, never edit manually
-
API routes: Use apiRouteWrapper
-
Client fetching: Use apiRequestWrapper
Common Mistakes
❌ Calling API routes from server components (use server functions directly) ❌ Using fetch directly in client components (use TanStack Query) ❌ Editing tmdb-server-functions.ts manually (regenerate with pnpm codegen:tmdb ) ❌ Creating API routes for mutations (call server functions directly)