Unhandled Rejection (SyntaxError): Unexpected end of JSON input with Get request fetch

Viewed 43

I am trying to do a MVC architecture tutorial from Codecademy's website: https://www.codecademy.com/article/mvc-architecture-for-full-stack-app

I finished the tutorial but when I run everything, I get this error:

enter image description here

It seems that what I'm returning is not valid JSON. So I think the problem is that the endpoint may be causing the error. But I'm not too sure. Here is the code where the error is triggered:

src/utils/index.js:

export const fetchExpenses = async (date) => {
 const selectDate = new Date(date).getTime() || new Date().getTime();
 const res = await fetch(`/api/expense/list/${selectDate}`);
 console.log('result',res);
  return res.json();
};

Here is the code from app.js in the "view" portion of my code:

useEffect(() => {
  // update view from model w/ controller
  fetchExpenses().then((res) => setExpenses(res));
}, []);

It seems the problem is the communication between the view and the controller. When I create an expense, it actually is updated in the database:

enter image description here

Any ideas why this error is happening?

Edit 1:

Here is the network response when I try to create a new expense in my application. So it seems that when I create a new expense, the fetchExpenses() is automatically called to display a list of current expenses.

enter image description here

this the raw response I get from fetchExpenses() : enter image description here

Edit 2:

Here is what the header shows from the response: enter image description here

The endpoint is causing the error, but I'm not sure why. Here is the endpoint:

export const createExpense = async (data) => {
  const res = await fetch(`/api/expense/create`, {
    method: 'POST',
    body: data,
  });
  return resHandler(res, 201);
};

and here is resHandler() which createExpense() returns:

export const resHandler = async (res, status) => {
  if (res.status === status) {
    return null;
  }
  const data = await res.json();
  if (data && data.emptyFields) {
    return data.emptyFields;
  }
  return null;
};

Here is the code from the controller when an expense is created:

exports.create = (req, res) => {
  const form = new formidable.IncomingForm();
  form.keepExtensions = true;
  form.parse(req, async (err, fields) => {
    const { title, price, category, essential, created_at } = fields;
    // check for all fields
    if (fieldValidator(fields)) {
      return res.status(400).json(fieldValidator(fields));
    }
    try {
      const newExpense = await pool.query(
        'INSERT INTO expenses (title, price, category, essential, created_at) VALUES ($1, $2, $3, $4, $5)',
        [title, price, category, essential, created_at]
      );
      return res.status(201).send(`User added: ${newExpense.rows}`);
    } catch (error) {
      return res.status(400).json({
        error,
      });
    }
  });
};

Edit 3

Here is the route /api/expense/list/{dateTime}:

const express = require('express');
const router = express.Router();

const { create, expenseById, 
    read, update, remove, expenseByDate } = require('../controllers');

router.get('/expense/list/:expenseDate', expenseByDate, read);

module.exports = router;

And here is my controllers.js that deal with the route above:

exports.expenseByDate = async (req, res, next, date) => {
  try {
    const expenseQuery = await pool.query(
      'SELECT * FROM expenses WHERE created_at BETWEEN $1 AND $2',
      [
        startOfDay(new Date(Number(date))).toISOString(),
        endOfDay(new Date(Number(date))).toISOString(),
      ]
    );
    const expenseList = expenseQuery.rows;
    req.expense =
      expenseList.length > 0
        ? expenseList
        : `No expenses were found on this date.`;
    return next();
  } catch (error) {
    return res.status(400).json({
      error,
    });
  }
};

exports.read = (req, res) => res.json(req.expense);
1 Answers

The reason you are getting an Unhandled Rejection (SyntaxError): Unexpected end of JSON input error is because your client app is expecting a JSON response and the express app /api/expense/list/{dateTime} route is not returning valid JSON.

The app is not returning valid JSON because the expenseByDate controller callback has an incorrect function signature so it is not getting called.

exports.expenseByDate = async (req, res, next, date) => <-- "date" is not a valid parameter.

This leads the read controller to return an undefined value to the json response.

exports.read = (req, res) => res.json(req.expense);  <-- req.expense is undefined.

res.json(undefined) ultimately returns an empty response to the client which can't be parsed and thus an error is thrown.

Solution

You can fix this error by correcting the expenseByDate controller to have a valid function signature by removing the fourth method parameter. To access a route parameter you should use req.params.

exports.expenseByDate = async (req, res, next, date) => {
    const date = req.params.expenseDate;
    ...

}
Related