Search code examples
javascriptreactjsmongodbmongoosenext.js

Can't delete by id from mongoDB in nextjs app


I'm making an app in NextJS to practice, and I am having a hard time getting single data to delete from the database using the findByIdAndDelete function.

CastError: Cast to ObjectId failed for value "undefined" (type string) at path "_id" for model "Appointment"

./page.jsx:

import AddAppointment from "./addAppointment";
import DeleteAppointment from "./deleteAppointment";
import UpdateAppointment from "./updateAppointment";

async function getAppointment() {
  const res = await fetch("http://localhost:3000/api/appointment", {
    method: "GET",
    cache: "no-store",
  });
  // const data = await res.json();
  return res.json();
}

async function Appointment() {
  const { appointment } = await getAppointment();

  return (

    <div className="py-10 px-10">
      <div className="py-2">
        <AddAppointment />
      </div>
      <table className="table w-full">
        <thead>
          <tr>
            <th>#</th>
            <th>Nama</th>
            <th>Tanggal</th>
            <th>No Telp.</th>
            <th>Terapis</th>
            <th>Status</th>
            <th>Action</th>
          </tr>
        </thead>
        <tbody>
          {appointment.map((row, i) => (
            <tr key={row._id}>
              <td>{i + 1}</td>
              <td>{row.name}</td>
              <td>{row.date}</td>
              <td>{row.phone}</td>
              <td>{row.terapist}</td>
              <td>{row.statust || "unknown"}</td>
              <td className="flex">
                <UpdateAppointment {...appointment} />
                <DeleteAppointment {...appointment} />
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

export default Appointment;

./deleteAppointment.jsx:

"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";

export default function DeleteAppointment() {
  const [modal, setModal] = useState(false);

  const router = useRouter();

  async function handleDelete({ id }) {
    await fetch(`http://localhost:3000/api/appointment?id=${id}`, {
      method: "DELETE",
    });

    router.refresh();
    setModal(false);
  }

  function handleChange() {
    setModal(!modal);
  }

  return (
    <div>
      <button className="btn btn-error btn-sm" onClick={handleChange}>
        Delete
      </button>

      <input
        type="checkbox"
        checked={modal}
        onChange={handleChange}
        className="modal-toggle"
      />

      <div className="modal">
        <div className="modal-box">
          <h3 className="font-bold text-lg">
            Anda yakin untuk menghapus data ""?
          </h3>

          <div className="modal-action">
            <button type="button" className="btn" onClick={handleChange}>
              Close
            </button>
            <button type="button" onClick={handleDelete} className="btn">
              Delete
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

API route:

import { connectMongoDB } from "@/lib/mongodb";
import Appointment from "@/models/appointment";
import { NextResponse } from "next/server";

export async function DELETE(req) {

  const id = req.nextUrl.searchParams.get("id");
  await connectMongoDB();
  await Appointment.findByIdAndDelete(id);
  return NextResponse.json({ message: "Appointment deleted" }, { status: 200 });
}

Model:

import mongoose, { Schema, models } from "mongoose";

const appointmentSchema = new Schema(
  {
    name: {
      type: String,
      required: true,
    },
    date: {
      type: String,
      required: true,
    },
    phone: {
      type: String,
      required: true,
    },
    terapist: {
      type: String,
      required: true,
    },
    statust: {
      type: String,
      required: true,
    },
  },
  { timestamps: true }
);

const Appointment =
  models.Appointment || mongoose.model("Appointment", appointmentSchema);
export default Appointment;

CastError: Cast to ObjectId failed for value "undefined" (type string) at path "_id" for model "Appointment"

Having a hard time to figure how to pass id and the difference between id and _id


Solution

  • There's no parameter sent with the onClick function. So, the id parameter is undefined. Therefore, it sends an API request with an undefined id parameter like this: http://localhost:3000/api/appointment?id=undefined

    On the client side, it is necessary to use an ObjectID to delete a document by using findByIdAndDelete() but the ID parameter comes from the request as undefined, not an ObjectID. Therefore, it can't find the related document from DB and returns this error.

    As a result, the issue is related to the client side. Need to send the correct ID to be deleted with the API parameter. Therefore, need to get the related row or only the id value as props in the DeleteAppointment component.

    It would be better to send the whole row object into props if you will need to use other fields as well:

    // Send related row as prop 
    <DeleteAppointment row={row} />
    

    Otherwise, it's enough to send only id:

    // Send only the related row's ID field as a prop
    <DeleteAppointment id={row.id} />
    

    Then get the props as a parameter in the DeleteAppointment component:

    export default function DeleteAppointment(props) {
    ...
    }
    

    After that, because of reaching the related id into the props, you can use props.id or props.row.id in the request id instead of the id parameter:

    async function handleDelete() {
      await fetch(`http://localhost:3000/api/appointment?id=${props.id}`, {
          method: "DELETE",
      });
    
      router.refresh();
      setModal(false);
    }
    

    If you send the whole row object as a prop, then you can use props.row.id:

    async function handleDelete() {
      await fetch(`http://localhost:3000/api/appointment?id=${props.row.id}`, {
          method: "DELETE",
      });
    
      router.refresh();
      setModal(false);
    }
    

    Hope, it's clear and helpful