Serve Routed React App With ExpressJS
By: Ryan Saeta
Tags: Single Page Applications, Web App Development, Create React App, Express Session, Expressjs

Problems Serving Routed React App

Everyone’s out here creating react apps using create-react-app and it makes getting started very easy. I find that whenever I try to add some backend functionality, things usually break.

I could complain about whole bunch of things about create-react-app but for now, let’s just solve one problem I recently ran into.

Context

I have a bunch of routes on my SPA and some API’s in my Express application. I’m serving up the UI using Express’s static asset rendering middleware: app.use(express.static(path.join(__dirname, 'public')) . And this is going great… Until I try to directly access the UI with a non-empty path. Here I get the message cannot GET /path .

Solution: Express-Sessions

Here is the whole file but we will break it down step-by-step:

Import Dependencies

import express from 'express';

import * as path from 'path;

import cookieParser from 'cookie-parser';

import session from 'express-session';



import apiRoutes from './api';

cookie-parser : Cookie parser is needed to support sessions

: Cookie parser is needed to support sessions express-session : Will hold temporary information while redirecting to and from the UI in our server.

: Will hold temporary information while redirecting to and from the UI in our server. ./api : This is just a placeholder for all of your server routes.

Creating Server and Applying Middleware

const app = express();

app.use(cookieParser());

app.use(session({

secret: 'my super secret secret',

resave: false,

saveUninitialized: true,

});

Add Server Routes

// Put your non-UI routes here, i.e. /api.

app.use('/api', apiRoutes);

Here, /api is just a placeholder for any prefix you want. In fact, you don’t even need to provide a prefix for this to work, I just personally like to explicitly separate my API’s.

Serve Built Static Assets

if (process.env.NODE_ENV === 'production') {

const publicPath = path.join(__dirname, 'public');

// Serve up built frontend application as static assets

app.use(express.static(path.join(publicPath)));

The Interesting Part

The interesting thing to note here is that the static asset serving is just helping express redirect requests for static assets at / to serve files designated in path.join(...) . The issue arises when a web browser requests a UI path that doesn’t get mapped to this static asset serving middleware, for instance /page1 .

The naive solution would just be to do something like:

app.get('*', (req, res) => res.sendFile(..., 'index.html'));

However, this doesn’t actually render anything! That’s because each subsequent request for static assets (the files loaded by index.html ) are automatically prepended with the /page1 prefix. For instance, if there is a stylesheet index.css defined in your index.html file, the request for this will actually be a GET /page1/index.css instead of just /index.css .

To handle this, we want to first send index.html , and then tell the webapp to redirect to the initial path. We use express-session to store the requested unmatched path.