Fullstackontwikkeling met Next.js 15 (App Router + Server Actions) · Les

Een fullstack-app bouwen

Werk een uitgebreid project uit waarin u alle geleerde concepten integreert in een complete fullstack Next.js-applicatie.

Les 2 van 411 stappen

Een fullstack-app bouwen is een gratis Fullstackontwikkeling met Next.js 15 (App Router + Server Actions)-les op CoddyKit. Dit is les 2 van 4. Je kunt 3 lessen uit dit leerpad gratis volledig lezen — daarna ontgrendelt CoddyKit PRO alle lessen, plus praktische oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Fullstackontwikkeling met Next.js 15 (App Router + Server Actions). Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Fullstackontwikkeling met Next.js 15 (App Router + Server Actions) bevat in totaal 4 lessen.

Fullstack-app: takenbeheer

Welkom bij het bouwen van een realistische fullstack-applicatie met Next.js 15! In deze les integreren we alle concepten die je hebt geleerd om een eenvoudige takenbeheerder te maken.

Met onze app kunnen gebruikers:

  • Veilig inloggen.
  • Hun persoonlijke taken bekijken.
  • Nieuwe taken toevoegen.
  • Taken als voltooid markeren.

Hiermee laten we zien hoe Server Components, Client Components, Server Actions, gegevens ophalen en authenticatie samenwerken.

Project initialiseren en database instellen

Laten we eerst ons project instellen en het databaseschema definiëren met Prisma. We hebben modellen nodig voor gebruikers en taken.

  • Next.js initialiseren: npx create-next-app@latest my-fullstack-app
  • Prisma installeren: npm install prisma @prisma/client
  • Prisma initialiseren: npx prisma init --datasource-provider sqlite (of de database van je voorkeur)

Hier is een eenvoudig schema.prisma voor onze takenbeheerder:

// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite" // or "postgresql", "mysql"
  url      = env("DATABASE_URL")
}

model User {
  id        String    @id @default(cuid())
  email     String    @unique
  name      String?
  password  String // Hashed password
  tasks     Task[]
}

model Task {
  id        String    @id @default(cuid())
  title     String
  completed Boolean   @default(false)
  userId    String
  user      User      @relation(fields: [userId], references: [id])
  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt
}

NextAuth.js implementeren

Authenticatie is een essentieel onderdeel van elke fullstack-applicatie. We integreren NextAuth.js om het inloggen, registreren en sessiebeheer van gebruikers af te handelen. Hiervoor maken we een API-route voor NextAuth.

Onze route [...nextauth] bepaalt hoe gebruikers worden geauthenticeerd:

// app/api/auth/[...nextauth]/route.ts
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { PrismaAdapter } from "@auth/prisma-adapter";
import { PrismaClient } from "@prisma/client";
import bcrypt from "bcrypt";

const prisma = new PrismaClient();

const handler = NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        email: { label: "Email", type: "text" },
        password: { label: "Password", type: "password" }
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials?.password) return null;

        const user = await prisma.user.findUnique({
          where: { email: credentials.email }
        });

        if (!user || !(await bcrypt.compare(credentials.password, user.password))) {
          return null;
        }
        return { id: user.id, email: user.email, name: user.name };
      },
    }),
  ],
  session: { strategy: "jwt" },
  callbacks: {
    jwt: async ({ token, user }) => {
      if (user) {
        token.id = user.id;
        token.email = user.email;
      }
      return token;
    },
    session: async ({ session, token }) => {
      if (token) {
        session.user.id = token.id as string;
        session.user.email = token.email;
      }
      return session;
    },
  },
  pages: {
    signIn: "/auth/signin",
  },
});

export { handler as GET, handler as POST };

Taken van gebruikers weergeven (RSC)

Ons hoofddashboard geeft taken weer die specifiek zijn voor de ingelogde gebruiker. We gebruiken een React Server Component (RSC) om deze gegevens rechtstreeks uit de database op te halen, waardoor er minder JavaScript aan de clientzijde nodig is.

Deze page.tsx haalt taken op en geeft ze door aan een Client Component om ze weer te geven:

// app/dashboard/page.tsx
import { getServerSession } from "next-auth";
import { authOptions } from "@/app/api/auth/[...nextauth]/route"; // Adjust path
import { PrismaClient } from "@prisma/client";
import TaskList from "./TaskList"; // This will be a Client Component

const prisma = new PrismaClient();

export default async function DashboardPage() {
  const session = await getServerSession(authOptions);

  if (!session || !session.user?.id) {
    return <p>Please sign in to view your tasks.</p>;
  }

  const tasks = await prisma.task.findMany({
    where: { userId: session.user.id },
    orderBy: { createdAt: "desc" },
  });

  return (
    <div>
      <h1>Welcome, {session.user.name || session.user.email}!</h1>
      <h2>Your Tasks</h2>
      <TaskList initialTasks={tasks} />
    </div>
  );
}

Taken toevoegen met Server Actions

Om nieuwe taken toe te voegen, gebruiken we Server Actions van Next.js. Hiermee kunnen we formulierinzendingen rechtstreeks op de server verwerken, wat de beveiliging verbetert en gegevens in onze gebruikersinterface automatisch opnieuw valideert.

Hier is een vereenvoudigde Server Action om een nieuwe taak te maken:

// app/actions/task-actions.ts
"use server";

import { revalidatePath } from "next/cache";
import { getServerSession } from "next-auth";
import { authOptions } from "@/app/api/auth/[...nextauth]/route";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export async function addTask(formData: FormData) {
  const session = await getServerSession(authOptions);

  if (!session || !session.user?.id) {
    throw new Error("Unauthorized");
  }

  const title = formData.get("title") as string;
  if (!title || title.trim() === "") {
    throw new Error("Task title cannot be empty.");
  }

  await prisma.task.create({
    data: {
      title,
      userId: session.user.id,
    },
  });

  revalidatePath("/dashboard"); // Revalidate dashboard to show new task
}

Interactief takenbeheer (RCC)

Hoewel Server Components de initiële gegevens verwerken, zijn Client Components (RCC) essentieel voor interactiviteit. We kunnen ze gebruiken om de gebruikersinterface direct bij te werken, bijvoorbeeld wanneer we een taak als voltooid markeren, vaak met optimistische updates.

Deze component TaskList gebruikt status aan de clientzijde en een Server Action:

// app/dashboard/TaskList.tsx (Client Component)
"use client";

import { useState } from "react";
// Assume toggleTaskCompletion is another server action
import { toggleTaskCompletion } from "@/app/actions/task-actions"; 

export default function TaskList({ initialTasks }) {
  const [tasks, setTasks] = useState(initialTasks);

  const handleToggle = async (taskId: string, currentStatus: boolean) => {
    // Optimistic UI update
    setTasks(tasks.map(task =>
      task.id === taskId ? { ...task, completed: !currentStatus } : task
    ));
    try {
      await toggleTaskCompletion(taskId, !currentStatus);
    } catch (error) {
      console.error("Failed to toggle task:", error);
      // Revert if error
      setTasks(initialTasks); // Revert to initial state on error
    }
  };

  return (
    <ul>
      {tasks.map(task => (
        <li key={task.id}>
          <input
            type="checkbox"
            checked={task.completed}
            onChange={() => handleToggle(task.id, task.completed)}
          />
          <span style={{ textDecoration: task.completed ? "line-through" : "none" }}>
            {task.title}
          </span>
        </li>
      ))}
    </ul>
  );
}

Routes beveiligen met Middleware

Om onze applicatie te beveiligen, moeten we routes beschermen waarvoor authenticatie vereist is. De Middleware van Next.js is hiervoor ideaal: hiermee kunnen we niet-geauthenticeerde gebruikers omleiden voordat ze überhaupt een pagina bereiken.

Onze middleware.ts kan authenticatie afdwingen voor specifieke paden:

// middleware.ts
import { withAuth } from "next-auth/middleware";
import { NextResponse } from "next/server";

export default withAuth(
  // `withAuth` augments the Next.js request with the user's token.
  function middleware(req) {
    const { pathname } = req.nextUrl;
    // Example: Only allow authenticated users to access /dashboard
    if (pathname.startsWith("/dashboard") && !req.nextauth.token) {
      return NextResponse.redirect(new URL("/auth/signin", req.url));
    }
    // Allow access to public pages or authenticated pages
    return NextResponse.next();
  },
  {
    callbacks: {
      authorized: ({ token }) => !!token, // Only allow if token exists
    },
    pages: {
      signIn: "/auth/signin",
    },
  }
);

export const config = {
  matcher: ["/dashboard/:path*", "/api/protected/:path*"], // Apply middleware to these paths
};

Laad- en foutstatussen

Een robuuste fullstack-app geeft duidelijke feedback. Next.js App Router helpt met speciale bestanden voor laad- en foutstatussen:

  • loading.js: Toont automatisch een laadinterface voor een routedeel terwijl de inhoud op de server wordt opgehaald.
  • error.js: Maakt UI-grenzen die fouten voor een routedeel opvangen en netjes weergeven, zodat de hele app niet crasht.

Voor Server Actions kun je ook clientstatussen gebruiken, zoals isPending uit useFormStatus, om laadindicatoren weer te geven.

De fullstack-integratiestroom

Laten we bekijken hoe deze onderdelen samenwerken in onze taakbeheerapp:

  • Een gebruiker probeert /dashboard te openen.
  • middleware.ts controleert de authenticatiestatus via NextAuth.js.
  • Als de gebruiker is geauthenticeerd, haalt dashboard/page.tsx (een RSC) gebruikersspecifieke taken op uit Prisma.
  • TaskList.tsx (een RCC) geeft deze taken weer en maakt het mogelijk om de voltooiingsstatus interactief te wijzigen via een Server Action.
  • Een afzonderlijk formulier (RCC) gebruikt een andere Server Action om nieuwe taken toe te voegen, waarna het pad /dashboard opnieuw wordt gevalideerd.

Deze naadloze interactie zorgt voor een krachtige en efficiënte gebruikerservaring.

Controle van fullstack-integratie

Bekijk een fullstack-applicatie met Next.js. Welke van de volgende uitspraken beschrijven correct het typische gebruik van Server Components, Client Components, Server Actions of Middleware?

Samenvatting van de fullstack-app

Je hebt zojuist het proces doorlopen voor het bouwen van een uitgebreide fullstack-applicatie met Next.js! We hebben verschillende belangrijke functies geïntegreerd:

  • Prisma ORM voor database-interactie.
  • NextAuth.js voor veilige authenticatie.
  • React Server Components voor efficiënt ophalen van gegevens aan de serverkant.
  • Next.js Server Actions voor veilige gegevensmutaties en hervalidatie.
  • React Client Components voor uitgebreide interactiviteit aan de clientkant.
  • Next.js Middleware voor het beveiligen van routes.

Deze les brengt veel fundamentele concepten samen en geeft je een robuust inzicht in het bouwen van complexe, productieklare Next.js-applicaties.

Gratis beginnen

Leer TypeScript met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
22
Lessen
88

Veelgestelde vragen

Is de les “Een fullstack-app bouwen” gratis?

Ja — je kunt hier op het web alle 3 lessen van het leerpad Fullstackontwikkeling met Next.js 15 (App Router + Server Actions), waaronder “Een fullstack-app bouwen”, gratis volledig lezen. Daarna ontgrendelt CoddyKit PRO alle lessen, plus interactieve oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. De cursus Fullstackontwikkeling met Next.js 15 (App Router + Server Actions) bevat in totaal 4 lessen.

Wat leer ik in “Een fullstack-app bouwen”?

Werk een uitgebreid project uit waarin u alle geleerde concepten integreert in een complete fullstack Next.js-applicatie. Je oefent met Fullstackontwikkeling met Next.js 15 (App Router + Server Actions) door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Fullstackontwikkeling met Next.js 15 (App Router + Server Actions) te beginnen?

Ervaring vooraf is niet nodig. Fullstackontwikkeling met Next.js 15 (App Router + Server Actions) op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 2 van 4.

Hoe lang duurt de les “Een fullstack-app bouwen”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Fullstackontwikkeling met Next.js 15 (App Router + Server Actions)?

Ja. Elke les over Fullstackontwikkeling met Next.js 15 (App Router + Server Actions) bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. Implementatie op Vercel
  2. Een fullstack-app bouwen
  3. Projectbeoordeling en best practices
  4. Monitoring en fouttracking in productie
← Terug naar Fullstackontwikkeling met Next.js 15 (App Router + Server Actions)