reactjsexpressundefinedput

undefined body in PUT request


I have a React / Redux / Node app that is making a request to update a user:

client/src/components/pages/AlignmentPage.tsx:

const AlignmentPage = () => {
  const dispatch = useAppDispatch();
  const [alignment, setAlignment] = useState("");
  const user: User | null = useAppSelector(
    (state: RootState) => state.userData.user
  );

  const handleSetAlignment = (value: string) => {
    setAlignment(value);
    displayNewAlignment(value);
    if (user) {
      console.log("in AlignmentPage.tsx:", { alignment: value });
      dispatch(updateUser(user._id, { alignment: value }));
    }
  };
...

In my Redux action, we are making a call to the PUT endpoint for the user:

export const updateUser = (userId: string, data: any) => async (dispatch: any) => {
  try {
    dispatch(setLoadingAction(true));

    const response = await fetch(`http://localhost:5000/api/user/${userId}`, {
      method: 'PUT',
      cache: 'no-cache',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data)
    });

    const updatedData = await response.json();
    dispatch(updateUserAction(updatedData.user));
  } catch (error) {
    dispatch(setErrorAction('error updating user!'));
  }
}

That controller endpoint looks like this:

exports.updateUser = asyncHandler(async (req: any, res: any, next: any) => {
  const { userId } = req.params;
  console.log(req.body)
  const data = req.body;

  const user = await updateUser(userId, data);

  res.status(200).json({ user });
});

For some reason, in the controller, I am able to log out the userId param just fine. But the body always returns undefined and thus the Redux store and Document in Mongo are not being updated. I am able to log the "data" successfully up until the call to const response = await fetch(http://localhost:5000/api/user/${userId}" in the Redux action. The updatedData logger returns the original MongoDB object, but without the updated field ("alignment"):

Object { user: {…} }
​user: Object { _id: "65d73ae14d1a69c64d6787e6", ipAddress: "::ffff:127.0.0.1", createdAt: "2024-02-22T12:15:29.745Z", … }
​​__v: 0
​​_id: "65d73ae14d1a69c64d6787e6"
​​alignment: ""
​​createdAt: "2024-02-22T12:15:29.745Z"
​ipAddress: "::ffff:127.0.0.1"

It seems like something is preventing the body from being sent to the controller. I'm not sure if this is the way I'm setting up the router or what. I read elsewhere that this may be due to not having bodyParser middleware installed on the server side, so I implemented this in my server.ts file, but that hasn't appeared to solve the problem.

(async () => {
  const port = env.PORT;
  app.use(express.json());
  app.use(bodyParser.json());
  app.use(bodyParser.urlencoded({ extended: false }));
  app.use('/api/user', userRouter);

  try {
    app.listen(port, () => {
      db.on("error", console.error.bind(console, "connection error:"));
      db.on("connected", function () {
        console.log("Connected to MongoDB");
      });
      console.log(`Server started at http://localhost:${port}`);
    });
  } catch (err) {
    const error = new Error("Request failed");
    console.log(err);
    throw error;
  } finally {
    console.log("Server started");
  }
})();

api/src/routes/user.ts:

router.get("/api/user", userController.getUser);
router.put("/api/user/:userId", userController.updateUser);

export { router };

Added screenshots of loggers and redux payload: enter image description here

enter image description here

enter image description here


Solution

  • I figured it out.

    I needed to pass express.json() as an argument when calling the user controller endpoint.

    api/src/routes/user.ts:

    router.put("/api/user/:userId", express.json(), userController.updateUser);
    

    api/src/controllers/user.ts:

    exports.updateUser = asyncHandler(async (req: any, res: any, next: any) => {
      const { userId } = req.params;
      const data = req.body;
    
      const user = await updateUser(userId, data);
    
      res.status(200).json({ user });
    });
    

    Data is being updated successfully now :)