javascriptreactjsnode.jsexpresspassport.js

Express doesn't redirect to react-router root path after successful MongoDB save - fails silently - how to redirect successfully?


I'm a novice with React and react-router, so I'm probably making a silly mistake.

I'm building a web app with React and react-router as the frontend server, and Express with MongoDB as the backend server. I am using Axios to send the POST form data from the frontend to the backend.

I have built a registration page http://localhost:3000/register. When I enter credentials into the registration form, Axios successfully sends the form data to an Express API POST route I've set up http://localhost:5000/api/users/register. Passport takes that data, registers the user and logs them in.

I am able to verify in the Mongo shell that each attempt is successful as I am able to see the newly created user show up in the database's users collection.

In the Express POST route, after the successful new user save into the MongoDB collection, I am attempting to redirect back the user's browser back to the root route of the React front end http://localhost:3000/ using `res.redirect('http://localhost:3000/)'.

I honestly can't tell if Express is making this redirect or not. My understanding is that Express ends a request/response cycle when it reaches a res.redirect() call, however I have a console.log() statement after the res.redirect('http://localhost:3000') call that runs when I hit that route, seemingly implying that the res.redirect() call is being ignored, however when I look at my browser's console, I can see an XHR OPTION request to http://localhost:3000/ as well as an XHR GET request to http://localhost:3000/, seemingly implying that the request was made to redirect to http://localhost:3000/. In the XHR GET request, in the response tab, I get the text You need to enable JavaScript to run this app., which is coming from the <noscript> tag in React's index.html document. This also makes me think the redirect request to http://localhost:3000/ is successfully being made, but something else is going wrong.

I definitely have JavaScript enabled in my browser. I have no problem navigating to http://localhost:3000/ manually in the browser. It's just when trying to get there via the Express res.redirect() call that it seems to fail.

Here is the relevant code I have:

In a frontend index.js file:

const router = createBrowserRouter([
  {
    path: "/",
    element: <Root/>,
    errorElement: <ErrorPage/>,
    loader: rootLoader,
    action: rootAction
  },
  {
    path: "/login",
    element: <Login/>
  },
  {
    path: "/register",
    element: <Register/>,
    action: registerUserAction
  }
]);

In a frontend register.jsx route file:

const handleSubmit = (event)=>{
    Axios.post('http://localhost:5000/api/users/register', {
      username: username,
      password: password
    });
  };

In a backend server.js file:

app.use(passport.initialize());
app.use(passport.session());
passport.use(new PassportLocal({username: 'username', password: 'password'}, User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());

//Creating API for user
app.use('/api/users', userRoutes);

In a backend userRoutes.js file:

import { createUser, getUsers, getUserById } from "../controllers/userController.js";
import express from 'express';
import User from '../models/userModel.js';
const router = express.Router();

router.route('/register').post(createUser);

In a backend userControllers.js file:

import express from 'express';
import User from '../models/userModel.js';
import asyncHandler from 'express-async-handler';

export const createUser = asyncHandler(async(req, res, next)=>{
  const registrationDetails = {username: req.body.username};
  const registrationPassword = req.body.password;
  const newUser = await new User(registrationDetails);
  const registeredUser = await User.register(registrationDetails, registrationPassword);
  req.login(registeredUser, (e)=>{
        if(e){
            console.log(e);
            return next(e);
        }else{
      res.redirect("http://localhost:3000/");
      console.log('in register user Express route after redirect');
        };
    });
});

What am I doing wrong? Or, why doesn't the browser redirect to http://localhost:3000/ after the the block runs successfully in the userControllers.js file?


Solution

  • Axios doesn't handle redirects automatically. The redirect happens on the server-side and is received by the Axios request in your frontend. However, Axios doesn't automatically cause the browser to navigate to this new URL. What you see ("You need to enable JavaScript to run this app") is the content of the root document of your React app being returned as the response body of the POST request, not an actual navigation event in the browser.

    To counteract this, you should return the redirect URL to the frontend in the response, and then let the frontend handle the redirect.

    Backend:

    // In your createUser function on the backend
    req.login(registeredUser, (err) => {
        if(e) {
            console.log(err);
            return next(e);
        } else {
            res.json({ success: true, redirectUrl: "http://localhost:3000/" });
        }
    });
    

    Frontend:

    import { useNavigate } from 'react-router-dom';
    
    const handleSubmit = (event) => {
        event.preventDefault(); // Prevent default form submission behavior
        const navigate = useNavigate();
        
        Axios.post('http://localhost:5000/api/users/register', {
            username: username,
            password: password
        }).then(response => {
            // Check the response and navigate
            if (res.data.redirectUrl) {
                navigate(redirectUrl);
            } else {
              // No redirect URL was sent back, treat this like an error   
            }
        }).catch(error => {
            console.error('Registration error:', error);
            // Handle error appropriately
        });
    };
    
    

    I hope that helps!