Next.js Starter
Charakterystyka
Section titled “Charakterystyka”The starter utilizes the App Router and React Server Components (RSC). Leveraging Next.js, data fetching happens server-side by default with force-cache, providing performance comparable to static sites.
1. Preview Implementation (app/api/preview/route.ts)
Section titled “1. Preview Implementation (app/api/preview/route.ts)”Handles the handshake by verifying the signature via the backend API and sets a secure session cookie.
import { cookies } from 'next/headers';import { redirect } from 'next/navigation';
export async function GET(request: Request) { const { searchParams } = new URL(request.url); const path = searchParams.get('path') || ''; const expires = searchParams.get('expires'); const signature = searchParams.get('signature');
const internalUrl = process.env.INTERNAL_API_URL; const verifyUrl = `${internalUrl}/api/preview/verify?path=${path}&expires=${expires}&signature=${signature}`;
try { const response = await fetch(verifyUrl, { cache: 'no-store' }); const data = await response.json();
if (!response.ok || !data.valid) { return new Response('Unauthorized signature', { status: 401 }); }
const cookieStore = await cookies(); cookieStore.set('pyxis_preview', data.preview_token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', path: '/', maxAge: 3600 });
const redirectPath = (path === 'homepage' || path === '') ? '/' : `/${path}`; redirect(redirectPath); } catch (e) { return new Response('API Connection Error: ' + e.message, { status: 500 }); }}2. Data Fetching (app/[[…slug]]/page.tsx)
Section titled “2. Data Fetching (app/[[…slug]]/page.tsx)”The main page component that automatically forwards the preview header if an active session is detected.
async function getPageData(slugArray: string[] | undefined) { const slug = slugArray?.length ? slugArray.join('/') : ''; const cookieStore = await cookies(); const previewToken = cookieStore.get('pyxis_preview')?.value;
const API_URL = process.env.INTERNAL_API_URL; const headers: HeadersInit = { 'Accept': 'application/json' };
if (previewToken) headers['X-Pyxis-Preview'] = previewToken;
const res = await fetch(`${API_URL}/api/pages/${slug}`, { cache: previewToken ? 'no-store' : 'force-cache', headers: headers, next: { revalidate: 3600 } });
if (!res.ok) return null; const json = await res.json(); return json.data;}3. Instant Revalidation (app/api/revalidate/route.ts)
Section titled “3. Instant Revalidation (app/api/revalidate/route.ts)”A webhook endpoint that invalidates the Next.js cache immediately after CMS publication using revalidatePath.
import { revalidatePath } from 'next/cache';import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) { try { const body = await request.json(); const { secret, path } = body;
// Verify token if (secret !== process.env.REVALIDATE_TOKEN) { console.warn(`[Revalidate] Unauthorized attempt with secret: ${secret}`); return NextResponse.json({ message: 'Invalid token' }, { status: 401 }); }
if (!path) { return NextResponse.json({ message: 'Path is required' }, { status: 400 }); }
// Normalization let cleanPath = path === 'homepage' ? '/' : path; if (!cleanPath.startsWith('/')) { cleanPath = `/${cleanPath}`; }
console.log(`[Revalidate] Purging cache for: ${cleanPath}`);
// Do revalidation revalidatePath(cleanPath, 'page');
return NextResponse.json({ revalidated: true, path: cleanPath, now: Date.now() }); } catch (error) { console.error('[Revalidate Error]', error); return NextResponse.json({ message: 'Error revalidating' }, { status: 500 }); }}