Search code examples
javascriptnode.jsexpresserror-handlingprisma

Conflict router when I hit another endpoint can effect other


I got some problem about routing on express js, when I hit some endpoint for example :

http://localhost:3000/api/v1/projects/task/f9488b07-76f8-4f8a-bafb-471f99dde7b8

http://localhost:3000/api/v1/project/0592679a-93f1-467b-b2cd-4226f0668036

I got the message on function handling like this:

{
    "errors": "Failed to find a project: Project ID is required"
}

but that message on function findProjectID :

todo-service.js
/* eslint-disable no-useless-catch */
const prismaClient = require("../prisma-client");
const { ResponseError } = require("../error/error-response");

const createProject = async ({
  projectname,
  description,
  owner,
  expiresAt,
}) => {
  try {
    const userConnections =
      Array.isArray(owner) && owner.length > 0
        ? owner.filter((o) => o.user_id).map((o) => ({ user_id: o.user_id }))
        : null;

    console.log();

    // Parse the input date string using Date constructor
    // Check if `expiresAt` is provided and is a valid date
    const timestamp = expiresAt ? new Date(expiresAt).toISOString() : null;

    if (expiresAt && isNaN(new Date(expiresAt))) {
      throw new Error("Invalid date format for expiresAt.");
    }

    const data = {
      projectname,
      description,
      ...(timestamp && { expiresAt: timestamp }),
      ...(userConnections.length > 0 && {
        owner: { connect: userConnections },
      }),
    };

    const createdProject = await prismaClient.project.create({
      data,
      include: {
        owner: {
          select: {
            user_id: true,
            username: true,
          },
        }, //
      },
    });

    return createdProject;
  } catch (error) {
    console.error("Error creating project:", error);
    throw error;
  }
};

//getvalueprojectbyownerid
const getProject = async (projectId) => {
  try {
    const project = await prismaClient.project.findMany({
      where: {
        project_id: projectId,
      },
      include: {
        owner: {
          select: {
            user_id: true,
            username: true, // Adjust according to your user model fields
          },
        },
      },
    });
    console.log(project);
    return project;
  } catch (error) {
    console.log(error);
  }
};

//editproject
const editproject = async (project_id, projectname, description, owner) => {
  try {
    const findProject = await findProjectID(project_id);
    console.log(findProject);
    if (!findProject) {
      throw new ResponseError(404,"Project ID not Found");
    }

    const data = {
      projectname,
      description,
    };

    if (Array.isArray(owner)) {
      const connectOwners = owner
        .filter((o) => o.user_id)
        .map((o) => ({ user_id: o.user_id }));

      if (connectOwners.length > 0) {
        data.owner = {
          connect: connectOwners,
        };
      } else {
        const disconnectOwners = findProject.owner.map((o) => ({
          user_id: o.user_id,
        }));
        if (disconnectOwners.length > 0) {
          data.owner = {
            disconnect: disconnectOwners,
          };
        }
      }
    }

    console.log("Data to update:", data);

    const projectUpdate = await prismaClient.project.update({
      where: {
        project_id,
      },
      data,
      include: {
        owner: {
          select: {
            user_id: true,
            username: true,
          },
        },
      },
    });
    return projectUpdate;
  } catch (error) {
    console.log(error);
    throw new Error("Failed to update project");
  }
};

//delete todo projectbyid
const deleteProjectById = async (project_id) => {
  try {
    const findProject = await findProjectID(project_id);

    if (!findProject) {
      throw new ResponseError(404, "Project Not Found");
    }
    const deleteProject = await prismaClient.project.delete({
      where: {
        project_id,
      },
    });
    return deleteProject;
  } catch (error) {
    console.log(error);
  }
};

//findProjecID
const findProjectID = async (project_id) => {
  try {
    if (!project_id) {
      throw new Error("Project ID is required");
    }

    const project = await prismaClient.project.findUnique({
      where: { project_id },
      include: { owner: true },
    });

    return project;
  } catch (error) {
    console.error("Error finding a project:", error.message);
    throw new Error(`Failed to find a project: ${error.message}`);
  }
};

//Task
const createTask = async ({
  title,
  description,
  status,
  priority,
  projectId,
  assigneeId,
  expiresAt
}) => {
  try {
    const timestamp = expiresAt ? new Date(expiresAt).toISOString() : null;

    if (expiresAt && isNaN(new Date(expiresAt))) {
      throw new Error("Invalid date format for expiresAt.");
    }
    const createTasks = await prismaClient.task.create({
      data: {
        title : title || null,
        description : description || null,
        status : status || null,
        priority: priority || null,
        project: projectId 
          ? { connect: { project_id: projectId } } 
          : undefined,
        
        Assignee: assigneeId 
          ? { connect: { user_id: assigneeId } } 
          : undefined,                    
          ...(timestamp && { expiresAt: timestamp }),
      },
      include: {
        Assignee: {
          select: {
            user_id: true,
            username: true,
          },
        },
        project: {
          select: {
            project_id: true,
            projectname: true,
          },
        },
      },
    });

    return createTasks;
  } catch (error) {
    console.error("Error creating task:", error.message);
    throw new Error(`Failed to create task: ${error.message}`);
  }
};

const getTaskById = async (task_id) => {
  try {
    if (!task_id) {
      throw new Error("task ID is required");
    }

    const task = await prismaClient.task.findUnique({
      where: { task_id : task_id},
      include : {
        project : {
          select : {
            projectname : true
          }
        },
        Assignee : {
          select : {
            username : true
          }
        }
      },
    });
    return task;
  } catch (error) {
    throw error;
  }
};

const editTask = async (task_id, title, description, status, priority, projectId, assigneeId, expiresAt) => {
  try {
    const findTask = await findTaskById(task_id);
    if (!findTask) {
      throw new ResponseError(404, "Task not found");
    }

    const timestamp = expiresAt ? new Date(expiresAt).toISOString() : null;

    const editedTask = await prismaClient.task.update({
      where: {
        task_id
      },
      data: {
        title: title || null,
        description: description || null,
        status: status || null,
        priority: priority || null,
        project: projectId 
          ? { connect: { project_id: projectId } } 
          : { disconnect: true },
        Assignee: assigneeId 
          ? { connect: { user_id: assigneeId } } 
          : { disconnect: true },
        ...(timestamp && { expiresAt: timestamp }),
      },
      include: {
        Assignee: {
          select: {
            user_id: true,
            username: true,
          },
        },
        project: {
          select: {
            project_id: true,
            projectname: true,
          },
        },
      },
    });

    return editedTask;
  // eslint-disable-next-line no-unused-vars
  } catch (error) {
    console.error("Error finding a project:", error.message);
    throw new Error("Failed to Edit Task");
  }
};

const deleteTask = async(taskid) => {
  try {
    if(!taskid) {
      throw new ResponseError(404, "task id is required!")
    }
    const findTaskId = await findTaskById(taskid)
    console.log("idne apa :: ",findTaskId)
    if(!findTaskId){
      throw new ResponseError(404, "task id not found")
    }

    const deleteTask = await prismaClient.task.delete({
      where : {
        task_id : taskid
      }
    })

    return deleteTask
  } catch (error) {
    throw error
  }
}


const findTaskById = async (task_id) => {
  try {
    if (!task_id) {
      throw new ResponseError(404,"task Id is required");
    }

    const task = await prismaClient.task.findUnique({
      where: { task_id : task_id},
    });

    if (!task) {
      throw new ResponseError(404,"Task id not found");
    }

    return task;
  } catch (error) {
    throw error;
  }
};

module.exports = {
  createProject,
  getProject,
  editproject,
  deleteProjectById,
  findProjectID,
  createTask,
  getTaskById,
  editTask,
  deleteTask,
  findTaskById
};

its a controller file :

todo-controller.js
const { createProject, getProject, editproject, deleteProjectById, findProjectID, createTask, getTaskById, editTask, findTaskById, deleteTask} = require("../services/todo-services");

const createProjectHandler = async (req, res , next) => {
    try {
        const {projectname, description, owner,expiresAt} = req.body
        console.log("Request body:", req.body);

            if (!owner) {
                throw new Error('User ID is required for connecting an owner.');
            }
        const createNewProject = await createProject({
            projectname,
            description,
            owner,
            expiresAt
        });

        res.status(201).json({status : 201,
            data: createNewProject
        })
    } catch (error) {
        console.log(error)
        next(error)
    }
}

const getProjectByIdHandler = async ( req, res , next) => {
    try {
        const projectId = req.params.projectId
        if (!projectId) {
            return res
              .status(400)
              .json({ error: "Invalid project ID", message: "Invalid Request" });
        }
        const project = await getProject(projectId)

        if(!project){
            return res.status(404).json({ error: "Project not found", message: "No project found for the given owner ID" });
        }
        return res.status(200).json(project);
    } catch (error) {
        console.log(error)
        next()
    }
}

const editProjectHandler = async ( req, res, next ) => {
    try {
        const projectId = req.params.projectId
        const {projectname, description, owner} = req.body
        console.log("project ID :: " ,projectId)

        if(!projectId) {
            return res
              .status(400)
              .json({ error: "Invalid Project ID", message: "Invalid Request" });
        }
        const updateProject = await editproject(projectId,projectname,description,owner)
        return res.status(201).json({message : "Project Succesfully to Update",updateProject})
    } catch (error) {
        console.log(error)
        next (error)
    }
}

//delete handler project
const deleteProjectHandler = async (req, res, next) => {
    try {
        const projectId = req.params.projectId;
        // Await the result of findProjectID to ensure it completes
        const findProject = await findProjectID(projectId);

        if (!findProject) {
            return res.status(404).json({ error: "Invalid Project ID", message: "Project not found" });
        }

        // Proceed to delete the project
        await deleteProjectById(projectId);

        // Return 204 No Content status as the deletion was successful
        return res.status(204).send();

    } catch (error) {
        // Log the error for debugging
        console.error("Delete Project Handler Error:", error);

        // Forward the error to the error handling middleware
        next(error);
    }
};

const createTaskHandler = async(req, res, next) => {
    try {
        const {title, description,status, priority,projectId,assigneeId, expiresAt} = req.body
        const newTask = await createTask({title, description,status, priority,projectId,assigneeId,expiresAt})
        console.log("newtask :: ", newTask)
        res.status(201).json({status : 201,
            data: newTask
        })
    } catch (error) {
        next(error)
    }
}

const getTaskByIdHandler = async (req, res, next) => {
    try {
      const task_id = req.params.taskId;
     // Check if taskId is provided
    if (!task_id) {
        return res.status(400).json({ status: 400, error: "Task ID is required" });
      }
      // Find the task by ID
      const task = await getTaskById(task_id);
      if (!task) {
        return res.status(404).json({ status: 404, error: "Task Not Found" });
      }
      // Return the found task
      res.status(200).json({
        status: 200,
        data: task,
      });
    } catch (error) {
      next(error);  // Pass the error to the error handling middleware
    }
};

const editTaskHandler = async(req, res, next) => {
    try {
        const taskId  = req.params.taskId
        console.log("task id :: ", taskId)
        const { title, description, status, priority,projectId,assigneeId, expiresAt} = req.body

        console.log("body ?? :: ",req.body)
        if(!taskId || taskId.trim() === ""){
            return res.status(404).json({status: 404, message : "taskid required!"})
        }

        const findTask = await findTaskById(taskId)
        if(!findTask) {
            return res.status(404).json({status: 200, message : "taskid not found!"})
        }

        const editNewTask = await editTask(taskId,title, description, status, priority,projectId,assigneeId, expiresAt)
        res.status(200).json({status : 200, message: "updated successfull!", editNewTask})
    } catch (error) {
        console.error("Error in editTaskHandler:", error.message);
        next(error)
    }
}

const deleteTaskHandler = async(req, res, next) => {
    try {
        const task_id = req.params.taskId
        console.log("paramsid  ::", req.params.taskId)
        if(!task_id){
            return res.status(404).json({
                status : 404,
                message : "task id is required!"
            })
        }
        await deleteTask(task_id)
        return res.status(204).json().send
    } catch (error) {
        console.error("Error in editTaskHandler:", error.message);
        next(error)
    }
}

module.exports = {
    createProjectHandler,
    getProjectByIdHandler,
    editProjectHandler,
    deleteProjectHandler,
    createTaskHandler,
    getTaskByIdHandler,
    editTaskHandler,
    deleteTaskHandler
}

its route file

const express = require("express");
const { authenticationMiddleware } = require("../middleware/auth-middleware");
const {
  createProjectHandler,
  getProjectByIdHandler,
  editProjectHandler,
  deleteProjectHandler,
  createTaskHandler,
  getTaskByIdHandler,
  editTaskHandler,
} = require("../controller/todo-controller");
const todoRouter = express.Router();

todoRouter.use(authenticationMiddleware);

//project
todoRouter.post("/project", createProjectHandler);
todoRouter.get("/project/:projectId", getProjectByIdHandler);
todoRouter.put("/project/:projectId?", editProjectHandler);
todoRouter.delete("/project/:projectId", deleteProjectHandler);

//task
todoRouter.post("/project/task/", createTaskHandler);
todoRouter.get("/project/task/:taskId", getTaskByIdHandler);
todoRouter.put("/project/task/:taskId?", editTaskHandler);
todoRouter.delete("/project/task/:taskId?", deleteProjectHandler);
module.exports = todoRouter;

I'm trying to changes the route it's works like for example :

before with method update

http://localhost:3000/api/v1/projects/task/taskid

after with methode update

http://localhost:3000/api/v1/projects/task/:taskId

but when i create a new route with metode delete like

http://localhost:3000/api/v1/projects/task/:taskId

it's not working and the output like that

{
    "errors": "Failed to find a project: Project ID is required"
}

help me to best practice to make proper routing


Solution

  • As the middleware is executed in order it is loaded, try moving concrete routes before those with parameters, i.e. move task routes before project:

    //task
    todoRouter.post("/project/task/", createTaskHandler);
    todoRouter.get("/project/task/:taskId", getTaskByIdHandler);
    todoRouter.put("/project/task/:taskId?", editTaskHandler);
    todoRouter.delete("/project/task/:taskId?", deleteProjectHandler);
    
    
    //project
    todoRouter.post("/project", createProjectHandler);
    todoRouter.get("/project/:projectId", getProjectByIdHandler);
    todoRouter.put("/project/:projectId?", editProjectHandler);
    todoRouter.delete("/project/:projectId", deleteProjectHandler);