id | title |
---|---|
10-nodejs |
Node.js |
- Node.js allows us to run JavaScript in different environments and run different tasks
- Using Node we can:
- Run a simple script (like our exercises)
- Bundle our front-end code and assets using different cool modules
- Run a web server
- Create back-end apps using different databases
- Over this course section we'll focus on using Node.js to create a web server and create a back-end app
- We'll create dynamic documents
- When we installed Node.js we also installed other program called NPM
-
NPM used to stand for Node Package Manager but nowadays it's just known as "NPM"
-
Thanks to NPM we can install many different Node.js modules and import them into our projects
-
This way we reuse code and we can contribute to other modules too
-
NPM is one of the largest modules repository
-
Visit NPM site to learn more about it
-
Also learn more reading NPM blog
-
Also, they have the best mascot ever.. NPM wombat!!
-
To start a new project first we need to create a new folder and change directory to go inside of it
-
You can do this in unix like systems
mkdir learning-node cd learning-node
-
Once we're in the right folder we run the NPM command
init
-
This command asks a lot of questions to configure our project
-
We can ignore them and just press enter
-
npm init
-
Or we can generate it without having to answer the questions
npm init -y
-
This script creates a new package.json file that allows us to configure our project
-
Just looking at the file extension we can figure out that we'll use JSON for our npm configuration file
{ "name": "learning-node", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" }
-
NPM init uses our answers to create the configuration file based on our answers
-
Once we have the file we can update any value on it
-
In the package.json file we can find a scripts key
-
By default we have a test script
-
We can create our own scripts
-
Add a start script
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node index.js" },
-
Now we can run this command from our command line
npm start
-
At this point we get an error
Error: Cannot find module 'index.js'
-
We get this error as we try to execute index.js script and node can't find it
-
As we are calling
node index.js
we need to create this new file at the same package.json or root level -
Update the start script definition if you need to move index.js to a different project/folder structure
-
Create index.js and add the following code
index.js
console.log("Welcome to JavaScript and Node.js server side");
-
Run the npm start command again
npm start Welcome to JavaScript and Node.js server side
-
Now we know how to create, configure and run scripts using NPM and Node
-
Using NPM we can install modules and use them on our projects
-
Run npm install and the module name to install the given module
-
You can check and search for modules on the NPM site
-
In this case we're going to install Express which is a popular module to create a web server
-
A web server is the magic that gives you a web page when you ask the internet for one :)
- If you are interested in knowing more, here is a good place to start
-
Check out express npm page
-
Also, take a look at express site and doc
npm install express
package.json
"dependencies": { "express": "^4.16.3" }
-
Running NPM install creates a node_modules folder
-
Inside node_modules we find all the installed modules for a given project
-
This folder can grow in size as we can have many installed modules or dependencies
-
To avoid having issues we can .gitignore this folder
-
So if we're not sharing the node_modules folder with other team members... do we need to remember the list of installed modules?
-
By default now NPM installs the modules and add them to our package.json dependencies
-
Delete the node_modules folder
-
Run the npm install command but without the module name
npm install
-
NPM reads our list of dependencies, downloads and install all the modules for us
-
In this case we have only Express as dependency
-
In many cases we need dependencies that are only for development purposes like for example a library to run our tests, build our bundle or deploy our project
-
NPM supports devDependencies that are modules that we don't deploy on our server
-
To install a devDependencies we run npm install and add --save-dev as command parameter
-
By default npm install now runs
npm install --save
to save the module as dependency -
Lets install a dev dependency called UglifyJS
npm install uglify-js --save-dev
-
Open package.json to see the installed dependecy
"devDependencies": { "uglify-js": "^3.3.25" }
-
Now we have express installed as dependency and uglify.js as dev dependency
-
Delete the node_modules and run npm install again to make sure that npm install both dependencies
-
Lets configure uglify.js to run as script
-
Add the following script to your package.json file
"uglify": "uglifyjs index.js --output script.js"
-
Open index.js and replace the console.log() for the following code
var firstname = "Ciro"; var lastname = "Rodriguez"; var age = 20; console.log(firstname); console.log(lastname); console.log(age);
-
Now run the following npm command
npm run uglify
-
When running this command you'll see the following output:
> uglifyjs index.js --output script.js
-
NPM has configured some scripts names as shortcuts like start and test
-
When we need to run our own scripts we need to use
npm run scriptname
-
NPM can execute the code configured on this script as uglify.js is installed in the node_modules folder
-
NPM can access installed node_modules modules
-
Open the generated script.js file
script.js
var firstname = "Ciro"; var lastname = "Rodriguez"; var age = 20; constole.log(firstname); constole.log(lastname); constole.log(age);
-
Uglify.js does a lot of things to bundle our code
-
Uglify removes all the spaces as we need to create a smaller file so it takes less time to download
-
This file is prepared to be deployed and not to read
-
The index.js file still has our code and that it's more readable
-
We don't need uglify.js as dependency as we prepare our files before we deploy them
-
This is why we can install it as devDependency
-
NPM can also uninstall packages running the uninstall command
-
First we'll install a new module so we can then test this new command
-
Install Morgan.js
npm install morgan
-
Check the package.json file
"dependencies": { "express": "^4.16.3", "morgan": "^1.9.0" },
-
Now run npm uninstall with the module name that you want to remove
npm uninstall morgan
-
After running npm uninstall your package.json should only have express as dependency
"dependencies": { "express": "^4.16.3" },
-
All this modules has been installed as local dependencies
-
NPM can also install modules as global so we can run them as programs from anywhere
-
To install a module as global we also run npm install but we add the -g parameter
-
For example when we installed http-server we installed it as global so we can use it from any folder
npm install http-server -g
-
As we're going to create a web server and we already have express installed we could start coding it
-
Express is a really flexible module but we want to add some structure to our project
-
Install express-generator as a global module
npm install express-generator -g
-
To make sure that the module has been installed run the following command:
express --help
-
If you get an error running the previous command try closing and opening your terminal again
-
Sometimes you need to restart your terminal so the terminal notice that there's other modules installed (not so common but it might happen)
-
Now you should get the following output
Usage: express [options] [dir] Options: -h, --help output usage information --version output the version number -e, --ejs add ejs engine support --pug add pug engine support --hbs add handlebars engine support -H, --hogan add hogan.js engine support -v, --view <engine> add view <engine> support (dust|ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade) -c, --css <engine> add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css) --git add .gitignore -f, --force force on non-empty directory
-
As we're learning express lets add our own structure and uninstall this module
-
We can add it later if we really need it
npm uninstall express-generator -g
-
As you can see NPM is a great package module and will help us install and uninstall regular or dev dependencies
-
A module is just a way to encapsulate our code in a single code unit
-
This means that we'll organize our code by functionality and create our own modules too
-
Node.js by default has a module loader that we can use
-
We can create a single file and use it as module in our project
-
The require function accepts a string as parameter with the name of the module that we want to get
-
Require will look into the node_modules folder to see if we have that module installed
const module = require("modulename");
-
We can also use relative paths to load our modules
/ |- index.js |- my-module.js
-
For example if we have the index.js file and my-module.js we can load the my-module from the index one in the following way
const module = require("./my-module");
-
As both files are siblings we can use
./
to let node node know that the my-module module is at the same level -
Also, you can see that we're not using
.js
as my module extension -
By default require will look for a JavaScript file
-
As we have express installed as module we can require it from our project
-
Delete all the
index.js
content and add the following oneconst express = require("express"); console.log(express);
-
Now run npm start
npm start
{ [Function: createApplication] application: { init: [Function: init], defaultConfiguration: [Function: defaultConfiguration], lazyrouter: [Function: lazyrouter], handle: [Function: handle], use: [Function: use], route: [Function: route], engine: [Function: engine], param: [Function: param], set: [Function: set], path: [Function: path], enabled: [Function: enabled], disabled: [Function: disabled], enable: [Function: enable], disable: [Function: disable], acl: [Function], bind: [Function], checkout: [Function], connect: [Function], copy: [Function], delete: [Function], get: [Function], head: [Function], link: [Function], lock: [Function], 'm-search': [Function], merge: [Function], mkactivity: [Function], mkcalendar: [Function], mkcol: [Function], move: [Function], notify: [Function], options: [Function], patch: [Function], post: [Function], propfind: [Function], proppatch: [Function], purge: [Function], put: [Function], rebind: [Function], report: [Function], search: [Function], subscribe: [Function], trace: [Function], unbind: [Function], unlink: [Function], unlock: [Function], unsubscribe: [Function], all: [Function: all], del: [Function], render: [Function: render], listen: [Function: listen] }, request: IncomingMessage { header: [Function: header], get: [Function: header], accepts: [Function], acceptsEncodings: [Function], acceptsEncoding: [Function], acceptsCharsets: [Function], acceptsCharset: [Function], acceptsLanguages: [Function], acceptsLanguage: [Function], range: [Function: range], param: [Function: param], is: [Function: is], protocol: [Getter], secure: [Getter], ip: [Getter], ips: [Getter], subdomains: [Getter], path: [Getter], hostname: [Getter], host: [Getter], fresh: [Getter], stale: [Getter], xhr: [Getter] }, response: ServerResponse { status: [Function: status], links: [Function], send: [Function: send], json: [Function: json], jsonp: [Function: jsonp], sendStatus: [Function: sendStatus], sendFile: [Function: sendFile], sendfile: [Function], download: [Function: download], type: [Function: contentType], contentType: [Function: contentType], format: [Function], attachment: [Function: attachment], append: [Function: append], header: [Function: header], set: [Function: header], get: [Function], clearCookie: [Function: clearCookie], cookie: [Function], location: [Function: location], redirect: [Function: redirect], vary: [Function], render: [Function: render] }, Route: [Function: Route], Router: { [Function] param: [Function: param], handle: [Function: handle], process_params: [Function: process_params], use: [Function: use], route: [Function: route], acl: [Function], bind: [Function], checkout: [Function], connect: [Function], copy: [Function], delete: [Function], get: [Function], head: [Function], link: [Function], lock: [Function], 'm-search': [Function], merge: [Function], mkactivity: [Function], mkcalendar: [Function], mkcol: [Function], move: [Function], notify: [Function], options: [Function], patch: [Function], post: [Function], propfind: [Function], proppatch: [Function], purge: [Function], put: [Function], rebind: [Function], report: [Function], search: [Function], subscribe: [Function], trace: [Function], unbind: [Function], unlink: [Function], unlock: [Function], unsubscribe: [Function], all: [Function] }, json: [Function: json], query: [Function: query], static: { [Function: serveStatic] mime: Mime { types: [Object], extensions: [Object], default_type: 'application/octet-stream', Mime: [Function: Mime], charsets: [Object] } }, urlencoded: [Function: urlencoded] }
-
We get a big object definition as output
-
By using
require('express')
we tell node to get express code -
In this case we can assign the value that we get back from the require call
const express = require('express');
-
Node.js also has it own modules
-
Take a look at Node.js module doc
-
We can learn more about modules by creating our own one
-
Create a
math.js
file and add the following codemath.js
const revenew = 50; const priceCalculation = function(price) { return price + revenew; }; module.exports = priceCalculation;
-
Comment the express require call
-
Require math module and use it from index.js
index.js
// const express = require('express') // console.log(express) const getPrice = require("./math"); const price = getPrice(200); console.log(price);
-
Run the project
npm start 250
-
In this example we created a module called
math
-
The math module exports a
priceCalculation
function that we can use -
This function accepts a number parameter and will return a value with the final price
-
Node.js uses
module.exports
that represents an object that we can export -
Now that we have our
math
module we canrequire
it fromindex.js
-
As the math module it's not installed in the node_module we use the relative path to import it
require('./math')
-
We defined a variable getPrice and assign what math module is exporting
-
In this case it's just a function but it could be an object or any other value
-
Then we can use the getPrice function with any numeric value to get the final price
-
By using this math module we calculate the final price without knowing the logic behind it
-
If we need to change the way that we calculate a price we just update the math module an it will work from anywhere it's being called
-
In case we need or want to share this module between project we can just publish it on the NPM repository
-
NPM has public and private repositories
-
Also we can link NPM modules while we develop our projects if we have them as external dependencies
-
Node.js uses a module pattern to export code
-
Once we export our code Node.js will enclose it inside a function so the module scope is private
-
Module variables and rest of the code will be private
-
We only can access and use what it has been exported using
module.exports
-
Also, we can export more code using just
exports
exports = getPrice;
-
Read and learn more about Node.js modules
-
Node.js uses arrow functions for its callbacks and you can see many examples in their documentation
-
Arrow functions are just JavaScript functions that we write in a different way
-
We won't use the function reserved word
-
Arrow function definition
// Regular function function() {} // Arrow function () => {}
-
We can assign them to variables in the same way that we used to
const myFunction = function() { console.log("my function call"); }; const myES6Function = () => { console.log("my function"); };
-
Then we can call them in the same way we used to:
const myFunction = function() { console.log("my function call"); }; const myES6Function = () => { console.log("my function"); }; myFunction(); myES6Function();
-
In this example we see how similar functions and arrow functions are
-
Arrow functions are part of the ES6 definition and they are simple to write
-
We can almost use them on any Browser too
const myFunction = function(parameter) { console.log("parameter: ", parameter); }; const myES6Function = parameter => { console.log("parameter: ", parameter); };
-
Passing parameters works the same way too
-
The only difference is that if we only pass one parameter we can remove the parentheses
const myFunction = function(parameter) { console.log("parameter: ", parameter); }; const myES6Function = parameter => { console.log("parameter: ", parameter); };
-
They are really usefull when we use them as callbacks:
const students = ["tom", "peter", "maria", "claire"]; students.forEach(function(student) { console.log(student); }); students.forEach(student => { console.log(student); });
-
In both cases the functions are working as callbacks but arrow functions looks much simpler
-
Arrow functions change the way that we can return values from it
-
Using arrow functions will return the value after the => symbol
function getMessage() { return "hi, this is a message"; } const getMessageArrowFunction = () => "hi, this is a message"; const message = getMessageArrowFunction();
-
As
{ }
in arrow functions are used to limit the function body we need to find a different way in case we need them to return an object -
We can use parentheses and curly brackets
function getUser() { const user = { name: "Maria", age: 26 }; return user; } const getUserArrowFunction = () => ({ name: "Maria", age: 26 }); const user = getUserArrowFunction(); console.log(user.name);
-
Arrow functions do not have their own this value
-
This means that the
this
inside the function it's going to work in a different way -
Lexical Scoping just means that it uses this from the code that contains the Arrow Function
-
You can see great examples
-
Read the MDN Arrow functions doc to learn more about Arrow Functions
-
Node.js has a build in HTTP module
-
Using the HTTP module we can create a Node.js Web Server
-
This web server will listen for
HTTP requests
and sendHTTP responses
-
Require the http module to start coding our server
-
We don't have to run npm install as we're using a build in Node.js module
-
Create a new server.js file and add the following code
server.js
const http = require("http"); const port = 3000;
-
We defined an http variable and assigned what http node module exported
-
Also we declared a port variable as we're going to need it to set up our server
-
This means that our server will be listening at port 3000
-
So now we need to create a server and we can do that using the
createServer
method -
This method accepts a callback function as parameter
-
The callback function gets
2 parameters
that representsrequest & response
-
This means that the callback function it's going to be executed on each request and we can handle the response
const requestHandler = (request, response) => { response.end("Hello Node.js Server!"); }; const server = http.createServer(requestHandler);
-
We'll use the requestHandler function as createServer callback
-
This means that requestHandler recibes a request and response object that represents the http request & response
-
requestHandler will be executed on each HTTP request
-
When we get a request we need to send a response back
-
In this example we're sending just a text with
'Hello Node.js Server!'
content -
So far we haven't told the server on which port it should listen to
-
We can configure it using
server.listen
-
This method accepts the port number as first parameter
-
And a callback function as second parameter
-
This function will get executed when the server start listening
-
The callback function gets an error parameter that we can use to check if there's an error
server.listen(port, err => { if (err) { return console.log("something bad happened", err); } console.log(`server is listening on ${port}`); });
-
Now we have all the code together
const http = require("http"); const port = 3000; const requestHandler = (request, response) => { console.log(request.url); response.end("Hello Node.js Server!"); }; const server = http.createServer(requestHandler); server.listen(port, err => { if (err) { return console.log("something bad happened", err); } console.log(`server is listening on ${port}`); });
-
Copy and paste this code into the server.js file
-
Run the following command
node server.js
-
If everything goes well you will see the following message as output
server is listening on 3000
-
Also, you can configure
npm start
"start": "node server.js"
-
Now we have a Node.js web server running
-
Open the browser and input localhost:3000 as url
-
We get a
Hello Node.js Server!
back -
As we can see we're using the configured 3000 port
-
The server will stop if we cut the Node.js script executing
-
After doing it we won't get a response back from the server
-
The HTTP module provides the basic features to have an HTTP server
-
To create a real application we need more abstraction so we can handle more complexity
-
This is the reason why we have many other modules that uses the basic HTTP adding more features and abstraction
-
Some the of the most common web server modules are:
-
In this course we'll use
Express.js
- Using express we'll be able to execute the following tasks:
- Handle any HTTP request (GET, POST, etc)
- Create and handle specific routes
- Configure templating engines to create dynamic HTML
- Configure and use express
middlewares
to add more features - Configure our server the way we want
- And much, much more!
- Express is a minimal and flexible Node.js web application framework
- This is why we'll add many modules to configure our server like sessions, coockies and more
- Some known modules are maintained by the express core team
- There's no silver bullets on how to create/configure an Express server
-
We already saw how we can create and start a Node web server using the HTTP module
-
Now we need to learn how to do it using express
-
The basic features are going to be the same:
- Request the module
- Configure the server
- Handle request
- Send response
-
The request object allow us to get a lot of data from the user like username & password, product id or search query
-
Using these values we can define what we should do, be it: interact with a database, create a session or just send some asset
-
We can configure express to respond with static content like we have been doing using HTTP-Server module
-
Also we can create dynamic content
-
We might do it using a database or not
-
To create dynamic content we'll use JavaScript and a template engine
-
So, Express allows us to handle http routes for each request and configure the corresponding response
-
Configure static assets
-
Configure template engine
-
And for any other task we can add many different modules that will help us work better
-
To have express working we need to first install it
npm i express
-
Now lets create a basic server by doing a Hello Node.js World example
Example:
// require the express module const express = require("express"); // then create a express server const app = express(); // configure the default route and send a text as response app.get("/", function(request, response) { response.send("Hello Node.js World!"); }); // configure the port that express is going to listen to app.listen(3000, function() { console.log("Example app listening on port 3000!"); });
-
In this example we see how to create a simple Express server
-
We need to require the express module and create a express app/server
-
Then we configure a GET default route
/
-
Using the listen method we tell express on which port it will listen to HTTP requests and show a message when the server starts
-
Now that we have our server running we can visit
localhost:3000
using a browser to get the response back from the server
-
As you can see creating routes with express is really easy
-
We use the
get
method to obtainGET
requests -
This method accepts a string as first parameter that represents the route that we want to handle
-
For example we used
/
to handle our root route -
The second parameter is a callback function that accepts 2 objects (
request & response
) as parameters -
The response object has a
send
method that we can use to send some data to the client as response -
Thats why we can see the message back when we send a request to our server
Example:
app.get("/", function(request, response) { response.send("Hello World!"); });
-
The express object has many methods to handle HTTP requests using different HTTP methods
Example:
app.post("/", function(req, res) { res.send("Request using POST"); }); app.put("/", function(req, res) { res.send("Request using PUT"); }); app.delete("/", function(req, res) { res.send("Request using DELETE"); });
-
Also we can use an
all
method to handle any type of HTTP method -
We can use this
all
method to configuremiddleware
features for all our callsExample:
app.all("/", function(req, res) { res.send("Request using GET, POST, PUT or DELETE"); });
-
Now that we know how express routes work we can create some to handle a products routes
Example:
app.get("/products", function(req, res) { res.send("On this call we show a list of products"); }); app.post("/product/:id", function(req, res) { res.send("On this call we create a product"); }); app.put("/product/:id", function(req, res) { res.send("On this call we update a product"); }); app.delete("/product/:id", function(req, res) { res.send("On this call we delete a product"); });
-
Using the browser we can only use GET requests
-
To use POST we need to create a form and set the method to POST
-
This is why we have a tool called Postman (you can find more like this one) that will help us request using different HTTP methods
-
Install and open Postman
-
Configure the URL that we want to use (request)
-
We use the send button to create a request to the given URL using the selected HTTP method
-
Then we can see the server response on the Postman lowers section of the screen
-
Using Postman we can know the response status (in this example it's 200)
-
We can also change the request and response HTTP headers
-
And configure different HTTP methods (POST, PUT, DELETE, etc)s
- Using the
response
object we can send the response to the client and choose different formats - We can send JSON format if we're creating an API with the corresponding JSON headers
- Also, we can send an HTML (static or dynamic) content as string too
- You can read about all the response types that Express can handle on express doc
JSON
-
The response
json
method allows us to send the user some data in JSON format -
We can pass a JavaScript object as parameter and the json method will transform it to JSON for us
-
It will also add the
Content-Type application/json
headerExample:
app.get("/", function(request, response) { response.json({ name: "Marta", age: "40" }); });
-
Then when we call this route we get: {"name":"Marta","age":"40"} back
-
Also, we get the right headers too
sendFile
-
Using the response
sendFile
method we can send a a file back -
We can send any type of file and it will depend on the client if it knows how to read or interpretate this response
-
If we send html, images, audio, video, js or css and the client is a browser it will be able to handle this response
Example:
const express = require("express"); const app = express(); const path = require("path"); app.get("/", function(request, response) { response.sendFile(path.join(__dirname, "index.html")); }); app.listen(3000, function() { console.log("Example app listening on port 3000!"); });
-
In this example we're using the
path module
to join Node.js special__dirname
variable and the file name (index.html) that we want to send -
Path module has a join method that allows us to join this two values
-
Node.js __dirname will return the current directory
-
By joining the current direname and the file name we're creating an absolute path so sendFile knows exactly where it needs to get the file from
- As we saw on the products route example we can have many routes for one resource and our server file can grow
- To handle this routing config better we can use
express router
- We'll be grouping our routes by action or resource type
- It's easier to read and maitain our server routes this way
Example:
-
home.js
// Create the express router to handle our home requests var express = require("express"); var router = express.Router(); router.get("/", function(req, res) { res.send("Welcome to our Site!"); }); module.exports = router;
-
products.js
// Create the express router to handle our products requests var express = require("express"); var router = express.Router(); router.get("/", function(req, res) { res.send("On this call we show a list of products"); }); router.post("/:id", function(req, res) { res.send("On this call we create a product"); }); router.put("/:id", function(req, res) { res.send("On this call we update a product"); }); router.delete("/:id", function(req, res) { res.send("On this call we delete a product"); }); module.exports = router;
-
index.js
Mount the routes
const express = require("express"); const app = express(); // Routers const home = require("./home.js"); const products = require("./products.js"); app.use("/", home); app.use("/products", products);
-
In this example we see how we can have multiple routers using the express
Router
module -
Each router that we create can be mounted on the express app and configure the url that it need to handle
-
Use express.Router() to create a new router
-
router.get / router.post
will handle the get and post HTTP methods -
Then using
app.use
we tell express to use this routes handlers -
We call this process
router mounting
-
The response object has a
status
method that allows us to set the HTTP status response codes -
By default is 200
-
It accepts a number as parameter
-
We can also chain this method with others like send
Example:
app.get("/", function(request, response) { res.status(200).json({ firsname: "Pepe", lastname: "Martin" }); }); app.get("/error", function(request, response) { res.status(500).send("Server error"); });
-
In the first example we set 200 as status code and send a JavaScript object as JSON as response
-
On the error call we want to send an error status back so we set 500 to let the user know that there's a server error
-
Also, we can handle 404 routes the following way:
Example:
app.use(function(req, res, next) { res.status(404).send("Sorry can't find that!"); });
-
In this example we don't specify the route that we want to handle
-
If the request doesn't match any of the other routes it will execute this one at last
-
As it didn't found the route we can send a 404 as there's no document to send
-
We could send a file back too with a cool 404 message
-
Express also allows us to handle errors
-
It's going to be similar to 404
-
We need to add one more parameter that's the error one
-
The callback function now will have the following parameter:
error, request, response and next
-
If we get an error message we can send it to as response
-
This functions that we pass as parameters are called middleware
-
The next parameter allows us to call the next express middleware (we'll talk more about it on the next section)
Example:
app.use(function(err, req, res, next) { console.error(err.stack); res.status(500).send("Something broke!"); });
-
A Middleware is a function that has access to the request & response objects
-
We can chain middleware calls using the
next
function in the application’s request-response cycle -
The
next
function is a function in the Express router which, when invoked, executes the middleware succeeding the current middleware -
So this means that the middleware gets three parameters:
request, response and a function callback called next
-
Express has many middlewares that we can install, configure and use
Example:
const express = require("express"); const app = express(); const myMiddleware = function(req, res, next) { // We set a new property in the request object req.someValue = "This is a value to test the Middleware"; // We call the next middleware next(); }; app.listen(3000);
-
To use a middleware we use the express
use
method -
This method accepts the function callback as parameter that will get executed on each request
Example:
const express = require("express"); const app = express(); const myMiddleware = function(req, res, next) { req.someValue = "This is a value to test the Middleware"; next(); }; app.use(myMiddleware); // Add a middleware app.listen(3000);
-
Now we can define a new route
Example:
const express = require("express"); const app = express(); const myMiddleware = function(req, res, next) { req.someValue = "This is a value to test the Middleware"; next(); }; app.use(myMiddleware); app.get("/", (req, res) => { const responseMessage = req.someValue; // We get the request value that we setted from the middleware res.send(responseMessage); }); app.listen(3000);
-
So, a middleware is just a function that will get executed on each request
-
The callback function will accept three parameters:
- req:
request
object - res:
response
object - next: it's a function to call the following middleware
- req:
-
To call the next middleware we need to call
next()
function -
Using the express app and the
use
method we can configure express to use middlewares -
For example we can use other modules like morgan:
Example:
const express = require("express"); const logger = require("morgan"); const app = express(); app.use(logger("dev"));
-
In this case we imported
morgan
and configured it as a middleware -
We can use morgan as our server logger
-
Learn about morgan and how to use it reading the module doc
-
Using other express middleware is really simple
-
Learn more about Express middleware reading the docs
-
So far we created routes to handle our requests
-
Many times we just need to return a file like index.html, styles.css or scripts.js
-
All this files doesn't change on the server as they're all static assets
-
We can configure express to serve statics files from a folder
-
By convention we call this folder public
-
Express has a
static
method that accepts one string parameter -
This parameter represents the static folder name
-
As this method returns a function we can use it as a middleware
-
We use express
use
method to configure a middlewareExample:
const express = require("express"); const app = express(); app.use(express.static("public"));
-
In this example we set a public folder to serve our static assets
-
So for example we can call the express server and request for files like index.html, styles.css or scripts.js
-
We don't need to define our own routes for static assets (get, post, etc)
-
If we take a deep look to this urls we can see that the public folder will be our site root for static assets
/ |-public | |- img | | |- logo.png | | | |- js | | |- script.js | | | |- css | | |- styles.css | |- index.html
-
We can also create a static assets alias using
-
To create an alias we use the alias name as
app.use
first parameter -
Then we pass the middleware as second parameter
Example:
const express = require("express"); const app = express(); app.use("/assets", express.static("public"));
-
In this example we create
/assets
as our public static assets -
This means that using
/assets
as url we'll have access to thepublic
folder content -
Due to this change we need to update the way that we call our assets:
-
Using an alias we don't let the user know about our folder architecture
-
Also, we can use any folder for our static assets
-
We can change the folder name without having to update the alias
-
Also we can configure more than one folder in case we need to
-
Express will keep on looking for assets in all configured folders in case that it doesn't find it
Example:
app.use(express.static("public")); app.use(express.static("imgs"));
-
In this case express will try to find first our assets on the
public
folder -
If it doesn't find it, then it will look for them on the
imgs
folder
-
Using express we can configure a template engine
-
Express can use many different templates engines
-
We'll use Pug as it's express default template engine
-
Pug it’s a high performance and feature-rich templating engine
-
Pug helps us write shorter HTML content
-
This template used to be called
Jade
and it was renamed toPug
-
Pug works with indentation or white spaces (like Python)
-
To see Pug in action we'll have to configure express to use Pug as template engine and also create our first template
-
Install pug and set it as template engine
npm install pug --save
Example:
app.set("view engine", "pug");
-
Use
app.set
method to set ourview engine
and usepug
-
And now we create our first pug template
-
As it's a pug template we'll use the
.pug
extension
Example:
-
index.pug
doctype html html(lang='en') head title= title body h1= message div.container p Starting using Pug!
-
In some way this looks like HTML but it's much shorter as we're using Pug to write the template
-
To use this template from express we need to save it on the views folder
-
If you don't have a views folder you can create one
Project structure:
/ |- index.js |- views |- index.pug
-
The html, head, title, body & h1 template values will render the corresponding HTML tags
-
Pug compiles this templates into HTML and it will throw a compilation error if identation it's not right
-
Using pug we can assign values to the elements in the following way:
title= title
orh1= message
-
In boths cases we're assigning a value to the title element and the h1 too
-
Now we need to pass those values to the template so it can render it
-
title & message
are JavaScript variables -
Using the
=
operator in pug will assign the variables value as HTML element content -
Use
.classname
to define a class name like div.container -
And we can just write text if it's static content like
p Starting using Pug!
where the element p will have Starting using Pug! as contentindex.js
const express = require("express"); const app = express(); app.set("view engine", "pug"); app.get("/", (req, res) => { res.render("index", { title: "Hey", message: "Hello there!" }); }); app.listen(3000);
-
In this example we configured a root route
-
Using the response
render
method we can send a response to the user -
The render method accepts two parameters
- The first parameter is the template name
- The second parameter is a JavaScript object where each property will become a template variable
-
So, calling http://localhost:3000 will render the index.pug template passing Hey text as title value and Hello there! as message
-
Express will render the template and create the content to send to the user
-
The final template result will be:
<!DOCTYPE html> <html lang="en"> <head> <title>Hey</title> </head> <body> <h1>Hello there!</h1> <div class="container"> <p>Starting using Pug!</p> </div> </body> </html>
-
You can read more about the render method on Express render doc
-
Using Pug we have different ways to set HTML content
-
We already saw that we can change the content using variables
-
Also, we can use three different ways to set static content:
- Leaving one space between the element and the content
p text as content
- Adding pipe and indentation
p | text as content
- Finally using a dot and indentation
p. text as content
- Leaving one space between the element and the content
-
Use parenthesis to write HTML element attributes
a(href='contat.html', target='_blank') Contact
-
Once this renders it will became a link tag with href and target attributes
<a href="contact.html" target="_blank">Contact</a>
-
Use ids and classes using the CSS notation,
#
for ids and.
for classesdiv#main main content div.red text in red
<div id="main">main content</div> <div class="red">text in red</div>
-
Also, we can combine ids and classes
div#container.left
-
Pug has link and script tags support
-
We use the tag name and parenthesis for the attributes like the rest of the elements
link(href='/css/styles.css', rel='stylesheet') script(src='/js/scripts.js')
-
We can also add css and js code as content in case we need to add styles or JavaScript to the document
-
For CSS we need to replace link for style but for JavaScript it will still be script (like in the HTML that we already know)
style. body { color: red; } script(type='text/javascript'). const message = 'Using JS from PUG'; alert(message);
<style> body { color: red; } </style> <script type="text/javascript"> const message = "Using JS from PUG"; alert(message); </script>
-
So far Pug it's great but we're still building a complete HTML document
-
It would be nice to have a more modular way of building our UI so we can reuse sections
-
Pug has mixin support that allow us to use code blocks
-
product-mixin.pug
mixin product .product-wrapper h2 Product p Product description
-
Now that we created the product mixin we can include it on our index.pug file and use it
-
index.pug
include product-mixin body +product
-
In this example we use pug include to get the mixin content and call it from the index template
-
Using
+
we let Pug know that this is a mixin that we want to use -
We can think about mixin like functions that we call
-
At this moment we're using the product code but it's just static so it's not that helpful
-
Adding variables we can make this mixin more useful
-
product-mixin.pug
mixin product(product) .product-wrapper h2= product.title p= product.description
-
index.pug
include product-mixin body +product({title: 'PS4 Pro', description: 'Best console ever, so far..' })
-
In this example we added a product parameter to the mixin so we can pass data to it
-
When we call the mixin now we need to pass the product object as it's what the mixin is using
-
Once Pug renders this template we get the expected output
<section> <div class="product-wrapper"> <h2>PS4 Pro</h2> <p>Best console ever, so far..</p> </div> </section>
-
We can re-use this mixin for other products
-
index.pug
include product-mixin body section +product({title: 'PS4 Pro', description: 'Best console ever, so far..' }) section +product({title: 'XBOX', description: 'Other great console' })
<section> <div class="product-wrapper"> <h2>PS4 Pro</h2> <p>Best console ever, so far..</p> </div> </section> <section> <div class="product-wrapper"> <h2>XBOX</h2> <p>Other great console</p> </div> </section>
-
Learn more about mixins on Pug mixin docs
-
Pug supports template inheritance
-
To use this useful feature we need to use
block and extends keywords
-
A
block
is simply a “block” of Pug that a child template may replace over the template -
This process is recursive
-
Pug blocks can provide default content in case we need it
-
This provides a default content and is purely optional
-
The following example defines a scripts, content & foot block
-
layout.pug
doctype html html(lang='en') head title= title style(type="text/css"). body { color: red; } block scripts body block content block foot
-
With this layout we can call the scripts, content and foot blocks
-
Now we can extend this layout for other templates
-
index.pug
extends ./layout.pug include product-mixin block scripts script. alert('alert from a block'); block content h1= message div.container p Starting using Pug! p | text as content p. text as content p text as content a.foo_link(href='about.html', target='_blank') About Us div#main main content div.red text in red section +product({title: 'PS4 Pro', description: 'Best console ever, so far..' }) section +product({title: 'XBOX', description: 'Other great console' }) block foot div This footer content comes from a block
-
Using block scripts we're able to add code to the scripts layout section
-
The same happens with content and foot
-
Once Pug renders everything together we get the final HTML result
<!DOCTYPE html> <html lang="en"> <head> <title>Hey</title> <style type="text/css"> body { color: red; } </style> <script> alert("alert from a block"); </script> </head> <body> <h1>Hello there!</h1> <div class="container"> <p>Starting using Pug!</p> </div> <p>text as content</p> <p>text as content</p> <p>text as content</p> <a class="foo_link" href="about.html" target="_blank">About Us</a> <div id="main">main content</div> <div class="red">text in red</div> <section> <div class="product-wrapper"> <h2>PS4 Pro</h2> <p>Best console ever, so far..</p> </div> </section> <section> <div class="product-wrapper"> <h2>XBOX</h2> <p>Other great console</p> </div> </section> <div>This footer content comes from a block</div> </body> </html>
-
You can learn more about layouts and blocks on the Pug inheritance docs
-
So far we learned a lot about Pug and how it works and it looks like it's a really helpful tool to build our ui
-
But we're still missing some main features like using js code, iterating elements or using conditionals inside our templates
-
Use
-
to define JavaScript variables inside the templates- const niceTitle = 'This is a nice title';
-
Then we can use this title variable on our templates
h1= niceTitle p This template has a nice #{niceTitle}
-
In this example we defined a niceTitle string variable with the text: This is a nice title
-
The we used this variable on our template in two different ways
- First we assign
niceTitle
value ash1 element content
using=
operator - The we used the same
niceTitle
value as string Interpolation using#{niceTitle}
- First we assign
-
We could even use HTML encoded content and Pug will escape that content for us
- myDivContent = '<span>This content is from a template variable</span>'; div This div has a span #{myDivContent}
<div> This div has a span <span>This content is from a template variable</span> </div>
-
We can see that Pug will replace the
< >
simbols for the HTML entities so we can show the HTML tags as content instead of real html content -
You can learn more about string interpolation, escape sequence, whitespace and more on Pug interpolation docs
-
We can use conditional inside our templates
- var language = "spanish" if language == "spanish" p Estas programando muy bien, felicitaciones!! else p Your code rocks, Congrats!!
-
In this case we'll get the following HTML as we defined language as spanish
-
Also, we could change the language value to other language and we'll see the message in english
<p>Estas programando muy bien, felicitaciones!!</p>
-
Pug gives us a
unless
that works like a negated if (!)unless language != "spanish" p Your code rocks, Congrats!!
-
Learn more about conditionals on Pug conditionals doc
-
We have different options to iterate over our values using Pug
-
Use each to iterate over an array
- hookCharacters = ['Peter', 'Nana', 'Captain Hook'] ul each character in hookCharacters li= character
<ul> <li>Peter</li> <li>Nana</li> <li>Captain Hook</li> </ul>
-
In this example we use a local array but we could get this value from express and the render method
-
Using
each
we get each value of the array -
In this case we iterate the
hookCharacters
array -
So on each iteration we get a
character
value -
Then we tell Pug to use the character name as li element content
li= character
-
We can also get the iteration index
- hookCharacters = ['Peter', 'Nana', 'Captain Hook'] ul each character, index in hookCharacters li= index + ': ' + character
<ul> <li>0: Peter</li> <li>1: Nana</li> <li>2: Captain Hook</li> </ul>
-
We can also use
while
as PUG iterator- var n = 0; ul while n < 4 li= n++
<ul> <li>0</li> <li>1</li> <li>2</li> <li>3</li> </ul>
-
Learn more about iteration on Pug iteration doc
-
Check out Pug assets and sources to learm much more about this powerful template engine
-
To send data from the client to the server we can use forms or ajax calls
-
Using forms we'll be able to send values using GET and POST
-
By doing AJAX calls we can use HTTP GET, POST, UPDATE and DELETE
-
We need to configure Express routes to get the request values using one of this methods
-
If the server expects the values from GET, the client will send them using GET method
-
In case the server expects the value from POST, the client will send them using POST method
-
So we need to send values to the server the way it expected them
-
One of the most simple way to pass a value to the route handler is using the express params
-
For example we can create a route handler for
/products/
and add a product id -
In this case the URL it's going to look like:
http://localhost:3000/products/1
-
In this case we're calling the products handler and passing an id to it
-
Express has a special way to configure URL parameters and it's using
:
on the route handler -
For example we can create a route like
/products/:id
-
In this URL
:id
it's the parameter that we're going to pass -
Lets define the route this way
app.get("/products/:id", (req, res) => { res.send("Product with the id: "); });
-
In this example we see that we can create a route handler that it's expecting a URL parameter
-
In this case the url it's going to be
http://localhost:3000/products/10
-
Now we need to know how to retrieve this id parameter using Express
-
The request object has a params property that it's an object with all the parameters that this request has
-
As we configured
:id
and we called using/products/10
there's going to be anid
property on the request.params object -
To get the id from the url we use
req.params.id
app.get("/products/:id", (req, res) => { const id = req.params.id; res.send(`Product with the id: ${id}`); });
-
In this example we see how using req.params.id we can get the id from the URL
-
Other way to send values to the server it's using query string
-
We know that using an url like
http://localhost:3000/products/?id=1
we are passingid=1
as query string -
The request object has a query property that allows us to get the URL query string params
-
Using
req.query
we get an object back that represents the URL query string -
In this case we want to get the id value so we can do
req.query.id
to get1
as value backapp.get("/products", (req, res) => { const id = req.query.id; res.send(`Product with the id: ${id}`); });
-
In this example we see that the route it's waiting for a query parameter with the id name
-
If we call
http://localhost:3000/products?id=1
we pass id as query parameter with value 1 -
In this case this req.query.id will become 1 and we assign it to the id variable
-
So we can say that this are two different ways to pass values using GET and express routes
-
Notice the different between using
/products/:id
andreq.params.id
-
The other option is to use
/products
and query string likereq.query.id
-
Now that we know this we can create a form and send values to the server
<form action="/products" method="get"> <input type="text" name="username" placeholder="username" /> <input type="text" name="firstname" placeholder="firstname" /> <input type="text" name="lastname" placeholder="lastname" /> <input type="submit" value="Submit" /> </form>
-
This form has three inputs username, firstname & lastname
-
When we submit this form all this values will be submitted to
/products
-
As the form is configured to use GET it will send all this values using query string
-
The URL it's going to look like:
http://localhost:3000/products?username=nisnardi&firstname=nicolas&lastname=isnardi
-
We can see that we'll have a
username, firstname & lastname
values -
On the Express route handler this will become:
req.query.username; req.query.firstname; req.query.lastname;
-
Now we can configure our route to handle this values
app.get("/products", (req, res) => { const username = req.query.username; const firstname = req.query.firstname; const lastname = req.query.lastname; res.send( `We got the following values from the query string: ${username}, ${firstname} & ${lastname}` ); });
-
We can see that we can use query string to retrieve values from the form
-
As we sent them using GET from the form we need to use req.query to retrieve the values
-
Once the route handler gets executed it will get the query values and we define three variables to store each query string value
-
Then we just send the response using this query string values
We got the following values from the query string: nisnardi, nicolas & isnardi
-
To send data to the server using POST we can change the form action
<form action="/products" method="post"> <input type="text" name="username" placeholder="username" /> <input type="text" name="firstname" placeholder="firstname" /> <input type="text" name="lastname" placeholder="lastname" /> <input type="submit" value="Submit" /> </form>
-
As you can see the only value that changed is
method="post"
(it used to be get) -
Now the values won't be submitted as query string and instead we'll send them on the request body
-
To use Express to get POST values we need to add body-parser that's a Express middleware
-
Body parser can get all the POST requests or we can configure it just fo the routes that we want
-
Then it will get the POST values and append then as request body property
-
So in this case we're sending
username, firstname & lastname
-
And they will become
req.body.username, req.body.firstname & req.body.lastname
after executing body-parser as middleware -
To use this module we need to install it
npm install body-parser
-
Once we installed the module we need to configure it
const bodyParser = require("body-parser"); const urlencodedParser = bodyParser.urlencoded({ extended: false });
-
First we required body-parser module
-
Then we configure body-parser for urlencoded (the form enctype that we use by default)
-
We assign the body-parser return value to the
urlencodedParser
-
Now we can use this middleware in our routes
-
We'll add
urlencodedParser
to any route that we want body-parser to append the POST values into the request bodyapp.post("/products", urlencodedParser, (req, res) => { const username = req.body.username; const firstname = req.body.firstname; const lastname = req.body.lastname; console.log(req.body); res.send( `We got the following values from the query string: ${username}, ${firstname} & ${lastname}` ); });
-
In this example we configured a post route handler for
/products/
-
When we submit the form it will send the request to this route handler
-
The request will send all the form values
-
Using
urlencodedParser
we configure this route to use body-parser -
This means that when this route gets called we'll have all the form values in the
req.body
object -
To get the values sent from the form we use:
req.body.username, req.body.firstname & req.body.lastname
-
This names comes from the form inputs name
-
When we show the
req.body
value on the console we get:{ username: 'nisnardi', firstname: 'nicolas', lastname: 'isnardi' }
-
The req.body it's a JavaScript object and body-parsed added username, firstname & lastname as properties
-
Then using this properties we can get the values
-
The last thing that we need to know is how to send files
-
Express won't handle our files upload so we need to use a module called Multer
-
Multer works in a similar way that body-parser
-
We can only use multer if we're uploading images
-
Something really important is that as we're going to be uploading a file we need to set the form enctype in a different way
-
When using multer set the
form enctype
tomultipart/form-data
-
Multer will append the values to the request body
-
We can configure multer to use one or many files
-
For now we can configure to use it with one file to make it easier
-
First we need to install multer to our project
npm i multer
-
Now that we have multer installed we need to require ir and configure it
const multer = require("multer"); const upload = multer({ dest: "upload" });
-
After requiring multer as module we need to configure the folder that we're going to store the uploaded images
-
In this case we configured
upload
as the destination folder -
As we're going to send a file we need to update our form
<form action="/products" method="post" enctype="multipart/form-data"> <input type="text" name="username" placeholder="username" /> <input type="text" name="firstname" placeholder="firstname" /> <input type="text" name="lastname" placeholder="lastname" /> <input type="file" name="file" /> <input type="submit" value="Submit" /> </form>
-
To upload the file we need to change the
enctype="multipart/form-data"
so multer can take care of it -
Also we need to add a
<input type="file" name="file">
-
Note that the name of the input is
file
as this is the value that we need to set up in multer tooapp.post("/products", upload.single("file"), (req, res) => { const filename = req.file.originalname; console.log(req.body); console.log(req.file); res.send(`Congrats we uploaded the following file ${filename}`); });
-
in this example we can see that multer will store the inputs values into the request body (the same as body-parser)
-
The body object will look something like:
{ username: 'nisnardi', firstname: 'nicolas', lastname: 'isnardi' }
-
In this example we see that it looks the same way that using body-parser
-
As we configure
upload.single('file')
we told multer to get thefile
value and upload it to theupload
folder -
Using
req.file
we can access the uploaded file values -
When we configure multer with
upload.single
it will usereq.file
to append all the file values -
Remember that we used
file
as input name -
If we use other input name like
avatar
we still use req.file to get the values butupload.single('avatar')
to configure the rout handler -
Our
req.file
object will look like this:{ fieldname: 'file', originalname: 'lukecage.jpg', encoding: '7bit', mimetype: 'image/jpeg', destination: 'upload', filename: '7b4f43860a856577f5c47aba1ae592c5', path: 'upload/7b4f43860a856577f5c47aba1ae592c5', size: 13078 }
-
In this example we can see that using
originalname
property we get the uploaded file real name -
Multer will add a random name to the uploaded file by default
filename: '7b4f43860a856577f5c47aba1ae592c5'
-
This means that the file on the server won't be
lukecage.jpg
but7b4f43860a856577f5c47aba1ae592c5
-
We can configure multer to use a different file name in case we want to
-
You can read about it on the multer doc
-
Express has many cool forms to handle sessions, form validations, security and much more!
-
Go for it and look for more Express modules and learn how to use it by reading the modules doc
- NodeSchool - Free Node.js, JavaScript and more courses
- pugjs.org
- Codeburst - getting-started-with-pug-template-engine - Medium ()
- dcode - Pug (Jade) Tutorial #1 - Getting Started | HTML + NodeJS - Youtube
- jade
- Learn pug.js with pugs
- Pug.js - Cheat sheet
- Express server side rendering
- Practical Node - online book (look at the PUG sections over the index)
- Youtube - Node.js / Express / MongoDB - Build a Shopping Cart
- Youtube - Node Authentication Tutorial | Creating a User Based App from Scratch