Intro to Express
Learning Objectives
-
Students Will Be Able To:
- List the Fundamental Capabilities of Web Frameworks
- Create a Basic Express Web App
- Define Basic Routes
- Respond to HTTP Requests
- Render Dynamic Views Using EJS
Roadmap
- Setup
- The Three Fundamental Capabilities of Web Frameworks
- Intro to Express
- Express "Hello World"
- Basic Structure of an Express App
- Our First Route
- The Route's Callback Function
- Practice - Define Another Route
- Ways to Respond to a Request
- Rendering Views
- Dynamic Templating using EJS
- Redirecting
Setup
- Create a folder called
express-practiceand then change into it
$ mkdir express-practice
$ cd express-practice- Create a file inside this folder called
server.js
$ touch server.js- Then create a
package.jsonand accept the defaults using this command:
$ npm init -y- Open the project's folder in VS Code.
The Three Fundamental Capabilities of Web Application Frameworks
-
Web Application Frameworks have three capabilities fundamental to writing a back-end web application:
- The ability to define routes
- The ability to process HTTP requests using middleware
- The ability to use a view engine to render dynamic templates
- Over the next few lessons, you will learn about how the Express framework implements these three fundamental capabilities.
Express Framework - Intro
- Express is the most popular web framework for Node.js.
- It is minimalistic and lightweight, especially when compared to massive frameworks like Django and Rails.
- Express uses Node's built-in HTTP module to listen for, and respond to, HTTP requests - Express simply adds those three web application capabilities on top of Node
Install the Express Module
-
Let's use
npmto install the Express module in this project:$ npm i expressNote that
iis a shortcut forinstall
Express - Hello World!
-
Let's write the obligatory "Hello World!" application:
// Load express const express = require('express'); // Create our express app const app = express(); // Define a "root" route directly on app // Tomorrow, we'll use best practice routing app.get('/', function (req, res) { res.send('<h1>Hello World!</h1>'); }); // Tell the app to listen on port 3000 // for HTTP requests from clients app.listen(3000, function () { console.log('Listening on port 3000'); });
-
Run the app:
$ node server - Browsing to
localhost:3000will hit our app's root route that we defined and return "Hello World!". - Using DevTools, we will find that despite just sending back the text of
<h1>Hello World!</h1>, the browser "built" a minimal HTML document to display it in. - The
sendmethod is a general purpose way to respond to the request, however, soon we'll be using more specific methods.
Basic Structure of Express App
- Here is a helpful outline of what a typical Express app does - let's put this guide right in our
server.js:
// Require modules
const express = require('express');
// Create the Express app
const app = express();
// Configure the app (app.set)
// Mount middleware (app.use)
// Mount routes
app.get('/', function(req, res) {
res.send('<h1>Hello World!</h1>');
});
// Tell the app to listen on port 3000
app.listen(3000, function() {
console.log('Listening on port 3000');
});Our First Route
-
Let's replace the content we just sent from our route with something else.
// Mount routes app.get('/', function(req, res) { res.send('<h1>Hello Express</h1>'); }); - Refreshing the page will reveal that it didn't work! This is because we have to restart the server, or...
Nodemon
nodemonis a popular development tool used for automatically restarting our Express app when we save changes.-
If you are unsure if you've already installed it, you can make sure you have it or at least ensure you have the latest version by running:
$ npm i -g nodemonCommand line tools are installed using the
-g(global) option - Now, thanks to the
mainkey inpackage.json, we can start the server by simply typingnodemon. - Let's move on to routing...
Our First Route (Cont)
- Like most web frameworks, Express uses the
HTTP Methodand thePathof the HTTP request to match a route defined in the application. - In our first route, we defined a route using the
getmethod on the Expressappobject. - The
getmethod defines a route that listens for aGETrequest. There are other methods such aspost,putanddelete, that map to the other HTTP verbs. - The first argument provided to
app.get,/, defines the path for the route. In this case the root of the application, i.e., just the host name likelocalhost:3000. - In Express, all strings used to define a path should start with a forward-slash character (
/). - In tomorrow's Express lesson, we'll learn a preferred way of defining routes using the Express
Routerobject, but you need to be aware of defining routes this way as well.
The Route's Callback
-
The second argument provided to
app.get()is a callback function:app.get('/', function(req, res) { res.send('<h1>Hello Express</h1>'); }); - Express will execute route's callback function only when a matching HTTP request (HTTP Method + Path) is received.
-
The route's callback function:
app.get('/', function(req, res) { res.send('<h1>Hello Express</h1>'); });defines two parameters:
req&res req: Represents Express's request object, andres: Represents Express's response object- Express provides those two objects as arguments when it invokes the callback.
- The
requestobject has properties and methods used to access information regarding the current HTTP request, including any data being sent from the browser. - The
responseobject contains properties and methods used to end the request/response cycle - like we've done so far using theres.sendmethod.
Practice - Define Another Route (3 mins)
- Define another route that matches a
getrequest to a path of/homethat sends a text response of<h1>Home Page</h1>. - Test it by browsing to
localhost:3000/home.
Review Question - Routing
- Is it okay to define more than one route on the same path?
For example:
app.get('/cars', function(req, res) {
res.send("Here's a list of my cars...");
});
app.post('/cars', function(req, res) {
res.send('Thanks for the new car!');
});Ways to Respond to a Request
- So far we have responded in our route handler (callback) code by using the
res.sendmethod. - The Express docs for the Response object explains the other ways to respond to the HTTP request.
-
Here are the methods we'll use the most:
res.render()- Render a view template and send the resulting HTML to the browser.res.redirect()- Tell the browser to issue anotherGETrequest.res.json()- Send a JSON response (used when we communicate via AJAX).
Rendering Views
- Another of the three fundamental capabilities of a web framework is to be able to use a view engine to render templates.
- A template can include a mixture of static HTML and "code" that generates HTML dynamically.
- For example, code in a template could generate a series of
<li>elements for data provided to it in an array. - In Express, we use
res.renderto process a template using a view engine and return the resulting HTML to the browser. - Express can work with a multitude of view engines.
Pug(formerlyJade) is a template language that leverages indentation to create HTML with a "shorthand" syntax.- However,
EJS(Embedded JavaScript) templates are one of the most popular! - Let's use EJS to render a
homeview for the existingGET /homeroute. -
Express applications are usually architected using the MVC design pattern, so we will put all view templates inside of a
viewsfolder:$ mkdir views $ touch views/home.ejs ejsis the file extension for the EJS view engine.-
Open
home.ejsthen type!and press tab to generate the HTML boilerplate:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>First Express</title> </head> <body> </body> </html> - For now, we will need to include the HTML boilerplate inside of every view.
- EJS includes the ability to make our views more DRY by using partial views.
- We will cover partial views later, however, if you want to check them out before then, check out the
includefunction here. - Let's add an
<h1>inside the<body>so that we see something :)
<body>
<h1>Home Page</h1>
</body>- Okay, now let's refactor the
GET /homeroute's callback to render our newhome.ejstemplate:
app.get('/home', function(req, res) {
res.render('home');
});- Just the file name, not the
ejsextension. - Browse to
localhost:3000/homeand - it doesn't work... - We're going to get a little practice reading Express errors in this lesson.
- The Express error Error: No default engine was specified..., makes it clear that we need to specify a view engine.
- This is our first opportunity to configure our app:
// Configure the app (app.set)
app.set('view engine', 'ejs');Keep in mind, when we configure our server like this, our
ejsview engine assumes all of our views ... i.e. (files with the.ejsextension, in this case), will be placed in a directory namedviewsat the root of our directory.
- The
app.setmethod is used to configure an Express app's settings... - Refresh and let's see what the next error is...
- Error: Cannot find module 'ejs' - this error is telling us that we need to install the EJS view engine package:
$ npm i ejs- We don't need to
requirethe view engine - Express knows how to find it. - Refresh the page - success!
Dynamic Templating Using EJS
- Again, view engines are used to dynamically generate HTML on the server before sending it to the client.
- We just used the
rendermethod, passing in the view name as an argument. - We can also pass in a JavaScript object as a second argument, and all of its properties will be accessible in the view within
ejstags! - Let's say we want to render a list of To Dos.
- Normally, the To Dos would be coming from a database, however, we'll "fake" a DB by putting the To Dos in a module and export a method to return them.
- Do this to set up the module:
$ mkdir data
$ touch data/todo-db.js- Next up, put code in the module...
- In the spirit of saving time, copy/paste the following inside of
todo-db.js, then we'll review the code:
module.exports = {
getAll
};
const todos = [
{text: 'Feed Dogs', done: true},
{text: 'Learn Express', done: false},
{text: 'Buy Milk', done: false}
];
function getAll() {
return todos;
}- To access our To Do "database", we need to
requireit inside of server.js:
// require the todo "database"
const todoDb = require('./data/todo-db');- Now let's add another route responsible for displaying the list of To Do's...
- Add this new route:
app.get('/todos', function(req, res) {
res.render('todos/index', {
todos: todoDb.getAll()
});
});- Again, to pass data to a view, we pass an object as a second argument to
render. - We should now be able to access a
todosvariable in thetodos/indexview... - It's a best practice to group views related to a data resource (in this case To Dos) in their own folder.
- We also commonly use
indexas a name for views, etc. used for all of something - in this case, displaying all To Dos. - Therefore, we need an
index.ejsview inside of aviews/todosfolder:
$ mkdir views/todos
$ touch views/todos/index.ejs-
Now let's code the
todos/index.ejsview. Start by copying over the HTML fromhome.ejsand fix it up to look like this:<body> <h1>Todos</h1> <ul> <% todos.forEach(function(todo) { %> <li> <%= todo.text %> - <%= todo.done ? 'done' : 'not done' %> </li> <% }); %> </ul> </body> - That my friends is embedded JavaScript between those
<% %>and<%= %>tags and I believe you are going to love their simplicity! - The
<% %>EJS tags are for executing JavaScript such as control flow. - The
<%= %>EJS tags are for writing JS expressions into the HTML page. - Refresh and browse to
localhost:3000/todos- yeah!
Redirecting
- One last trick for the day...
- Currently, if we browse to the root route, we see"Hello Express", however...
- We can use the
res.redirectmethod to redirect toGET /homeso that we will see the Home page upon browsing to the app... - Refactor the root route as follows:
app.get('/', function(req, res) {
res.redirect('/home');
});- Redirects tell the browser to make a new
GETrequest to the providedpath. - Later, when we start creating, updating, or deleting data, we will always perform a
redirect.
Essential Questions
❓ When we define routes in a web app, we are mapping HTTP requests to ____.
❓ What method do we call to render a view and on what object does that method exist?