Search code examples
javascriptnode.jsexpresshttp-status-code-404react-router

Express status 404 with react-router


I have an express server that handles: 1 API route and rendering my initial index.html to include bundle.js holding my React/React-Router/Redux application.

As it stands, it is impossible to 404 on my web page as I have a catch all:

app.use(function (req, res) {
  return res.render('index')
})

In order for react-router's NoMatch to work I need to send a 404 code.

My routes are as follows:

Express — /api/test/:x/:y

React Router — :x/, :x/:y

What I am essentially trying to achieve is, if the user ever goes to a URL of: :x/:y/z/and/further then return a 404, unless what they've gone to is /api/test/:x/:y

Questions:

  1. How can I match routes, excluding my API routes, preferably in a scalable way, returning appropriate status codes?
  2. For something so simple, is there significant overhead in setting this up on a subdomain? Would that even alleviate the issue? Would I face issues when the app grows?

Solution

  • Take a look at react-router server side rendering docs: reacttraining.com/react-router/web/guides/server-rendering

    Solution:

    1. Extract routes to separate files and require it in express app
    2. Add a middleware in express app that check url in express using match function from react-router. It should be written after middlewares that responsible for API routes.
    3. In case there is no appropriate routes for request url, response with 404.

    So, middleware should be similar to this:

    // origin code from https://github.com/reactjs/react-router/blob/master/docs/guides/ServerRendering.md
    // this is ES6, but easily can be written on a ES5.
    
    import { match, RouterContext } from 'react-router'
    import routes from './routes' 
    
    var app = express();
    
    // ...
    
    app.use((req, res, next) => {
      match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
        if (error) {
          res.status(500).send(error.message)
        } else if (redirectLocation) {
          res.redirect(302, redirectLocation.pathname + redirectLocation.search)
        } else if (renderProps) {
             // You can also check renderProps.components or renderProps.routes for
            // your "not found" component or route respectively, and send a 404 as
            // below, if you're using a catch-all route.
    
            // Here you can prerender component or just send index.html 
            // For prependering see "renderToString(<RouterContext {...renderProps} />)"
            res.status(200).send(...)
        } else {
          res.status(404).send('Not found')
        }
      })
    });
    

    If any routes change, you don't need to do something on express app, because you're using same code for frontend and backend.