Skip to content

Membangun Platform E-Learning dengan Next.js, Prisma, dan TypeScript (Part 2)

Published: at 12:00 PM

Table of Contents

Open Table of Contents

Route Management

Dynamic Routes

Next.js mendukung dynamic routes untuk membuat halaman yang dinamis berdasarkan parameter. Misalnya, untuk menampilkan detail course berdasarkan id.

Buat file pages/courses/[id].tsx:

import { useRouter } from "next/router";
import { useEffect, useState } from "react";

const CourseDetail = () => {
  const router = useRouter();
  const { id } = router.query;
  const [course, setCourse] = useState(null);

  useEffect(() => {
    if (id) {
      fetch(`/api/courses/${id}`)
        .then(res => res.json())
        .then(data => setCourse(data));
    }
  }, [id]);

  if (!course) return <div>Loading...</div>;

  return (
    <div>
      <h1>{course.title}</h1>
      <p>{course.description}</p>
    </div>
  );
};

export default CourseDetail;

Protected Routes

Untuk melindungi route tertentu (misalnya, hanya admin yang bisa mengakses dashboard), kita bisa menggunakan middleware atau client-side protection.

Contoh middleware di pages/middleware.ts:

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  const role = request.cookies.get("role")?.value;

  if (request.nextUrl.pathname.startsWith("/admin") && role !== "admin") {
    return NextResponse.redirect(new URL("/login", request.url));
  }

  return NextResponse.next();
}

Zustand State Management

Setup Zustand

Install Zustand:

npm install zustand

Buat store untuk mengelola state global, misalnya untuk user dan course.

Buat file store/userStore.ts:

import { create } from "zustand";

interface UserState {
  user: { id: number; name: string; role: string } | null;
  setUser: (user: { id: number; name: string; role: string }) => void;
  clearUser: () => void;
}

export const useUserStore = create<UserState>(set => ({
  user: null,
  setUser: user => set({ user }),
  clearUser: () => set({ user: null }),
}));

Global State untuk User dan Course

Contoh penggunaan Zustand di komponen:

import { useUserStore } from "../store/userStore";

const UserProfile = () => {
  const user = useUserStore(state => state.user);

  if (!user) return <div>Please login</div>;

  return (
    <div>
      <h1>Welcome, {user.name}</h1>
      <p>Role: {user.role}</p>
    </div>
  );
};

export default UserProfile;

Role-Based Access Control (RBAC)

Implementasi RBAC di API Routes

Tambahkan validasi role di API routes. Misalnya, hanya admin yang bisa menambahkan course.

Contoh di pages/api/courses/index.ts:

import { NextApiRequest, NextApiResponse } from "next";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const role = req.headers["role"];

  if (req.method === "POST" && role !== "admin") {
    return res.status(403).json({ message: "Forbidden" });
  }

  if (req.method === "POST") {
    const { title, description } = req.body;
    try {
      const course = await prisma.course.create({
        data: { title, description },
      });
      res.status(201).json(course);
    } catch (error) {
      res.status(500).json({ message: "Failed to add course" });
    }
  } else {
    res.status(405).json({ message: "Method not allowed" });
  }
}

Implementasi RBAC di Frontend

Gunakan Zustand untuk memeriksa role user sebelum mengakses halaman tertentu.

Contoh di pages/admin/dashboard.tsx:

import { useUserStore } from "../../store/userStore";
import { useEffect } from "react";
import { useRouter } from "next/router";

const AdminDashboard = () => {
  const user = useUserStore(state => state.user);
  const router = useRouter();

  useEffect(() => {
    if (user?.role !== "admin") {
      router.push("/login");
    }
  }, [user, router]);

  if (!user || user.role !== "admin") return <div>Redirecting...</div>;

  return (
    <div>
      <h1>Admin Dashboard</h1>
      <p>Welcome, {user.name}</p>
    </div>
  );
};

export default AdminDashboard;

Optimasi

Code Splitting

Gunakan React.lazy dan Suspense untuk code splitting:

import { lazy, Suspense } from "react";

const LazyComponent = lazy(() => import("./LazyComponent"));

const Home = () => (
  <div>
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  </div>
);

export default Home;

Caching dengan SWR

Install SWR untuk caching dan fetching data:

npm install swr

Contoh penggunaan SWR:

import useSWR from "swr";

const fetcher = (url: string) => fetch(url).then(res => res.json());

const CourseList = () => {
  const { data, error } = useSWR("/api/courses", fetcher);

  if (error) return <div>Failed to load</div>;
  if (!data) return <div>Loading...</div>;

  return (
    <ul>
      {data.map(course => (
        <li key={course.id}>{course.title}</li>
      ))}
    </ul>
  );
};

export default CourseList;

Prisma Query Optimization

Optimasi query Prisma dengan menggunakan select dan include secara bijak:

const courses = await prisma.course.findMany({
  select: {
    id: true,
    title: true,
    materials: {
      select: {
        title: true,
      },
    },
  },
});

Kesimpulan

Dengan menerapkan route management, Zustand state management, role-based access control, dan optimasi, platform e-learning Anda akan lebih terstruktur, aman, dan efisien. Selamat mencoba!


Next Post
Membangun Platform E-Learning dengan Next.js, Prisma, dan TypeScript (Part 1)