Skip to content

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

Published: at 12:00 PM

Table of Contents

Open Table of Contents

Pengenalan

Dalam bagian ketiga ini, kita akan menambahkan fitur CRUD (Create, Read, Update, Delete) untuk item di platform e-learning kita. Kita juga akan mengelola akses berdasarkan peran pengguna, sehingga hanya pengguna dengan peran tertentu yang dapat menambah atau menghapus item.


Model Item di Prisma

Pertama, kita perlu menambahkan model Item di file prisma/schema.prisma:

model Item {
  id          Int      @id @default(autoincrement())
  title       String
  description String
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
  userId      Int
  user        User     @relation(fields: [userId], references: [id])
}

Setelah menambahkan model, jalankan migrasi untuk memperbarui database:

npx prisma migrate dev --name add_item_model

API Routes untuk CRUD Item

Create Item

Buat API route untuk menambah item di pages/api/items/index.ts:

import { NextApiRequest, NextApiResponse } from "next";
import { PrismaClient } from "@prisma/client";
import { authenticate } from "../../../lib/auth";
import { authorize } from "../../../lib/authorize";

const prisma = new PrismaClient();

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  await authenticate(req, res, async () => {
    if (req.method === "POST") {
      const { title, description } = req.body;
      const userId = req.user.id; // Ambil userId dari token

      try {
        const item = await prisma.item.create({
          data: {
            title,
            description,
            userId,
          },
        });
        res.status(201).json(item);
      } catch (error) {
        res.status(500).json({ message: "Failed to create item" });
      }
    } else {
      res.status(405).json({ message: "Method not allowed" });
    }
  });
}

Read Items

Buat API route untuk mengambil semua item di pages/api/items/index.ts:

if (req.method === "GET") {
  try {
    const items = await prisma.item.findMany({
      include: { user: true }, // Menyertakan informasi pengguna
    });
    res.status(200).json(items);
  } catch (error) {
    res.status(500).json({ message: "Failed to fetch items" });
  }
}

Update Item

Buat API route untuk memperbarui item di pages/api/items/[id].ts:

import { NextApiRequest, NextApiResponse } from "next";
import { PrismaClient } from "@prisma/client";
import { authenticate } from "../../../lib/auth";
import { authorize } from "../../../lib/authorize";

const prisma = new PrismaClient();

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  await authenticate(req, res, async () => {
    const { id } = req.query;

    if (req.method === "PUT") {
      const { title, description } = req.body;

      try {
        const item = await prisma.item.update({
          where: { id: Number(id) },
          data: { title, description },
        });
        res.status(200).json(item);
      } catch (error) {
        res.status(500).json({ message: "Failed to update item" });
      }
    } else {
      res.status(405).json({ message: "Method not allowed" });
    }
  });
}

Delete Item

Buat API route untuk menghapus item di pages/api/items/[id].ts:

if (req.method === "DELETE") {
  try {
    const item = await prisma.item.delete({
      where: { id: Number(id) },
    });
    res.status(200).json(item);
  } catch (error) {
    res.status(500).json({ message: "Failed to delete item" });
  }
}

Mengelola Akses Berdasarkan Peran

Kita perlu memastikan bahwa hanya pengguna dengan peran tertentu (misalnya, “admin” atau “teacher”) yang dapat menambah atau menghapus item. Kita dapat menggunakan middleware authorize yang telah kita buat sebelumnya.

Contoh Penggunaan Middleware untuk Create dan Delete:

await authorize(["admin", "teacher"])(req, res, async () => {
  // Logic untuk menambah atau menghapus item
});

Contoh Penggunaan di Create Item

await authenticate(req, res, async () => {
  await authorize(["admin", "teacher"])(req, res, async () => {
    // Logic untuk menambah item
  });
});

Contoh Penggunaan di Delete Item

await authenticate(req, res, async () => {
  await authorize(["admin", "teacher"])(req, res, async () => {
    // Logic untuk menghapus item
  });
});

Frontend untuk Mengelola Item

Buat komponen untuk menampilkan dan mengelola item. Contoh komponen untuk menampilkan daftar item:

import { useEffect, useState } from "react";

interface Item {
  id: number;
  title: string;
  description: string;
}

const ItemList: React.FC = () => {
  const [items, setItems] = useState<Item[]>([]);

  const fetchItems = async () => {
    const res = await fetch("/api/items");
    const data = await res.json();
    setItems(data);
  };

  useEffect(() => {
    fetchItems();
  }, []);

  return (
    <div>
      <h1 className="text-2xl font-bold">Item List</h1>
      <ul>
        {items.map(item => (
          <li key={item.id} className="border p-2">
            <h2 className="font-semibold">{item.title}</h2>
            <p>{item.description}</p>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default ItemList;

Form untuk Menambah Item

Buat form untuk menambah item:

import { useState } from "react";

const AddItem: React.FC = () => {
  const [title, setTitle] = useState("");
  const [description, setDescription] = useState("");

  const handleAddItem = async () => {
    const res = await fetch("/api/items", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${localStorage.getItem("token")}`,
      },
      body: JSON.stringify({ title, description }),
    });

    if (res.ok) {
      // Reset form or update UI
    } else {
      // Handle error
    }
  };

  return (
    <div>
      <h1 className="text-2xl font-bold">Add Item</h1>
      <input
        type="text"
        value={title}
        onChange={e => setTitle(e.target.value)}
        placeholder="Title"
        className="border p-2"
      />
      <textarea
        value={description}
        onChange={e => setDescription(e.target.value)}
        placeholder="Description"
        className="border p-2"
      />
      <button onClick={handleAddItem} className="bg-blue-500 p-2 text-white">
        Add Item
      </button>
    </div>
  );
};

export default AddItem;

Kesimpulan

Dengan menambahkan fitur CRUD untuk item dan mengelola akses berdasarkan peran pengguna, Anda telah memperkuat platform e-learning yang sedang dibangun. Menggunakan Next.js, Prisma, dan TypeScript bersama dengan kontrol akses berbasis peran memberikan fondasi yang kuat untuk aplikasi web yang aman dan terkelola dengan baik. Selamat mencoba!


Previous Post
React dan Next.js Cheatsheet untuk Pemula (TypeScript dan Tailwind CSS)
Next Post
React dan Next.js Cheatsheet untuk Pemula (TypeScript dan Tailwind CSS)