Click here to view as a presentation
- 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
- 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
-
Create a folder and cd into it:
$ mkdir first-express $ cd first-express
-
Then create a
package.json
using this command:$ npm init
Accept the defaults, except for the entry point - set this to be "server.js":
-
Open the project's folder in your code editor.
-
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 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
-
Let's use
npm
to install the Express module in this project:$ npm i express
Note that
i
is a shortcut forinstall
-
Create a
server.js
to put our web app's main code in:$ touch server.js
-
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:3000
will 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
send
method is a general purpose way to respond to the request, however, soon we'll be using more specific methods.
-
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 // Tell the app to listen on port 3000 app.listen(3000, function() { console.log('Listening on port 3000'); });
-
Let's define a route to replace the one we just wrote:
// 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
is a popular development tool used for automatically restarting our Express app when we save changes. -
You may have installed it during installfest, however, you can make sure you have the latest version by running:
$ npm i -g nodemon
Command line tools are installed using the
-g
(global) option -
Now, thanks to the
main
key inpackage.json
, we can start the server by simply typingnodemon
. -
Let's move on to routing...
-
Like most web frameworks, Express uses the
HTTP Method
and thePath
of the HTTP request to match a route defined in the application. -
In our first route, we defined a route using the
get
method on the Expressapp
object. -
The
get
method defines a route that listens for aGET
request. There are other methods such aspost
,put
anddelete
, 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
Router
object, but you need to be aware of defining routes this way as well.
-
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, and -
res
: Represents Express's response object -
Express provides those two objects as arguments when it invokes the callback.
-
The
request
object has properties and methods used to access information regarding the current HTTP request, including any data being sent from the browser. -
The
response
object contains properties and methods used to end the request/response cycle - like we've done so far using theres.send
method.
-
Define another route that matches a
get
request to a path of/home
that sends a text response of<h1>Home Page</h1>
. -
Test it by browsing to
localhost:3000/home
.
-
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!'); });
-
So far we have responded in our route handler (callback) code by using the
res.send
method. -
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 anotherGET
request.res.json()
- Send a JSON response (used when we communicate via AJAX).
-
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.render
to 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 sweet like bear meat!
-
Let's use EJS to render a
home
view for the existingGET /home
route. -
Express applications are usually architected using the MVC design pattern, so we will put all view templates inside of a
views
folder:$ mkdir views $ touch views/home.ejs
-
ejs
is the file extension for the EJS view engine.
-
Open
home.ejs
then 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
include
function 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 /home
route's callback to render our newhome.ejs
template:app.get('/home', function(req, res) { res.render('home'); });
-
Just the file name, not the
ejs
extension. -
Browse to
localhost:3000/home
and - it doesn'twork...
-
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');
-
The
app.set
method is used to configure an Express app's settings...
-
We also need to tell Express where all of our views can be found:
// Configure the app (app.set) app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, 'views'));
-
Don't be intimidated by this code:
path.join(__dirname, 'views')
...
-
path.join
is just a Node method that builds a properly formatted path from segment strings passed to it.__dirname
is always available and represents the path of the current folder where the currently running code lives; andviews
is the name of the folder we created to hold our views. -
path
is a core Node module, but it still must be required before we can use it...
-
Core Node modules don't have to be installed with
npm install
, but we do have torequire
them:// Require modules const express = require('express'); const path = require('path');
-
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
require
the view engine - Express knows how to find it. -
Refresh the page - success!
-
Again, view engines are used to dynamically generate HTML on the server before sending it to the client.
-
We just used the
render
method, 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
ejs
tags!
-
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:const todos = [ {todo: 'Feed Dogs', done: true}, {todo: 'Learn Express', done: false}, {todo: 'Buy Milk', done: false} ]; module.exports = { getAll: function() { return todos; } };
-
To access our To Do "database", we need to
require
it inside of server.js:const path = require('path'); // 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
todos
variable in thetodos/index
view...
-
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
index
as a name for views, etc. used for all of something - in this case, displaying all To Dos. -
Therefore, we need an
index.ejs
view inside of aviews/todos
folder:$ mkdir views/todos $ touch views/todos/index.ejs
-
Now let's code the
todos/index.ejs
view. Start by copying over the HTML fromhome.ejs
and fix it up to look like this:<body> <h1>Todos</h1> <ul> <% todos.forEach(function(t) { %> <li> <%= t.todo %> - <%= t.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!
-
One last trick for the day...
-
Currently, if we browse to the root route, we see
"Hello Express", however... -
We can use the
res.redirect
method to redirect toGET /home
so 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
GET
request to the providedpath
. -
Later, when we start creating, updating, or deleting data, we will always perform a
redirect
.
Review briefly, then on with the picker:
-
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?