⇦ Back

WDX-180

Web Development X

Express + EJS Fundamentals

Building Your First Server-Rendered Web Application

“A web framework is not magic. It is simply a collection of abstractions that make common tasks easier.”

Today, we’ll pull back the curtain and understand what Express and EJS are actually doing.

Learning Objectives

By the end of this lesson, students will be able to:

Before We Begin

What Are We Building?

Throughout this course we will gradually build a Product CMS.

A CMS (Content Management System) allows users to manage data through a web interface.

Examples:

Our CMS will allow users to:

This is known as CRUD:

Operation Meaning
Create Add data
Read View data
Update Modify data
Delete Remove data

Today we are building the foundation.

No database yet.

No authentication yet.

Just Express, EJS, and understanding how web applications work.

What Is Express?

The Problem

Node.js can already create web servers.

Example:

  const http = require('http');

  http.createServer((req, res) => {
    res.end('Hello World');
  }).listen(3000);

But building real applications this way becomes painful.

We would need to manually:

Express solves these problems.

What Express Gives Us

Express provides:

Routing

  app.get('/', () => {});
  app.post('/products', () => {});

Middleware

  app.use(...)

Request Helpers

  req.body
  req.params
  req.query

Response Helpers

  res.render(...)
  res.json(...)
  res.redirect(...)

Express is essentially a thin layer on top of Node’s HTTP server.

Project Setup

Create Project Folder

  mkdir express-crud
  cd express-crud

Initialize Node Project

  npm init -y

This creates a package.json that looks like this:

  {
    "name": "express-crud",
    "version": "1.0.0",
    ...
  }

Install Dependencies

  npm install express ejs express-ejs-layouts

What Did We Install?

Package Purpose
express Web framework
ejs Template engine
express-ejs-layouts Layout support

Add Scripts

  {
    "scripts": {
      "start": "node index.js",
      "dev": "node --watch index.js"
    }
  }

Now:

  npm run dev

automatically reloads the server whenever files change.

Your future self will thank you.

Security Considerations

Before installing any dependencies, it’s important to run some safety checks and ensure that you are not installing an infected or malicious npm module.

Here are some steps to protect you:

Version Control

Of course, every serious projects needs some kind of version control.

Before you start this project make sure to initialize the project folder as a git repository by running git init.

Now, every time you complete a step of this module or implement a feature, make sure to stage (git add) and commit (git commit) your changes.

Project Structure

Professional projects rely on predictable structure.

  express-crud/
  ├── index.js
  ├── package.json
  ├── views/
  │   ├── layout.ejs
  │   ├── index.ejs
  │   └── partials/
  │       ├── header.ejs
  │       └── footer.ejs
  ├── public/
  │   └── css/
  │       └── style.css

Creating Our First Express Server

Create:

  // index.js

  const express = require('express');

  const app = express();

  app.get('/', (req, res) => {
    res.send('Hello Express!');
  });

  app.listen(3000, () => {
    console.log('Server running at http://localhost:3000');
  });

Run:

  npm run dev

Visit:

  http://localhost:3000

Congratulations.

You now have a working web server.

You’re officially serving bytes to strangers.

Or at least to yourself.

Understanding Routes

A route defines:

  HTTP METHOD + URL

Examples:

Method URL Purpose
GET / Homepage
GET /products List products
GET /products/1 View product
POST /products Create product

Example:

  app.get('/about', (req, res) => {
    res.send('About Page');
  });

Now:

  localhost:3000/about

returns:

  About Page

Middleware

Middleware is one of the most important concepts in Express.

A middleware function sits between:

  Request
    ↓
  Middleware
    ↓
  Route
    ↓
  Response

Example:

  app.use((req, res, next) => {
    console.log(req.method, req.url);
    next();
  });

Every request now gets logged.

Why Middleware Exists

Middleware can:

Most Express applications are simply chains of middleware.

Serving Static Files

Websites usually contain:

These are called static assets.

Create:

  app.use(express.static('public'));

Now:

  public/css/style.css

becomes accessible through:

  /css/style.css

Create:

  body {
    font-family: sans-serif;
  }

Introducing EJS

So far we have been sending strings:

  res.send('Hello');

Real applications send HTML.

Instead of writing HTML inside JavaScript, we use templates.

This is where EJS comes in.

What Is EJS?

EJS means:

  Embedded JavaScript

It allows JavaScript inside HTML.

Example:

  <h1><%= title %></h1>

If:

  title = 'Products'

Output becomes:

  <h1>Products</h1>

Configuring EJS

index.js:

  const path = require('node:path');

  app.set('view engine', 'ejs');
  app.set('views', path.join(__dirname, 'views'));

Create:

  app.get('/', (req, res) => {
    res.render('index', {
      title: 'Products CMS'
    });
  });

Create:

  <!-- views/index.ejs -->

  <h1><%= title %></h1>

The page now renders dynamically.

Passing Data to Views

Let’s pass an array.

  const products = [
    { name: 'Keyboard' },
    { name: 'Mouse' },
    { name: 'Monitor' }
  ];

  app.get('/', (req, res) => {
    res.render('index', {
      title: 'Products',
      products
    });
  });

View:

  <h1><%= title %></h1>

  <ul>
    <% products.forEach(product => { %>
      <li><%= product.name %></li>
    <% }) %>
  </ul>

Output:

  Keyboard
  Mouse
  Monitor

EJS Syntax

Output Escaped Data

  <%= name %>

Recommended for user data.

Protects against XSS attacks.

Execute JavaScript

  <% products.forEach(...) %>

No output.

Only logic.

Output Raw HTML

  <%- html %>

Dangerous unless trusted.

Avoid when possible.

Layouts

Imagine having:

  <header>...</header>
  <footer>...</footer>
  <nav>...</nav>

copied into 30 pages.

That becomes a maintenance nightmare.

Layouts solve this.

Enable Layouts

index.js:

  const expressLayouts = require('express-ejs-layouts');

  app.use(expressLayouts);

layout.ejs

  <!doctype html>
  <html>
  <head>
    <title><%= title %></title>
  </head>
  <body>

    <%- include('partials/header') %>

    <main>
      <%- body %>
    </main>

    <%- include('partials/footer') %>

  </body>
  </html>

Partials

Partials are reusable pieces of UI.

Header Section

views/partials/header.ejs:

  <header>
    <h1>Products CMS</h1>
  </header>

Footer Section

views/partials/footer.ejs:

  <footer>
    <p>Copyright 2026</p>
  </footer>

Benefits:

This follows the DRY principle:

Don’t Repeat Yourself

Building a Small Product List

Route

  const products = [
    {
      id: 1,
      name: 'Keyboard',
      price: 49.99
    },
    {
      id: 2,
      name: 'Mouse',
      price: 19.99
    }
  ];

  app.get('/', (req, res) => {
    res.render('index', {
      title: 'Products',
      products
    });
  });

View

  <h2>Products</h2>

  <ul>
    <% products.forEach(product => { %>

      <li>
        <strong><%= product.name %></strong>

        -

        $<%= product.price.toFixed(2) %>
      </li>

    <% }) %>
  </ul>

Common Beginner Mistakes

1. Forgetting to Install Dependencies

  npm install express ejs

2. Wrong Views Folder

  app.set('views', ...)

must point to the correct directory.

3. Forgetting Middleware

  app.use(express.urlencoded({ extended: true }));

Without it:

  req.body

will be empty.

4. CSS Not Loading

Ensure:

  app.use(express.static('public'));

exists.

5. Using <%- %> Everywhere

Never trust user input.

Use:

  <%= value %>

unless you specifically need raw HTML.

Key Takeaways

Today you learned:

Assignment

Level 1

Create a page that displays:

  Your Name
  Your Favourite Programming Language
  Your Years of Experience

using EJS variables.

Level 2

Create an array of 5 products.

Render them dynamically using a loop.

Level 3

Add a navigation bar partial.

Include:

Bonus Challenge

Create:

  GET /about

Render an EJS page explaining:


⚠️ A large part of the content of this module was created using Generative AI (ChatGPT). The synthetic (AI-generated) content was reviewed and curated by Kostas Minaidis.


Project maintained by in-tech-gration Hosted on GitHub Pages — Theme by mattgraham