Next.js 15, App Router''ı olgunluk seviyesine ulaştırdı. React Server Components default, server actions stabil, partial pre-rendering experimental olmaktan çıktı. Bu yazı App Router''ın temellerini — RSC, streaming, loading states, server actions — production kodu örnekleriyle anlatır.
File System Routing
İlgili rehberler: Yazılım geliştirme süreçleri · PostgreSQL optimizasyonu · Git ileri seviye komutlar · Redis nedir, nasıl kullanılır · Docker ile deploy
app/
├── layout.tsx # root layout (tüm sayfalar)
├── page.tsx # / route
├── about/
│ └── page.tsx # /about
├── blog/
│ ├── layout.tsx # blog layout
│ ├── page.tsx # /blog
│ └── [slug]/
│ └── page.tsx # /blog/:slug
├── (marketing)/ # Route group (URL'e etki etmez)
│ ├── pricing/page.tsx
│ └── contact/page.tsx
└── api/
└── users/route.ts # /api/users (Route Handler)
React Server Components (RSC)
App Router''da component''ler default olarak Server Component. Client''ta render olmaz, bundle''a girmez, direkt DB''ye bağlanabilir. Interaktiflik gerekirse ''use client'' directive''ini ekleyin.
// app/blog/page.tsx — Server Component (default)
import { db } from '@/lib/db';
export default async function BlogPage() {
const posts = await db.posts.findMany({ orderBy: { createdAt: 'desc' } });
return (
<ul>
{posts.map(p => <li key={p.id}>{p.title}</li>)}
</ul>
);
}
// DB'ye direkt bağlanıyor, client'a HTML + JSON gidiyor
// Bundle'a 'db' kütüphanesi girmiyor
// app/components/Counter.tsx — Client Component
'use client';
import { useState } from 'react';
export default function Counter() {
const [n, setN] = useState(0);
return <button onClick={() => setN(n + 1)}>{n}</button>;
}
Layouts ve Templates
// app/layout.tsx — root layout (HTML dökümanı)
export default function RootLayout({ children }) {
return (
<html lang="tr">
<body>
<nav>{/* ... */}</nav>
{children}
<footer>{/* ... */}</footer>
</body>
</html>
);
}
// app/blog/layout.tsx — nested layout
export default function BlogLayout({ children }) {
return (
<div className="blog-container">
<aside>Sidebar</aside>
<main>{children}</main>
</div>
);
}
Streaming ve Suspense
import { Suspense } from 'react';
export default function DashboardPage() {
return (
<div>
<h1>Dashboard</h1>
{/* Yavaş olan kısım Suspense ile sarılır */}
<Suspense fallback={<Skeleton />}>
<RecentOrders />
</Suspense>
<Suspense fallback={<Skeleton />}>
<Stats />
</Suspense>
</div>
);
}
async function RecentOrders() {
const orders = await db.orders.findMany({ take: 10 });
return <OrderList orders={orders} />;
}
// HTML streaming ile kullanıcı anında skeleton görür
// Her bölüm hazır olunca yerine geçer
loading.tsx ve error.tsx
// app/blog/[slug]/loading.tsx — otomatik Suspense
export default function Loading() {
return <div className="skeleton">Yükleniyor...</div>;
}
// app/blog/[slug]/error.tsx — otomatik ErrorBoundary
'use client';
export default function Error({ error, reset }) {
return (
<div>
<h2>Bir sorun oldu</h2>
<p>{error.message}</p>
<button onClick={reset}>Tekrar dene</button>
</div>
);
}
// app/not-found.tsx — 404
export default function NotFound() {
return <h1>Sayfa bulunamadı</h1>;
}
Data Fetching
// Cached fetch (default ISR gibi)
const res = await fetch('https://api.example.com/data');
// No cache
const res = await fetch('https://api.example.com/data', { cache: 'no-store' });
// Revalidate every 60s
const res = await fetch('https://api.example.com/data', { next: { revalidate: 60 } });
// Tag-based revalidation
const res = await fetch('...', { next: { tags: ['products'] } });
// Sonra:
import { revalidateTag } from 'next/cache';
revalidateTag('products');
Server Actions
// app/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
const post = await db.posts.create({ data: { title } });
revalidatePath('/blog');
return post;
}
// app/new/page.tsx — form submit direkt server'da çalışır
import { createPost } from '../actions';
export default function NewPost() {
return (
<form action={createPost}>
<input name="title" />
<button>Kaydet</button>
</form>
);
}
Route Handlers (API Routes)
// app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
const users = await db.users.findMany();
return NextResponse.json(users);
}
export async function POST(request: Request) {
const body = await request.json();
const user = await db.users.create({ data: body });
return NextResponse.json(user, { status: 201 });
}
// Dynamic route: app/api/users/[id]/route.ts
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const user = await db.users.findUnique({ where: { id: params.id } });
if (!user) return new Response('Not found', { status: 404 });
return Response.json(user);
}
Parallel Routes
app/dashboard/
├── @analytics/page.tsx # /dashboard → analytics slot
├── @team/page.tsx # /dashboard → team slot
└── layout.tsx # slotları yerleştir
// layout.tsx
export default function Layout({ children, analytics, team }) {
return (
<div>
{children}
<div className="grid grid-cols-2">
{analytics}
{team}
</div>
</div>
);
}
Metadata API
// Statik
export const metadata = {
title: 'Blog — KEYDAL',
description: 'Technical blog posts'
};
// Dinamik
export async function generateMetadata({ params }) {
const post = await getPost(params.slug);
return {
title: post.title,
description: post.description,
openGraph: { images: [post.coverImage] }
};
}
Modern Yazılım Geliştirme ve DevOps Pratikleri
Profesyonel yazılım geliştirme süreci üç pillar üzerine kuruludur: kaynak kontrolü (Git + GitHub/GitLab pull request akışı, code review zorunlu), CI/CD pipeline (otomatik test + lint + build + deploy), ve gözlemlenebilirlik (Sentry/Datadog/Grafana ile log, metric, trace toplama). Test piramidi (unit > integration > e2e) ile kod kalitesini garantilemek, mikroservis mimarisinde Docker container ve Kubernetes orkestrasyonu kullanmak, REST veya GraphQL API tasarımında OpenAPI/GraphQL Schema sözleşmesi tutmak modern standardlardır. Yazılım geliştirme yaşam döngüsü boyunca (gereksinim → tasarım → implementasyon → test → deploy → bakım) Agile/Scrum sprintleri 1-2 hafta, DevOps takımları sürekli teslim (continuous delivery) prensibiyle çalışır.
Next.js App Router ile modern React uygulaması geliştirmek için iletişime geçin