Loading

Quipoin Menu

Learn • Practice • Grow

node-js / Serving HTML Files
tutorial

Serving HTML Files

Sending plain text or small HTML strings is fine for testing, but real websites have proper HTML files with content, CSS, and JavaScript. In this chapter, you'll learn how to read HTML files from disk and serve them to the browser.

Why Serve HTML Files?

  • Separation of concerns: Keep HTML separate from your server logic.
  • Maintainability: Edit HTML files without touching your Node.js code.
  • Easier design: Designers can work on HTML files directly.
  • Real websites: Most websites have multiple HTML pages.

Think of your server as a waiter who doesn't cook, but instead brings pre-made dishes from the kitchen. The HTML files are those pre-made dishes.

Project Structure

Create a folder structure like this:
my-server/
├── server.js
├── views/
│ ├── index.html
│ ├── about.html
│ └── 404.html

Sample HTML Files
views/index.html
<!DOCTYPE html>
<html>
<head>
  <title>Home Page</title>
</head>
<body>
  <h1>Welcome to My Website</h1>
  <p>This is the home page.</p>
  <a href="/about">About</a>
</body>
</html>

views/about.html
<!DOCTYPE html>
<html>
<head>
  <title>About Us</title>
</head>
<body>
  <h1>About Us</h1>
  <p>We are learning Node.js!</p>
  <a href="/">Back to Home</a>
</body>
</html>

views/404.html
<!DOCTYPE html>
<html>
<head>
  <title>Page Not Found</title>
</head>
<body>
  <h1>404 - Page Not Found</h1>
  <p>The page you requested does not exist.</p>
  <a href="/">Go Home</a>
</body>
</html>

Serving HTML Files with Node.js

We'll use the `fs` module to read the HTML files and the `path` module to build correct file paths.
const http = require('http');
const fs = require('fs');
const path = require('path');

const server = http.createServer((req, res) => {
  <!-- Build file path based on URL -->
  let filePath = path.join(__dirname, 'views', req.url === '/' ? 'index.html' : req.url + '.html');
 
  <!-- Read the file -->
  fs.readFile(filePath, (err, content) => {
    if (err) {
      <!-- If file not found, serve 404 page -->
      fs.readFile(path.join(__dirname, 'views', '404.html'), (err404, content404) => {
        res.writeHead(404, { 'Content-Type': 'text/html' });
        res.end(content404, 'utf8');
      });
    } else {
      res.writeHead(200, { 'Content-Type': 'text/html' });
      res.end(content, 'utf8');
    }
  });
});

server.listen(3000, () => {
  console.log('Server running on port 3000');
});

Explanation
  1. We build the file path using `path.join()` and `__dirname` to ensure it works on all operating systems.
  2. If the URL is `/`, we serve `index.html`. Otherwise, we try to serve `URL + '.html'` (e.g., `/about` → `about.html`).
  3. We use `fs.readFile()` to read the file asynchronously.
  4. If there's an error (file not found), we serve the 404 page with a 404 status code.

A Better Approach: Using a Function

We can create a helper function to serve files:
function serveFile(res, filePath, contentType, statusCode = 200) {
  fs.readFile(filePath, (err, content) => {
    if (err) {
      res.writeHead(500);
      res.end('Server Error');
    } else {
      res.writeHead(statusCode, { 'Content-Type': contentType });
      res.end(content);
    }
  });
}

<!-- Usage -->
const server = http.createServer((req, res) => {
  if (req.url === '/') {
    serveFile(res, path.join(__dirname, 'views', 'index.html'), 'text/html');
  } else if (req.url === '/about') {
    serveFile(res, path.join(__dirname, 'views', 'about.html'), 'text/html');
  } else {
    serveFile(res, path.join(__dirname, 'views', '404.html'), 'text/html', 404);
  }
});

Two Minute Drill

  • Store HTML files in a separate folder (e.g., `views`).
  • Use `fs.readFile()` to read HTML files from disk.
  • Set correct `Content-Type: text/html`.
  • Handle 404 errors by serving a custom 404 page.
  • Use `path.join()` and `__dirname` for reliable file paths.
  • This approach keeps your server logic separate from your HTML content.

Need more clarification?

Drop us an email at career@quipoinfotech.com