skip to Main Content

I’ve got my Mongoose code in a separate model.js file, while Express code for handling http requests is in app.js. I’m just practising creating APIs and testing them on Postman, for an imaginary wiki article site. The api I’m struggling to get it to work is deleting one article. (Note: for the sake of brevity, I’ve only included the code in question, i.e. app.delete('/articles/:id' .... from app.js, and the static method it calls from model.jsdeleteOneArticleFromDB(articleID)

app.js:

const express = require('express');
const bodyParser = require('body-parser');
const model = require('./model');

const app = express();

app.use(bodyParser.urlencoded({ extended: true }));

app.delete('/articles/:id', async (req, res) => {
    const articleID = req.params.id;
    console.log(`req.params.id: ${req.params.id}`);
    try {
        const response = await model.DBUtility.deleteOneArticleFromDB(articleID);
        res.status(200).json({message: response, app: 'wiki-api'});
    } catch (err) {
        res.json({message: err, app: 'wiki-api'});
    }
});

const port = 3000;
app.listen(port, () => {
    console.log(`Server started on port ${port}`);
});

model.js:

const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/wikiDB', {useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false });

const articleSchema = new mongoose.Schema({
    title: String,
    content: String
});

const Article = mongoose.model('Article', articleSchema);

class DBUtility {

    static deleteOneArticleFromDB(articleID) {
        return new Promise((resolve, reject) => {
            Article.findByIdAndDelete(articleID, (err) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(`Deleted article ${articleID} successfully`);
                }
            });
        });
    }
}

exports.DBUtility = DBUtility;

I have 5 articles (5 documents) in my Database:

{
    "_id" : "5c139771d79ac8eac11e754a",
    "title" : "API",
    "content" : "API stands for Application Programming Interface. It is a set of subroutine definitions, communication protocols, and tools for building software. In general terms, it is a set of clearly defined methods of communication among various components. A good API makes it easier to develop a computer program by providing all the building blocks, which are then put together by the programmer."
}

/* 2 */
{
    "_id" : "5c1398aad79ac8eac11e7561",
    "title" : "Bootstrap",
    "content" : "This is a framework developed by Twitter that contains pre-made front-end templates for web design"
}

/* 3 */
{
    "_id" : "5c1398ecd79ac8eac11e7567",
    "title" : "DOM",
    "content" : "The Document Object Model is like an API for interacting with our HTML"
}

/* 4 */
{
    "_id" : "5ea2c188fa57aa1b6453eda5",
    "title" : "Node JS",
    "content" : "Node.js is an open-source, cross-platform, JavaScript runtime environment that executes JavaScript code outside of a web browser. Node.js lets developers use JavaScript to write command line tools and for server-side scripting—running scripts server-side to produce dynamic web page content before the page is sent to the user's web browser. Consequently, Node.js represents a "JavaScript everywhere" paradigm,[6] unifying web-application development around a single programming language, rather than different languages for server- and client-side scripts.",
    "__v" : 0
}

/* 5 */
{
    "_id" : "5ea2d5304e19b11e0013a86a",
    "title" : "EJS",
    "content" : "EJS is a simple templating language that lets you generate HTML markup with plain JavaScript. No religiousness about how to organize things. No reinvention of iteration and control-flow. It's just plain JavaScript",
    "__v" : 0
}

I’m trying to delete the last article (document) with title EJS. So in Postman I run the http request as follows:

enter image description here

As you can see, I get a success response. However, when I check my database, the document is still there (I have clicked refresh several times and also tested it with a GET request to return all articles, which showed the article is still there):
enter image description here

Here’s the terminal output:

[nodemon] starting `node app.js`
Server started on port 3000
req.params.id: 5ea2d5304e19b11e0013a86a

I’ve been on this for two days. I’ve checked all previous SO posts with similar titles to mine, but I couldn’t see one that applies to my issue. I don’t understand where I’m going wrong!! Any help would be really appreciated.

UPDATE

As per the solution below by Mohammed Yousry, I realised that I added the _id field manually using strings, as I was following along to a tutorial. Hence not allowing MongoDB to create the _id field instead, as an ObjectId. Therefore, my _id field was of type String rather than ObjectId. So to resolve this, I deleted all documents from the database and re-added them, using POSTMAN with the POST method I created – for creating/adding a new article document to the database, providing only the title and content fields in the request body. Hence allowing MongoDB to create the _id field instead, for each article document. Now in the database the _id field is of type ObjectId. This still hasn’t resolved my issue fully, but it’s one step further. Still working on reaching a solution. Please refer to the discussions below in the solution section.

enter image description here

4

Answers


  1. In MongoDB, the _id property is of type ObjectId.
    I had the same problem and fixed it by passing over an ObjectId instead.

    Have you tried the following ?

    const Mongoose = require('mongoose');
    const articleId = Mongoose.Types.ObjectId(req.params.id);
    
    Login or Signup to reply.
  2. 1- may be your mongoose version does not support findByIdAndDelete

    try findByIdAndRemove instead

    2- you can pass the id to that method as a string or as an objectId

    make sure you are passing the right id without any spaces

    const articleID = req.params.id.toString().trim(); // trim will remove any spaces before or after the id
    

    Update

    according to this image

    enter image description here

    it seems you have inserted your data manually, and these data contained the _id as a string, as the type of the _id is a string here as you can see in this image

    I suggest you to let MongoDB to set the _id itself

    If you could change the _id type to be ObjectId

    or If you can’t change the type of _id, you can take a backup of your articles, then remove all those articles manually and add them again without the _id, let mongoDB set that _id, and try your code again

    hope this may solve the issue



    Update 2

    After checking the codes in github, I know the reason why you got this strange behavior

    1- you have a route app.route('/articles/:articleTitle') defined before the route app.delete('/articles/:id'

    so when you are trying to delete some article by passing the id, nodeJS an express will pass it to the first matching route, as it does not know the params you send, it’s searching for the base url in the route, so it deals with the first route which matching the url : '/articles/something', take that something as the article title

    then call the deleteOneArticleByTitleFromDB method

    MongoDB is then searching for some article with title = the id you passed, it found nothing, then, it doesn’t remove any article

    2- I suggest you not to define some routes with the same base url

    so you can define the delete by Id route like the following

    app.delete('/arti/cles/:id', async (req, res) => // note the slash inside articles word
    

    or anything you want except the current one

    by this you avoid this contradiction between the routes

    Login or Signup to reply.
  3. @Hazzaldo I was facing the same problem. But apparently, I didn’t write await before findByIdAndDelete(). After writing await before it fixed it for me.
    I saw your code, as you are wrapping the findByIdAndDelete inside a promise which in itself is a promise, I would recommend you to try to await the findByIdAndDelete first and then return the promise.

    Login or Signup to reply.
  4. I was trying to find a solution for this the whole day! I used a completely different logic on the backend. Heres my code:

    `

    export const deletePost = async (req, res) => {
        const { id } = req.params;
        // Confirm data
        if (!id) {
            return res.status(400).json({ message: 'Note ID found' })
        }
        // Confirm note exists 
        const post = await PostMessage.findById(id).exec()
        if (!post) {
        return res.status(400).json({ message: 'Note not found' })
        }
        const result = await post.deleteOne()
        const reply = `Post '${result.title}' with ID ${result._id} deleted`
        res.json(reply)
    }`
    

    I hope you find this helpful.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search