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:
- root - the root location where static files are
located. In an MVC application, this is probably
going to be the /public directory in the project
root, so "./public" is usually what you'll use for
this argument.
- options (optional) - an object containing one or
more properties for various options. Feel free to
explore the options in the documentation.
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!
Automatic Index Loading
As you know from your previous experience, if the user makes a
request to a directory, the directory's index.html page will
load automatically (depending also on the server's configuration).
If your application has an index.html file in the /public static
files (or in any of the sub-directories), you will also want the
same functionality. This is enabled by default in the
express.static()
middleware function, but you have
to specify the name of the default index file to use. You can
configure this using the index
option:
app.use("/myapp", express.static(path.join(__dirname, "public"),
{index: "index.html"}));
To disable this functionality completely, set index
to
false:
app.use("/myapp", express.static(path.join(__dirname, "public"),
{index: false}));
There are other options you can configure, so make sure you
check out the express.static() documentation.