Intro to Express, Templating and API's (EJS, Handlebars, Mustache, Pug)

Alex Merced - Mar 4 '21 - - Dev Community

Express is by far the most popular backend web framework in the NodeJS world. It is a right of passage for every javascript developer to learn how to create full-stack applications and APIs.

In this blog post, we'll introduce you to setting up an express project, working with the most popular templating libraries for server-side rendered websites, and how to create APIs to send JSON data to use React/Angular/Vue/Svelte to create client-side rendered websites.

Prerequisites

To follow this tutorial you need to have the following installed

  • nodeJS 14 or later
  • A Web Browser (Chrome)
  • A Text Editor (Visual Studio Code)
  • Bash Terminal (the native terminal on Mac/Linux, download git-bash for windows)

Setup

Open up your editor and terminal to an empty folder for the project and follow the following directions:

  • create a fresh npm project by running

    npm init -y

    which will create a fresh package.json file that will track our dependencies.

  • run the command

    npm install express nodemon

    this will install the two libraries and create a node_modules folder that holds all the libraries we will install. Notice both libraries have been added in the dependencies section of the package.json file.

  • create a views folder, this folder will be used for holding all our templates later on.

  • create a public folder for demonstrating static folders later on

Round 1 - Serving a Static HTML File

  • create a file server_static.js

  • adjust the scripts in your package.json to look like this

"scripts": {
    "static": "nodemon server_static.js"
  }
Enter fullscreen mode Exit fullscreen mode
  • add the following to file console.log("it works")

  • run the command npm run static

You'll notice nodemon begins watching your files and you'll see your log (nodemon restarts your server when you make changes to its source code). Whenever we say npm run <command> it will run a command by the same name from package.json scripts, neat! To have nodemon stop watching your files use ctrl+c or command+c to kill the process in your terminal

  • Now enter the following code, read the comments to understands what it is doing.
// We are importing the express library
const express = require("express")

// We use express to create an application object that represents our server
const server = express()


// Tell our server serve the assets in public folder statically
server.use(express.static("public"))


// Tell our server to start listening for requests on port 1985, my birth year 😆
// Second argument is a function we can use to post a message we are up and running!
server.listen(1985, () => console.log("🤠 Server Listening on Port 1985 🤠"))
Enter fullscreen mode Exit fullscreen mode
  • add a file called index.html inside of public like so
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>This folder is served statically. When you go to the base url it will look for an index.html in this folder as the main page.</h1>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode
  • run your server with the command npm run static then go to localhost:1985 in your browser and you'll see the html file load up! Ta da, you just served a static site with express!

EJS Templating

There are many templating languages. The way they work is you'll create a template that can have placeholders (variables). So we will use a render function to tell express to render the template along with some data to fill those variables.

  • first let's install ejs npm install ejs

  • create another file server_ejs.js

  • let's add another script in package.json

  "scripts": {
    "static": "nodemon server_static.js",
    "ejs": "nodemon server_ejs.js"
  }
Enter fullscreen mode Exit fullscreen mode
  • Put the following in server_ejs.js
// We are importing the express library
const express = require("express")

// We use express to create an application object that represents our server
const server = express()

// We Tell Express to Look for ejs files when we use the render function
// templates are by default looked for in the views folder
server.set("view engine", "ejs")

// We create a "route" for the root url "/" what will render a template
server.get("/", (request, response) => {
    // the render function takes two arguments
    // 1. the file to look for in the views to use as the template
    // 2. an object of data to make available to the template
    response.render("index", {
        cheese: "gouda",
        bread: "rye"
    })
})

// Tell our server to start listening for requests on port 1985, my birth year 😆
// Second argument is a function we can use to post a message we are up and running!
server.listen(1985, () => console.log("🤠 Server Listening on Port 1985 🤠"))
Enter fullscreen mode Exit fullscreen mode
  • in the views folder make a file called index.ejs with the following
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1><%= cheese %></h1>
    <h1><%= bread %></h1>

</body>
</html>
Enter fullscreen mode Exit fullscreen mode
  • Run the server with npm run ejs then go to localhost:1985 and you'll see our data got injected in the template!!!

Learn EJS Syntax Here

Mustache

  • first, let's install mustache npm install mustache-express

  • create another file server_mustache.js

  • let's add another script in package.json

  "scripts": {
    "static": "nodemon server_static.js",
    "ejs": "nodemon server_ejs.js",
    "mustache": "nodemon server_mustache.js",
  }
Enter fullscreen mode Exit fullscreen mode
  • Put the following in server_mustache.js
// We are importing the express library
const express = require("express")

//import mustache-express
const mustache = require('mustache-express')

// We use express to create an application object that represents our server
const server = express()

// We Tell Express to Look for mustache files when we use the render function
// templates are by default looked for in the views folder
server.engine('mustache', mustache()) //Change the view engine
server.set("view engine", "mustache")

// We create a "route" for the root url "/" what will render a template
server.get("/", (request, response) => {
    // the render function takes two arguments
    // 1. the file to look for in the views to use as the template
    // 2. an object of data to make available to the template
    response.render("index", {
        cheese: "munster",
        bread: "wheat"
    })
})

// Tell our server to start listening for requests on port 1985, my birth year 😆
// Second argument is a function we can use to post a message we are up and running!
server.listen(1985, () => console.log("🤠 Server Listening on Port 1985 🤠"))
Enter fullscreen mode Exit fullscreen mode
  • in the views folder make a file called index.mustache with the following
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>{{cheese}}</h1>
    <h1>{{bread}}</h1>

</body>
</html>
Enter fullscreen mode Exit fullscreen mode
  • Run the server with npm run mustache then go to localhost:1985 and you'll see our new data got injected in the template!!!

Learn Mustache Syntax Here

Handlebars

  • first, let's install handlebars npm install express-handlebars

  • create another file server_handlebars.js

  • let's add another script in package.json

  "scripts": {
    "static": "nodemon server_static.js",
    "ejs": "nodemon server_ejs.js",
    "mustache": "nodemon server_mustache.js",
    "handlebars": "nodemon server_handlebars.js"
  }
Enter fullscreen mode Exit fullscreen mode
  • Put the following in server_handlebars.js
// We are importing the express library
const express = require("express")

//import mustache-express
const handlebars = require('express-handlebars')

// We use express to create an application object that represents our server
const server = express()

// We Tell Express to Look for mustache files when we use the render function
// templates are by default looked for in the views folder
server.engine('handlebars', handlebars()) //Change the view engine
server.set("view engine", "handlebars")

// We create a "route" for the root url "/" what will render a template
server.get("/", (request, response) => {
    // the render function takes two arguments
    // 1. the file to look for in the views to use as the template
    // 2. an object of data to make available to the template
    response.render("index", {
        cheese: "pepper jack",
        bread: "oat"
    })
})

// Tell our server to start listening for requests on port 1985, my birth year 😆
// Second argument is a function we can use to post a message we are up and running!
server.listen(1985, () => console.log("🤠 Server Listening on Port 1985 🤠"))
Enter fullscreen mode Exit fullscreen mode
  • in the views folder make a folder called layouts and make a main.handlebars with the following
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
{{{body}}}

</body>
</html>
Enter fullscreen mode Exit fullscreen mode
  • then in the views folder make an index.handlebars with the following
    <h1>{{{cheese}}}</h1>
    <h1>{{{bread}}}</h1>
Enter fullscreen mode Exit fullscreen mode
  • Run the server with npm run handlebars then go to localhost:1985 and you'll see our new data got injected in the template!!!

Learn Handlebars Syntax Here

Pug

  • first, let's install pug npm install pug

  • create another file server_pug.js

  • let's add another script in package.json

  "scripts": {
    "static": "nodemon server_static.js",
    "ejs": "nodemon server_ejs.js",
    "mustache": "nodemon server_mustache.js",
    "handlebars": "nodemon server_handlebars.js",
    "pug": "nodemon server_pug.js"
  }
Enter fullscreen mode Exit fullscreen mode
  • Put the following in server_pug.js
// We are importing the express library
const express = require("express")

// We use express to create an application object that represents our server
const server = express()

// Set the view engine to pug
server.set('view engine', 'pug')

// We create a "route" for the root url "/" what will render a template
server.get("/", (request, response) => {
    // the render function takes two arguments
    // 1. the file to look for in the views to use as the template
    // 2. an object of data to make available to the template
    response.render("index", {
        cheese: "brie",
        bread: "corn"
    })
})

// Tell our server to start listening for requests on port 1985, my birth year 😆
// Second argument is a function we can use to post a message we are up and running!
server.listen(1985, () => console.log("🤠 Server Listening on Port 1985 🤠"))
Enter fullscreen mode Exit fullscreen mode
  • then in the views folder make an index.pug with the following
    h1 hello world #{cheese} & #{bread}
Enter fullscreen mode Exit fullscreen mode
  • Run the server with npm run pug then go to localhost:1985 and you'll see our new data got injected in the template!!!

Learn Pug Syntax Here

Express API

So far we have served files statically and generated web pages using server-side templating engines. Another thing we can do is have our routes return JSON data instead of a web page. This way we can build APIs that allow us to instead build our frontend using a client-side UI library/framework like jQuery, React, Angular, Svelte, and Vue.

  • create a new file, server_api.js

  • since the fetch function doesn't exist natively in node as it does in the browser we can use using the node-fetch library. Install it, npm install node-fetch.

  • add a new script to package.json

  "scripts": {
    "static": "nodemon server_static.js",
    "ejs": "nodemon server_ejs.js",
    "mustache": "nodemon server_mustache.js",
    "handlebars": "nodemon server_handlebars.js",
    "pug": "nodemon server_pug.js",
    "api": "nodemon server_api.js"
  }
Enter fullscreen mode Exit fullscreen mode
  • in the server_api.js add this
// We are importing the express library
const express = require("express")

// Make the fetch function available with node-fetch to call other apis
const fetch = require("node-fetch")

// We use express to create an application object that represents our server
const server = express()

// We create a "route" for the root url "/" what will return a javascript object as json
server.get("/", (request, response) => {

    //any object or array send via response.json will be sent as json data
    response.json({
        cheese: "cheddar",
        bread: "italian",
        // the request.query object contains an url queries from the request
        query: request.query
    })

})

// A second route using a url param to grab data from the url, any part of the url with a : is a variable
server.get("/proxy/:num", (request, response) => {
    // Before we send a response we can get a data from other sources like databases or other apis
    // We will call data from another api and use our url param to modify the call
    // all the params are made available in the request.params object
    const id = request.params.num
    //notice we interpolate the param in the url so you can grab different post by number
    fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
  .then(response => response.json())
  .then(data => response.json({
      params: request.params,
      data
  }))
})

// Tell our server to start listening for requests on port 1985, my birth year 😆
// Second argument is a function we can use to post a message we are up and running!
server.listen(1985, () => console.log("🤠 Server Listening on Port 1985 🤠"))
Enter fullscreen mode Exit fullscreen mode
  • start your server with npm run api

  • test the main url without a url query localhost:1985

  • then test the main url with a query localhost:1985?cheese=gouda&bread=rye

  • then test the second route with multiple numbers

    • localhost:1985/proxy/1
    • localhost:1985/proxy/2
    • localhost:1985/proxy/3

The beauty of making API calls from your backend server is that you can avoid CORS errors. So instead your frontend would make a request to your server who delivers the API data as a go-between (AKA, a Proxy).

Conclusion

What have we learned?

  • Installing npm libraries
  • adding and using scripts to package.json
  • how to create an express server that serves static files
  • how to create an express server that renders templates
  • how to create an express server that sends JSON data
  • how to use URL queries in your express routes
  • how to use URL params in your express routes
  • how to call an API in your routes

Your express applications can do all of the above at the same time, be creative and experiment. You'll find express to be a very flexible platform to build the backend services your application needs.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .