Server-only Data Layer

Keep Queries Out of Components

Do not call prisma directly in fifty components. Put queries in lib/data/*.ts files marked server-only. Components call named functions; you can cache, test and secure them in one place.

app/products/[id]/page.tsx  ──►  lib/data/products.ts  ──►  lib/prisma.ts  ──►  PostgreSQL
app/admin/...               ──►  lib/data/admin.ts        (import 'server-only')
app/.../actions.ts          ──►  lib/data/orders.ts

Example

// lib/data/products.ts
import 'server-only';
import { cache } from 'react';
import { prisma } from '@/lib/prisma';

export type ProductCard = { id: number; name: string; price: number; imageUrl: string | null; inStock: boolean };

export const getProductCards = cache(async (categorySlug: string, page: number): Promise<ProductCard[]> => {
  const rows = await prisma.product.findMany({
    where: { category: { slug: categorySlug } },
    select: { id: true, name: true, price: true, imageUrl: true, stock: true },
    orderBy: { createdAt: 'desc' },
    skip: (page - 1) * 24,
    take: 24,
  });
  return rows.map((r) => ({ id: r.id, name: r.name, price: Number(r.price), imageUrl: r.imageUrl, inStock: r.stock > 0 }));
});

Tip: Use select to return only the fields the UI needs. Smaller queries, smaller RSC payloads, and no accidental leaks of internal columns.