Create subdomains in express.js with vhost in Heroku and Namecheap

Wondering how to replicate the Apache vhost feature in Express.js? Virtual hosting is a method for hosting multiple domain names on a single server. In case you are new to vhost, it allows a web server to listen on a single port and serve different web apps depending on the domain name or the subdomain. Implementing such a feature in Express.js is very easy.

I will be using Heroku as a cloud hosting provider with Namecheap as a domain registrar and Node.js (Express.js) for the Back-End displaying my portfolio which is made with Vue.js.

The goal

  • Create and connect subdomain on the services
  • Directory structure
  • Node.js server setup

Requirements

  • Basic node.js/ express.js knowledge
  • Payed Dyno (hobby)
  • The will to learn

Lets get started!

Create and connect subdomain

Lets start with Heroku where you need to be logged in:

heroku login.PNG

After login your dynos will be displayed...

after login.PNG

  • Pick the one you want to use.
  • Go to settings

dyno settings.PNG

  • Scroll down to domains
  • Click on "Add domain"

add domain.PNG

enter subdomain.PNG

  • Copy DNS Target

after entering subdomain.PNG

It will show you "waiting" on heroku until you connect it to a domain with DNS Target

Now lets head to namecheap to connect DNS Target

  • Login

namecheap login.PNG

  • Go to Domain List

namecheap domain list.PNG

  • choose your domain and click on "manage"

namecheap manage domain list.PNG

  • Click on advanced DNS and scroll down to "host records"
  • Click "add new record"

namecheap add new record.PNG

  • Select "CNAME" from the dropdown menu in the type column
  • Enter your subdomain in the "host" column
  • Paste in your "DNS Target" in The "value" column
  • Click on the "greenish" check mark to save

namecheap add subdomain.PNG

That was it for the registeration of the subdomain

Now on heroku there would be a red flag because of the SSL certificate. It will take some time for it to be activated on the subdomain (5-10 min).

heroku wait for dns and SSL.PNG

After some time has passed it will say that everything is OK

heroku subdomain ok.PNG

Directory structure

  • Root folder
    • server.js
    • public (main website)
      • index.html
      • style.css
      • main.js
    • subdomains
      • test (project directory)
        • server.js
        • public
          • index.html
          • style.css
          • main.js

In the root directory a creation of an express app is needed npm init then npm install express. Now we create our server.js file which if not changed during the "npm package initialization process" will be named index.js by default. It can be checked in the package.json file.

  "scripts": {
    "start": "node server.js"
  }

Every website needs its own express app to be rendered by. In the root directory there is the one express app to rule them all. In our case to route the views for the specified URL.

As seen above every project directory has a server.js file which is the node server (express app) and the public directory where the website is which will be rendered in the browser.

to serve are the website files e.g. index.html / style.css / main.js. It is needed to put them all in one directory e.g. public / client / frontend because we will be serving/ rendering the view from server.js.

The subdomain directory will hold all the other directories of the individual projects which will be rendered when visiting the registered subdomain in our case it will be test.ahmedaltaai.com

Nodejs server setup

Lets take a look at server.js for displaying the main website

// importing express
const express = require('express')

// The path module provides utilities for working with 
// file and directory paths
const path = require('path')

// app is an instance of express
// why app? I DON'T KNOW it is the law
const app = express()

// serves files from public directory 
// which contains the main website
const website = express.static(path.join(__dirname, '/public')

// Mounts the specified middleware function or functions 
// at the specified path: the middleware function is executed 
// when the base of the requested path matches path.
app.use(website)

// routes an HTTP request, where METHOD is the HTTP 
// method of the request, such as 
// GET, PUT, POST, and so on, in lowercase. 
app.get('/', (req, res) => {
// The path.join() method joins all given path segments together
// using the platform-specific separator as a delimiter, 
// then normalizes the resulting path.
 res.render(path.join(__dirname, '/public'))
})

// in many environments (e.g. Heroku), and as a convention,
// you can set the environment variable PORT 
// to tell your web server what port to listen on.
const port = process.env.PORT || 8080

// binds and listens for connections on the specified host and port.
app.listen(port)

// just logging out on which port the app is listening
console.log("listening on... " + port)

Now when visiting yourwebsite.com the index.html inside the public directory will be rendered.

Now lets use vhost to display the subdomains

To display the subdomains we need to create a module out of the test directory's express app which is named server.js

It is nearly the same as a normal express app except we won't use app.listen(...) rather we will export it as a module and make it run on the same port and server which is being used for the main website.

test subdomains express app

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

app.use(express.static(path.join(__dirname, './public')))

app.get('/', (req, res) => {
  res.render(path.join(__dirname, './public'))
})

module.exports = app

Now lets configure vhost in the root express app.

vhost needs to be installed through npm in the root directory npm install vhost then import it as we did with express and path with require('vhost')

main express app

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

const website = express.static(path.join(__dirname, '/public')

// it is important to have it at the top 
// before app.use(website) otherwise it wont work
// the express app which we exported 
// is being requires thus it is being executed
// also the code says it should render a view
app.use(vhost('test.ahmedaltaai.com', require('./subdomains/test/public/app')))

app.use(website)

app.get('/', (req, res) => {
 res.render(path.join(__dirname, '/public'))
})

const port = process.env.PORT || 8080

app.listen(port)

console.log("listening on... " + port)

The repository can be found here

I hope you learned something new

No Comments Yet