Let's say I want to host my 2 websites (cats.com and dogs.com) on the same server with a single IP address (i.e. with virtual hosts). I want to write them both with hapi.js and have them running as a single process.
The sites may have overlapping paths, for instance they might both have a /about
page.
How could I implement this with hapi?
A nice way of achieving that with hapi is by putting your different sites in to separate plugins and using the vhost
modifier when loading the plugin, ideally using Glue.
Here's an example:
sites/dogs.js
exports.register = function (server, options, next) {
// Put all your routes for the site in here
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
reply('Dogs homepage');
}
});
next();
};
exports.register.attributes = { name: 'dogs' };
sites/cats.js
exports.register = function (server, options, next) {
// Put all your routes for the site in here
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
reply('Cats homepage');
}
});
next();
};
exports.register.attributes = { name: 'cats' };
index.js
const Glue = require('glue');
const Hoek = require('hoek');
const manifest = {
connections: [{
port: 4000,
}],
registrations: [
{
plugin: {
register: './sites/cats'
},
options: {
routes: {
vhost: 'cats.com'
}
}
},
{
plugin: {
register: './sites/dogs'
},
options: {
routes: {
vhost: 'dogs.com'
}
}
}
]
};
const options = {
relativeTo: __dirname
};
Glue.compose(manifest, options, (err, server) => {
Hoek.assert(!err, err);
server.start((err) => {
Hoek.assert(!err, err);
console.log('server started');
});
});
You can then confirm that the routing works correctly with a couple of cURL commands:
$ curl -H "Host: cats.com" localhost:4000/
Cats homepage
$ curl -H "Host: dogs.com" localhost:4000/
Dogs homepage
A browser will set that Host header for you though so when you browse to http://cats.com or http://dogs.com hapi will serve you the correct content (provided your DNS is configured correctly).
If you are using a Hapi version >= 17, the details have changed slightly, though the idea is the same.
We will want to have a plugin for each site. Then, once we have each site extracted into a plugin (cats
and dogs
below), we can compose the separate configurations using glue
and serve the site with hapi
.
In the example below, the plugins will not know or care which domain they are being served on.
This is the "server" that's meant to be used with cats.com
. It returns the text Hello Cats!
at the root path, /
. In real life it would do something more useful, and you would likely have many more routes and handlers in a real project, but the idea remains the same.
// ./sites/cats.js
exports.plugin = {
name: 'cats',
version: '1.0.0',
register: async function(server, options) {
server.route({
method: 'GET',
path: '/',
handler: (request, h) => {
return 'Hello Cats!'
}
})
}
}
This is the server for the content appearing at dogs.com
. It is exactly the same as the cats
plugin, except it returns the text Hello Dogs!
. Again, this is not a useful plugin, it is for illustration only.
// ./sites/dogs.js
exports.plugin = {
name: 'dogs',
version: '1.0.0',
register: async function(server, options) {
server.route({
method: 'GET',
path: '/',
handler: (request, h) => {
return 'Hello Dogs!'
}
})
}
}
This is where the vhost
is specified, assigning plugins to hosts cats.com
and dogs.com
.
// ./server.js
const Hapi = require('hapi')
const Glue = require('glue')
const manifest = {
server: {
host: process.env.HOST || '0.0.0.0',
port: process.env.PORT || 8080
},
register: {
plugins: [
{
plugin: './sites/cats',
routes: {
vhost: 'cats.com'
}
},
{
plugin: './sites/dogs',
routes: {
vhost: 'dogs.com'
}
}
]
}
}
const options = {
relativeTo: __dirname
}
const startServer = async function () {
try {
const server = await Glue.compose(manifest, options)
await server.start()
console.log('Hapi days for Cats and for Dogs!')
}
catch (err) {
console.error(err)
process.exit(1)
}
}
startServer()
$ node server.js
Hapi days for Cats and for Dogs!
$ curl -H "Host: cats.com" localhost:8080/
Hello Cats!
$ curl -H "Host: dogs.com" localhost:8080/
Hello Dogs!
$ curl localhost:8080/
{"statusCode":404,"error":"Not Found","message":"Not Found"}
$ curl -H "Host: platypus.com" localhost:8080/
{"statusCode":404,"error":"Not Found","message":"Not Found"}
Note that there are no routes for the default host, so not specifying a host will result in a 404
being returned.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With