60frames / webpack-hot-server-middleware

:fire: Hot reload webpack bundles on the server
MIT License
324 stars 50 forks source link

is it possible to call the default route from another route in express #79

Closed dagda1 closed 6 years ago

dagda1 commented 6 years ago

Hi,

In webpack-hot-server-middleware you have your serverRender function and you set up dev and prod like this:

const express = require('express');
const path = require('path');
const app = express();

if (process.env.NODE_ENV !== 'production') {
    const webpack = require('webpack');
    const webpackDevMiddleware = require('webpack-dev-middleware');
    const webpackHotMiddleware = require('webpack-hot-middleware');
    const webpackHotServerMiddleware = require('webpack-hot-server-middleware');
    const config = require('./webpack.config.js');
    const compiler = webpack(config);
    app.use(webpackDevMiddleware(compiler, {
      serverSideRender: true
    }));
    app.use(webpackHotMiddleware(compiler.compilers.find(compiler => compiler.name === 'client')));
    app.use(webpackHotServerMiddleware(compiler));
} else {
    const CLIENT_ASSETS_DIR = path.join(__dirname, '../build/client');
    const CLIENT_STATS_PATH = path.join(CLIENT_ASSETS_DIR, 'stats.json');
    const SERVER_RENDERER_PATH = path.join(__dirname, '../build/server.js');
    const serverRenderer = require(SERVER_RENDERER_PATH);
    const stats = require(CLIENT_STATS_PATH);
    app.use(express.static(CLIENT_ASSETS_DIR));
    app.use(serverRenderer(stats));
}

Say I have a non root url like some-root is it possible for me to call the serverRenderer function without redirecting? All I can think to do is this.

express.Router().post('/other-route', (_: Request, res: Response) => {
  // do stuff

  res.redirect('/');
});
dagda1 commented 6 years ago

Is this package no longer active?

richardscarrott commented 6 years ago

@dagda1 you can mount the middleware on any path you like, e.g.

const router = express.Router();
const serverRenderer = webpackHotServerMiddleware(compiler);
router.post('/other-route', serverRenderer);
router.get(serverRenderer);

Alternatively, you could even export a router from the server entry, e.g.

// server.js
const router = express.Router();
const render = (req, res) => res.send(renderToString(<div>Hello World</div>));
router.post('/other-route', render);
router.get(render);
export default router;
dagda1 commented 6 years ago

@richardscarrott ha, never thought of mounting the renderer on another path.

Having thought about this a bit more (I'm new to ssr) is there a react pattern for maintaining state between middleware calls, e.g. say I have a form post handler:

export const createPaymentHandler = async (req: Request, res: Response, next: NextFunction) => {
  const { paymentType } = req.body;

  try {
    await makeRequest<CreatePaymentRequest, void>({ body: paymentType, method: HttpMethod.POST });
  } catch (err) {
    console.dir(err.message);

    // the next middleware does the rendering
    // how can I pass a value to the rendering middleware to specify an error has happened

    next();
  }
};

This will be called before the react-hot-server-middleware.

Is there a pattern for passing state between the middleware?

I'm using this on a small app and redux would be overkill.

richardscarrott commented 6 years ago

Each middleware in the stack receives the same instance of the request object so you could do this:

app.use((req, res, next) => {
    req.message = 'Hello World';
});
app.use((req, res, next) => {
    res.send(req.message);
});

However, in your case I would consider implementing an error middleware which short-circuits the middleware stack, e.g.

app.use((req, res, next) => {
    next(new Error('Something went wrong'));
});

app.use((req, res, next) => {
    // This will never be called as an error was thrown in the first middleware
});

app.use((err, req, res, next) => {
    res.send(err.message);
});
dagda1 commented 6 years ago

@richardscarrott thank you so much for taking the time to answer my question.