The Problem
Node is an incredibly robust tool all by itself. But actually building out a server environment without any libraries (while certainly possible) gets to be a bit verbose.
The Solution
Express is self defined as a fast, unopinionated, minimalist web framework for Node.js. It provides us with a toolbox that sits on top of the Node framework and allows us to quickly and easily build and maintain web applications.
Getting Started
Let's build an example Express application to see just how it works. To get started, let's make a new Node project. Inside of ~/wcci/code
let's make a new project directory called hello-express
.
Inside of Git Bash, execute the following:
mkdir hello-express
cd hello-express
npm init -y
npm i express
code .
This will give us a new application that has express installed as a dependency.
Start Coding!
at the root of our project, let's make an entry point to start writing some code. In your project root, create a file called app.js
. This will be the main entry point of our application and what will serve all of our content. Add the following code to this file:
const express = require("express");
const app = express();
const port = 3000;
app.get("/", (req, res) => res.send("Hello World!"));
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
Run it
In Git Bash, run the following command: node app.js
Now, in your browser, navigate to localhost:3000
. You should see the words "Hello World!"
Congrats! You just made your first Express application!!
But What is Going On???
Ok, so we have our Hello World example done, but just what is this code doing. Let's crawl through it. Let's start with the first 3 lines.
const express = require("express");
const app = express();
const port = 3000;
- The first line is using Node's built in module system to give us access to the Express framework that we installed.
- The next line is creating a variable called
app
and assigning it an instance of the Express framework. Now that we have that, we can access all of the magic that Express provides. - The final line is creating a variable called
port
that we use to tell our Node server exactly what port we want to run our application on. (This number is somewhat arbitrary.)
Actually Doing Something
Now let's look at the next line
app.get("/", (req, res) => res.send("Hello World!"));
This one is doing a lot more heavy lifting. First, in app.get(...)
, .get()
is a method we have access to from Express. This allows us to tell Express (and in turn, Node) that we are mapping a GET
HTTP request. This method accepts two parameters. The first is the path that we want to map behavior to. In this case we are selecting the root of the domain. The second parameter is a callback function that we use to execute some behavior.
This callback takes two parameters as well req
and res
which stand for request and response respectively. These parameters handle requests from the browser to our application and responses from our application to the browser. In our example, we are sending (via res.send
) the plain text "Hello World!". This is how we get that to render in the browser.
Listening
Finally, the last line:
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
This line is what actually let's us see that text in the browser. The .listen()
method is an Express method just like .get()
. This one, however, is the one that actually starts our server. The first parameter this method accepts is a number that represents the port we want our application to run on, 3000 in our example. The second parameter is optional but is often used to send a confirmation message that our server is indeed running. This parameter is just a callback function where we can do pretty much anything we want.
Express Generator
Now that we understand the basics of Express it's time to explore a resource that will quickly bootstrap an Express application for us so we can focus on coding, the Express Generator. This helpful tool gets you started with an Express project that is ready to go out of the box. Let's install it:
npm i -g express-generator
And now let's create a project. From ~/wcci/code
, run the following
express hello-express-generator --view=hbs
The --view
flag at the end is telling the Express Generator that we want to use Handlebars for our view engine. (More on that later)
cd hello-express-generator && code .
This opens our new application in VSCode.
What Do We Have Here?
We have a pretty organized (albeit confusing at first) directory structure. What are these directories for?
bin
- Where we store Binary Executable files. For us, this is where our initialization script is housed.node_nodules
- Per the usual, our project dependencies are housed here.public
- This is where we store any static resources such as CSS or client-side JS.routes
- This is where we organize our routes or endpoints for our application. This helps us keep the size of our app file down by abstracting that code away.views
- This is where we keep our template files that we will ultimately render.app.js
- Our applications entry point
Digging In
Let's open up app.js
and take a look. Let's focus in particular on lines 13-23:
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "hbs");
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));
app.use("/", indexRouter);
app.use("/users", usersRouter);
View Rendering
Let's examine these in sections:
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "hbs");
These lines are telling our Express application that we will be using HBS (or Handlebars) as our template engine. This allows Express to read those files now that we told it we'll be using them. It also gives Express a path to where those templates will be housed.
Middleware
The next set of lines are where Express REALLY excels:
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));
This is what is known as Middleware. Named such because it happens in the middle of the application being initialized and the application being delivered (via the browser) to the user. Express allows you to add as much Middleware as you'd like and passes all data (requests and responses) through these "middle" applications so that we can perform certain tasks on it.
Routing
The final lines of code manage routing:
app.use("/", indexRouter);
app.use("/users", usersRouter);
This handles our routing. Essentially it tells Express that when the user hits the domain root or /users
go to the provided file and execute whatever that file says to do based on the request type.
Conclusion
That does it for our introduction to Express. Express is an incredibly powerful tool and is absolutely invaluable to the JS developer. Be sure to check out the documentation and familiarize yourself more fully.