Lesson Overview

In the previous lesson on Express MVC, you handled requests for basic HTML/EJS templates. But what if those templates referenced CSS files, images, or client-side JavaScript files? How do we send those back to the client in the response?

Recall that when a web page is requested and received by the browser, the browser renders the HTML line by line. When it encouters a line such as a <link> to a CSS stylesheet, or an <img> source, or a <script src=""> for a JavaScript file, it has to make another, separate request for each of these files. This means that your server needs to also handle these requests. You will need to add middleware to handle requests for those kinds of static files. We did a very basic and Express-less version of this in the lesson on Basic Routing with Node.js. In this lesson, we'll learn how to use Express middleware to handle these requests in a way that allows for more modular, re-usable, and extensible code.

Pre-Requisites

Before doing this lesson, make sure you've gone through the Express Model View Controller tutorial.

Serving Static Files

Static files such as images, CSS, and client-side Javascript can be handled by using the express.static() middleware function. It takes two arguments:

So if a web page contains a <link rel="stylesheet" href="css/main.css">, the browser will make a request for that file. In this case, the req.url will be css/main.css. If your static files are in /public of your main project, you'll need to set up the static middleware as:

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

This assumes that express.static() is called from a file taht's in the same directory as /public. However, what if you are invoking express.static() from somewhere else? It's safer to use the Node.js path module when dealing with directory names and paths:

const path = require("path");
...
app.use(express.static(path.join(__dirname, "public")));

The path.join() joins segments into a full path. It's much easier to do this than fiddling around with the right direction of slash, file/directory names, etc.

__dirname (2 underscores in front of "dirname") contains the full absolute path to the project's root. This is an easy way to ensure that you have the right path/location, regardless of where the file is that is invoking express.static() (or any other code that needs a correct path to a file).

So the code above will handle any request for a static file and it will look for and load the file that's in the project root directory's /public. For example, if the request is for css/main.css and the full absolute path is /users/home/mystore, then path.join() will create the path /users/home/mystore/public.

If your project's URL is in a virtual directory, you can add it to app.use() as a mount path. For example, if your project is mystore.com and the project is for the past and present orders with a url of mystore.com/orders, then you might use the mount path like this:

app.use("/orders", express.static(path.join(__dirname, "public")));

Practical Example

Let's try an example. Go back to the program from the previous lesson on Express MVC that displayed a menu for a specific day of the week.

Add the /public directory to your project root, and add the subdirectories /public/css and /public/images. Add a CSS file to /public/css and add an image file to /public/images.

Reference your CSS file in your index.ejs file, for example:

<link rel="stylesheet" href="css/main.css">

Now add your image somewhere on the page:

<div><img src="images/ArtiDisdain.jpg" 
  alt="Arti the cat looking disdainful"></div>

Now add the middleware to your app.js file. Make sure this goes after the app.get("/menu:day"): the middleware to load static files should happen after all other requests: you don't want it executing before other .get() and .post() endpoints.

app.use("/menu", express.static(path.join(__dirname, "public")));

If you restart your application and load your menu page, you should notice that your styling and image works!