Search code examples
node.jsmongodbexpressmongooseinternal-server-error

I am getting 500 "Internal Server Error" with findByIdAndUpdate, using express and mongoDB


I'll begin by stating that I am fairly new to express and backend programming. I was not sure how to format my question and if there are duplicates, please forward me to them.

I've created an express app where I use mongoDB and various tools to create an API for an upcoming blog that is going to be a react app. So far I've been doing fairly well and already got working get, post and delete methods. When I started creating my put method is where I ran into my problem. I've created a mongoose Schema, a router for my put request and use "findByIdAndUpdate" to update my database. For testing I use jest and supertest.

The problem I'm having is that when I try to update the object in my database based on its id, I'm getting a 500 "Internal Server Error", even though the object in my database successfully updates. Given that the request goes through even though there clearly is a server error, I'm without answers.

What I've tried:

  • I'm using a try/catch block around all the relevant code, but my error logs aren't really giving me any answers.

  • I've made a test file using supertest and jest to test this and all my previous operations, which have worked just fine except for this one.

  • I've used the VS Code REST Client plugin to update my database.

  • I tried to make my supertest expect(500), which made the test move on and pass, thus confirming that the object updates despite the server error.

My blog model, using Mongoose Schema

const mongoose = require('mongoose')

const blogSchema = mongoose.Schema({
  title: {
    type: String,
    required: true
  },
  author: String,
  url: {
    type: String,
    required: true
  },
  likes: Number
})

blogSchema.set('toJSON', {
  transform: (document, returnedObject) => {
    returnedObject.id = returnedObject._id.toString()
    delete returnedObject._id
    delete returnedObject.__v
  }
})

module.exports = mongoose.model('Blog', blogSchema)

My router

const blogsRouter = require('express').Router()
const Blog = require('../models/blog')

blogsRouter.put('/:id', async (req, res, next) => {
  const body = req.body
  const blog = {
    title: body.title
  }
  try {
    const updatedBlog = await Blog.findByIdAndUpdate(req.params.id, blog, {
      new: true
    })
    console.log("Updated blog: ", updatedBlog)
    res.json(updatedBlog.toJSON())
  } catch (err) {
    next(err)
  }
})

module.exports = blogsRouter

My test_helper.js file

const Blog = require('../models/blog')

const initialBlogs = [
  {
    title: 'testTitle',
    author: 'testAuthor',
    url: 'testUrl',
    likes: 3
  },
  {
    title: 'anotherTest',
    author: 'anotherAuthor',
    url: 'anotherUrl',
    likes: 25
  }
]

const nonExistingId = async () => {
  const blog = new Blog({ title: 'willremovethissoon' })
  await blog.save()
  await blog.remove()

  return blog._id.toString()
}

const blogsInDb = async () => {
  const blogs = await Blog.find({})
  return blogs.map(blog => blog.toJSON())
}

module.exports = {
  initialBlogs,
  nonExistingId,
  blogsInDb
}

My blogs_api.test.js file

const mongoose = require('mongoose')
const supertest = require('supertest')
const helper = require('./test_helper')
const app = require('../app')
const api = supertest(app)

const Blog = require('../models/blog')

describe('when there is initially some blogs saved', () => {
  beforeEach(async () => {
    await Blog.remove({})

    for (let blog of helper.initialBlogs) {
      let blogObject = new Blog(blog)
      await blogObject.save()
    }
  })
describe('updating of blogs', () => {
test('a blog can be updated', async () => {
  const blog = {
    title:
      'updatedTitle - this gets through even thought Im getting 500 
       "internal server error"'
  }

  const blogsAtStart = await helper.blogsInDb()
  const blogToUpdate = blogsAtStart[0]
  console.log('Blog to update: ', blogToUpdate)

  await api
    .put(`/api/blogs/${blogToUpdate.id}`)
    .send(blog)
    .expect(200)

  const blogsAtEnd = await helper.blogsInDb()
  const blogAfterUpdate = blogsAtEnd[0]

  expect(blogAfterUpdate.title).toBe(blog.title)
})
})
})

My log when trying to update my object

jonas$ jest -t updated

FAIL  tests/blog_api.test.js
Console

console.log tests/blog_api.test.js:129
  Blog to update:  { title: 'testTitle',
    author: 'testAuthor',
    url: 'testUrl',
    likes: 3,
    id: '5ce30cbfd3ed531b538579ac' }
console.log controllers/blogs.js:62
  Updated blog: { _id: 5ce30cbfd3ed531b538579ac,
    title:
     'updatedTitle - this gets through even thought Im getting 500 "internal server error"',
    author: 'testAuthor',
    url: 'testUrl',
    likes: 3,
    __v: 0 }
console.log utils/logger.js:8
  updatedBlog.toJSON is not a function

when there is initially some blogs saved › updating of blogs › a 
blog can be updated

 expected 200 "OK", got 500 "Internal Server Error"

  at Test.Object.<anonymous>.Test._assertStatus (node_modules/supertest/lib/test.js:268:12)
  at Test.Object.<anonymous>.Test._assertFunction (node_modules/supertest/lib/test.js:283:11)
  at Test.Object.<anonymous>.Test.assert (node_modules/supertest/lib/test.js:173:18)
  at Server.localAssert (node_modules/supertest/lib/test.js:131:12)

As I stated earlier, the object is updating correctly into my database, but I can not continue before I solve this error. What makes it so hard for me to debug on my own is the fact that the object is updating correctly into my database, both in development and test environments.

I tried to post as little code as possible, please let me know if more is needed, or if the question is unclear. I included the test files only to better explain my console logs, do tell if they are unnecessary.


Solution

  • Notice this line in your log file:

    updatedBlog.toJSON is not a function

    Thus, the line res.json(updatedBlog.toJSON()) is likely the offending one. Please ensure that you're attempting to invoke the toJSON() method on the correct object type and that you're not already receiving JSON as your return value.