Search code examples
javascriptnode.jshttpexpress-handlebars

Can't get express-handlebars render an HTML page


I'm following a NodeJS tutorial on how to render an HTML page, but I can't seem to make it work. I installed all of the requirements and use the --save option so it's inside my node_modules folder. After I do npm start and visit my localhost, this is the error message I get:

Error: No default engine was specified and no extension was provided.
        at new View (C:\Users\Billy\NodeJSProjects\HelloWorld\node_modules\express\lib\view.js:62:11)
        at EventEmitter.render (C:\Users\Billy\NodeJSProjects\HelloWorld\node_modules\express\lib\application.js:570:12)
        at ServerResponse.render (C:\Users\Billy\NodeJSProjects\HelloWorld\node_modules\express\lib\response.js:966:7)
        at app.get (C:\Users\Billy\NodeJSProjects\HelloWorld\app\index.js:25:12)
        at Layer.handle [as handle_request] (C:\Users\Billy\NodeJSProjects\HelloWorld\node_modules\express\lib\router\layer.js:95:5)
        at next (C:\Users\Billy\NodeJSProjects\HelloWorld\node_modules\express\lib\router\route.js:137:13)
        at Route.dispatch (C:\Users\Billy\NodeJSProjects\HelloWorld\node_modules\express\lib\router\route.js:112:3)
        at Layer.handle [as handle_request] (C:\Users\Billy\NodeJSProjects\HelloWorld\node_modules\express\lib\router\layer.js:95:5)
        at C:\Users\Billy\NodeJSProjects\HelloWorld\node_modules\express\lib\router\index.js:281:22
        at Function.process_params (C:\Users\Billy\NodeJSProjects\HelloWorld\node_modules\express\lib\router\index.js:335:12)

./index.js

require('./app/index')

const path = require('path')
const express = require('express')
const exphbs = require('express-handlebars')


const app = express()

app.engine('.hbs', exphbs({
  defaultLayout: 'main',
  extname: '.hbs',
  layoutsDir: path.join(__dirname, 'views/layouts')
}))
app.set('view engine', '.hbs')
app.set('views', path.join(__dirname, 'views'))

./app/index.js

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

app.use((request, response, next) => {
  console.log(request.headers)
  next()
})

app.get('/', (request, response) => {
  response.render('home', {
    name: 'John'
  })
})

app.get('/', (request, response) => {
  throw new Error('oops')
})

app.use((err, request, response, next) => {
  // log the error, for now just console.log
  console.log(err)
  response.status(500).send('Something broke!')
})

app.listen(3000)

(auto-generated) package.json

{
  "name": "name-first-nodejs-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "mocha test",
    "your-custom-script": "echo npm"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "async": "^2.3.0",
    "express": "^4.15.2",
    "express-handlebars": "^3.0.0",
    "handlebars": "^4.0.6",
    "lodash": "^4.17.4",
    "promise-async": "^0.2.0"
  },
  "devDependencies": {
    "mocha": "^3.3.0"
  }
}

./views/layouts/main.hbs

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Express handlebars</title>
  </head>
  <body>
    {{{body}}}
  </body>
</html>

./views/home.hbs

<h2>Hello {{name}}</h2>

Solution

  • You are making two separate app objects and configuring handlebars on one of them and configuring your routes on the other one. Thus, the one with your routes is not configured for handlebars. You need to have only one app object that you share between your two modules. You can export it from one and import that module into the other or pass the app object to the other with a module constructor or a method after you've loaded the module to get access to the single shared app object from the other module.

    For example:

    ./index.js

    const path = require('path');
    const express = require('express');
    const exphbs = require('express-handlebars');
    
    const app = express();
    
    app.engine('.hbs', exphbs({
      defaultLayout: 'main',
      extname: '.hbs',
      layoutsDir: path.join(__dirname, 'views/layouts')
    }));
    app.set('view engine', '.hbs');
    app.set('views', path.join(__dirname, 'views'));
    
    // now that handlebars is configured,
    // configure all our routes on this app object
    require('./app/index')(app);
    

    ./app/index.js

    module.exports = function(app) {
    
        app.use((request, response, next) => {
          console.log(request.headers);
          next();
        });
    
        app.get('/', (request, response) => {
          response.render('home', {
            name: 'John'
          });
        });
    
        app.use((err, request, response, next) => {
          // log the error, for now just console.log
          console.log(err)
          response.status(500).send('Something broke!')
        });
    
        app.listen(3000);
    
    };