#100DaysOfMERN - Day 23

#100DaysOfMERN - Day 23

·

9 min read

✏ Serving Static Files with Express Server

In the last post, I explored how to serve static files with a Vanilla JS server, and it was quite a bit of code. I had to manually set the Content-Type, and to do that, I had to get the extension of the requested file. Finally, I needed the fs module to read the content of the requested file to send it back to the client.

All of this gets much easier with the Express framework, because most of it is being handled for me under the hood.

First of all, I'll create an express.html, which I'd like to serve if someone visits the main directory (instead of an index.html):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2280%22>&#128420;</text></svg>">
    <title>Express</title>
</head>
<body style="font-family:sans-serif">
    <h1>I am a webpage!</h1>
    <p>brought to you with Express JS</p>
</body>
</html>

Now for the server that I've already set up on Day 21:

express.js

import express from 'express';
const app = express();

app.get('/', (req, res) => {
    res.send('<h1>Express is Serving</h1>');
})

app.listen(3001, ()=>console.log('express is serving'))

Instead of the hard-coded <h1>, I want to serve my HTML file now.

One thing that tripped me up first when trying this out: Not making sure that I get the right file path.


✏ Relative and Absolute File Paths

For the Vanilla Server, I used fs.readFile(path), and that function takes a relative path (relative to the root directory that the vanilla.js server file is in). Using the .sendFile method of Express, I'd either have to include a configuration object to tell it what's the absolute path of the root directory, or get the absolute path manually.

import express from 'express';
import path from 'path';

const app = express();

app.get('/', (req, res) => {
    let filePath = '.' + req.url;
    if (filePath === './'){
        filePath += 'express.html'
    }

    // with relative path:
    const rootDir = path.resolve('./');
    res.sendFile(filePath, {root: rootDir})

    // with absolute path:
    const rootDir = path.resolve('./');
    filePath = path.join(rootDir, filePath);
    res.sendFile(filePath)
}

app.listen(3001, ()=>console.log('express is serving'))

Here, I've used the path.join method, which as far as I can see just inserts slashes between the given arguments, so path.join(dir1, dir2, file.ext) would return a string dir1\dir2\file.ext.

This is already working, my file gets served:

server-express-html-file.jpg

However, Express offers a much simpler way to serve static files, but first, a few words on Middleware.


✏ Middleware

Middleware is just a function that has access to the response and request objects, and that can be used to run any code you like when someone makes a request to the server. It also gives you a next function (commonly named as such, but you can rename it if you like), that needs to be called in order to proceed to the next function execution. Express offers a list of ready-made middleware that you can use, or you can write your own custom function.

To demonstrate the basic principle, I'll write a simple logger middleware, to log some details of the request object, and then invoking it with app.use, as soon as the server receives a request, for example to localhost:3001/test.html:

const logger = (req, res, next) => {
    console.log(`${req.protocol}://${req.get('host')}${req.originalUrl}`);
    next();
}

app.use(logger) // http://localhost:3001/test.html

✏ Setting a Static Folder in Express

Let's say I have a number of static files (index.html, logo.jpg, about.html, products.html etc) in a folder called public. Instead of writing the logic myself in app.get(path, fn), all that can be replaced with one line, because Express already provides a middleware for this:

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

Whenever I make a GET request to localhost:3001/filename.ext(not to localhost:3001/public/filename.ext), Express will find the file and send it back, including all necessary Headers. If the request URL is the root directory and there's an index.html in the folder, Express will handle that too.

This will terminate the execution of the server script, so if you're using the logger middleware afterwards, it won't get called.

Note: If you use require instead of import, you'd have to change it to

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

So the advantages of Express are obvious. To have the same functionality with Vanilla JS, I'd have to write a lot more code, and Express offers a lot more than the above, which I'll cover in the next post.


✏ Recap

I've learned

  • how to serve a static HTML file with an Express JS server

✏ Next:

  • more features of Express JS

✏ Thanks for reading!

I do my best to thoroughly research the things I learn, but if you find any errors or have additions, please leave a comment below, or @ me on Twitter. If you liked this post, I invite you to subsribe to my newsletter. Until next time 👋


✏ Previous Posts

  • Day 1: Introduction, Node.js, Node.js in the terminal
  • Day 2: npm, node_modules, package.json and package-lock.json, local vs global installation of packages
  • Day 3: Create a React app without create-react-app, Webpack, Babel
  • Day 4: npx and cowsay
  • Day 5: npm vs. npx, npm audit, semantic versioning and update rules
  • Day 6: Call stack, event loop, JavaScript engine, JavaScript runtime
  • Day 7: Call stack and event loop in Node.js, setImmediate()
  • Day 8: setImmediate(), process.nextTick(), event loop phases in Node.js
  • Day 9: Network requests with XMLHttpRequest and callbacks
  • Day 10: Promises
  • Day 11: Network requests with XMLHttpRequest and Promises
  • Day 12: React Quiz App part 1
  • Day 13: React Hangman
  • Day 14: FullStackOpen course 1: details of GET and POST requests, request headers
  • Day 15: React Hangman: Trigger fetch with click event callback vs useEffect
  • Day 16: REST API and CRUD
  • Day 17: Boring Book App part 1: React Frontend, Express Backend, GET requests, CORS
  • Day 18: Boring Book App part 2: POST request, File System API
  • Day 19: Boring Book App part 3: Request Parameters, DELETE request
  • Day 20: Boring Book App part 4: PUT request
  • Day 21: Express JS vs Vanilla JS part 1: Server setup, routes
  • Day 22: Express JS vs Vanilla JS part 2: Serve static files with Vanilla Server