I have a requirement of using Hapi with create-react-app
where Hapi acts as a proxy for api requests and also serves the React app.
I'm trying to get the routing working, but it doesn't seem to work with the current Hapi configuration.
Here is my server code:
const Path = require('path');
const Hapi = require('hapi');
const Inert = require('inert');
const init = async () => {
const server = new Hapi.Server({
port: process.env.PORT || 5000,
routes: {
files: {
relativeTo: Path.join(__dirname, '../build')
}
}
});
await server.register(Inert);
server.route({
method: 'GET',
path: '/{param*}',
handler: {
directory: {
path: '.'
}
}
});
const options = {
ops: {
interval: 1000
},
reporters: {
myConsoleReporter: [
{
module: 'good-console',
args: [{ request: '*', response: '*' }]
},
'stdout'
]
}
};
await server.register({
plugin: require('good'),
options,
});
await server.start();
console.log('Server running at:', server.info.uri);
};
init();
The index.html file loads fine when localhost:5000
is open. I have configured a route /dashboard
in the react-router
part. Hitting localhost:5000/dashboard
gives a 404.
Questions:
create-react-app
Note: The routing works when running the react app with npm start
. But this is without the Hapi server running.
I'm new to using Hapi so any pointers are appreciated.
So I played around with various hapi+inert combo and this is what ended up working for me.
server.js
const Path = require('path');
const Hapi = require('hapi');
const Inert = require('inert');
const routes = require('./routes');
const init = async () => {
console.log('Routes are', routes);
const server = new Hapi.Server({
port: process.env.PORT || 5000,
routes: {
files: {
relativeTo: Path.join(__dirname, '../build')
}
}
});
await server.register(Inert);
server.route(routes);
/**
* This is required here because there are references to *.js and *.css in index.html,
* which will not be resolved if we don't match all remaining paths.
* To test it out, comment the code below and try hitting /login.
* Now that you know it doesn't work without the piece of code below,
* uncomment it.
*/
server.route({
method: 'GET',
path: '/{path*}',
handler: {
directory: {
path: '.',
redirectToSlash: true,
index: true,
}
}
});
const options = {
ops: {
interval: 1000
},
reporters: {
myConsoleReporter: [
{
module: 'good-console',
args: [{ request: '*', response: '*' }]
},
'stdout'
]
}
};
await server.register({
plugin: require('good'),
options,
});
await server.start();
console.log('Server running at:', server.info.uri);
};
init();
/routes/index.js
/**
* Use this for all paths since we just need to resolve index.html for any given path.
* react-router will take over and show the relevant component.
*
* TODO: add a 404 handler for paths not defined in react-router
*/
const fileHandler = {
handler: (req, res) => {
console.log(res.file('index.html'));
return res.file('index.html');
}
}
const routes = [
{ method: 'GET', path: '/login', config: fileHandler },
]
module.exports = routes;
The key thing to observe here is that for any named path (in this case /login
) we always return the index.html
file. For all other paths we tell hapi to return files from out of our build
directory so that any references to *.css
or *.js
file in our index.hml
will be resolved and we don't face a 404.
I'm not sure how react-router
takes over the path resolution once index.html is loaded, but it beyond the scope of this question and is a topic of discussion for another time maybe.
As for the second question regarding hot-reload, I am still trying to figure it out. For now I am running both the hapi server and react-app independently, as I need /api
for use in the react-app. Any suggestions or answers are welcome.