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 15 geliştirme

Next.js App Router ile modern React uygulaması geliştirmek için iletişime geçin

WhatsApp