Express handbook

The Express Handbook follows the 80/20 rule: learn in 20% of the time the 80% of a topic. I find this approach gives a well-rounded overview. This book does not try to cover everything under the sun related to Express. Express is a Node.js Web Framework. Node.js is an amazing tool for building networking services and applications. Express builds on top of its features to provide easy to use functionality that satisfy the needs of the Web Server use case.
展开查看详情

1.

2. Table of Contents The Express Handbook Express overview Request parameters Sending a response Sending a JSON response Manage Cookies Work with HTTP headers Redirects Routing CORS Templating The Pug Guide Middleware Serving static files Send files Sessions Validating input Sanitizing input Handling forms File uploads in forms An Express HTTPS server with a self-signed certificate Setup Let's Encrypt for Express 2

3.The Express Handbook The Express Handbook The Express Handbook follows the 80/20 rule: learn in 20% of the time the 80% of a topic. I find this approach gives a well-rounded overview. This book does not try to cover everything under the sun related to Express. If you think some specific topic should be included, tell me. You can reach me on Twitter @flaviocopes. I hope the contents of this book will help you achieve what you want: learn the basics Express. This book is written by Flavio. I publish web development tutorials every day on my website flaviocopes.com. Enjoy! 3

4.Express overview Express overview Express is a Node.js Web Framework. Node.js is an amazing tool for building networking services and applications. Express builds on top of its features to provide easy to use functionality that satisfy the needs of the Web Server use case. Express is a Node.js Web Framework. Node.js is an amazing tool for building networking services and applications. Express builds on top of its features to provide easy to use functionality that satisfy the needs of the Web Server use case. It's Open Source, free, easy to extend, very performant, and has lots and lots of pre-built packages you can just drop in and use, to perform all kind of things. Installation You can install Express into any project with npm: 4

5.Express overview npm install express --save or Yarn: yarn add express Both commands will also work in an empty directory, to start up your project from scratch, although npm does not create a package.json file at all, and Yarn creates a basic one. Just run npm init or yarn init if you're starting a new project from scratch. Hello World We're ready to create our first Express Web Server. Here is some code: const express = require('express') const app = express() app.get('/', (req, res) => res.send('Hello World!')) app.listen(3000, () => console.log('Server ready')) Save this to an index.js file in your project root folder, and start the server using node index.js You can open the browser to port 3000 on localhost and you should see the Hello World! message. Learn the basics of Express by understanding the Hello World code Those 4 lines of code do a lot behind the scenes. First, we import the express package to the express value. We instantiate an application by calling its app() method. Once we have the application object, we tell it to listen for GET requests on the / path, using the get() method. 5

6.Express overview There is a method for every HTTP verb: get() , post() , put() , delete() , patch() : app.get('/', (req, res) => { /* */ }) app.post('/', (req, res) => { /* */ }) app.put('/', (req, res) => { /* */ }) app.delete('/', (req, res) => { /* */ }) app.patch('/', (req, res) => { /* */ }) Those methods accept a callback function, which is called when a request is started, and we need to handle it. We pass in an arrow function: (req, res) => res.send('Hello World!') Express sends us two objects in this callback, which we called req and res , that represent the Request and the Response objects. Request is the HTTP request. It can give us all the info about that, including the request parameters, the headers, the body of the request, and more. Response is the HTTP response object that we'll send to the client. What we do in this callback is to send the 'Hello World!' string to the client, using the Response.send() method. This method sets that string as the body, and it closes the connection. The last line of the example actually starts the server, and tells it to listen on port 3000 . We pass in a callback that is called when the server is ready to accept new requests. 6

7.Request parameters Request parameters A handy reference to all the request object properties and how to use them Request parameters I mentioned how the Request object holds all the HTTP request information. These are the main properties you'll likely use: Property Description .app holds a reference to the Express app object .baseUrl the base path on which the app responds contains the data submitted in the request body (must be parsed and .body populated manually before you can access it) contains the cookies sent by the request (needs the cookie-parser .cookies middleware) .hostname the server hostname .ip the server IP .method the HTTP method used .params the route named parameters .path the URL path .protocol the request protocol .query an object containing all the query strings used in the request .secure true if the request is secure (uses HTTPS) contains the signed cookies sent by the request (needs the cookie- .signedCookies parser middleware) .xhr true if the request is an XMLHttpRequest How to retrieve the GET query string parameters using Express The query string is the part that comes after the URL path, and starts with a question mark ? . Example: 7

8.Request parameters ?name=flavio Multiple query parameters can be added using & : ?name=flavio&age=35 How do you get those query string values in Express? Express makes it very easy by populating the Request.query object for us: const express = require('express') const app = express() app.get('/', (req, res) => { console.log(req.query) }) app.listen(8080) This object is filled with a property for each query parameter. If there are no query params, it's an empty object. This makes it easy to iterate on it using the for...in loop: for (const key in req.query) { console.log(key, req.query[key]) } This will print the query property key and the value. You can access single properties as well: req.query.name //flavio req.query.age //35 How to retrieve the POST query string parameters using Express POST query parameters are sent by HTTP clients for example by forms, or when performing a POST request sending data. How can you access this data? 8

9.Request parameters If the data was sent as JSON, using Content-Type: application/json , you will use the express.json() middleware: const express = require('express') const app = express() app.use(express.json()) If the data was sent as JSON, using Content-Type: application/x-www-form-urlencoded , you will use the express.urlencoded() middleware: const express = require('express') const app = express() app.use(express.urlencoded()) In both cases you can access the data by referencing it from Request.body : app.post('/form', (req, res) => { const name = req.body.name }) Note: older Express versions required the use of the body-parser module to process POST data. This is no longer the case as of Express 4.16 (released in September 2017) and later versions. 9

10.Sending a response Sending a response How to send a response back to the client using Express In the Hello World example we used the Response.send() method to send a simple string as a response, and to close the connection: (req, res) => res.send('Hello World!') If you pass in a string, it sets the Content-Type header to text/html . if you pass in an object or an array, it sets the application/json Content-Type header, and parses that parameter into JSON. send() automatically sets the Content-Length HTTP response header. send() also automatically closes the connection. Use end() to send an empty response An alternative way to send the response, without any body, it's by using the Response.end() method: res.end() Set the HTTP response status Use the Response.status() : res.status(404).end() or res.status(404).send('File not found') sendStatus() is a shortcut: res.sendStatus(200) // === res.status(200).send('OK') res.sendStatus(403) // === res.status(403).send('Forbidden') 10

11.Sending a response res.sendStatus(404) // === res.status(404).send('Not Found') res.sendStatus(500) // === res.status(500).send('Internal Server Error') 11

12.Sending a JSON response Sending a JSON response How to serve JSON data using the Node.js Express library When you listen for connections on a route in Express, the callback function will be invoked on every network call with a Request object instance and a Response object instance. Example: app.get('/', (req, res) => res.send('Hello World!')) Here we used the Response.send() method, which accepts any string. You can send JSON to the client by using Response.json() , a useful method. It accepts an object or array, and converts it to JSON before sending it: res.json({ username: 'Flavio' }) 12

13.Manage Cookies Manage Cookies How to use the `Response.cookie()` method to manipulate your cookies Use the Response.cookie() method to manipulate your cookies. Examples: res.cookie('username', 'Flavio') This method accepts a third parameter which contains various options: res.cookie('username', 'Flavio', { domain: '.flaviocopes.com', path: '/administrator', sec ure: true }) res.cookie('username', 'Flavio', { expires: new Date(Date.now() + 900000), httpOnly: true }) The most useful parameters you can set are: Value Description domain the cookie domain name expires set the cookie expiration date. If missing, or 0, the cookie is a session cookie httpOnly set the cookie to be accessible only by the web server. See HttpOnly maxAge set the expiry time relative to the current time, expressed in milliseconds path the cookie path. Defaults to / secure Marks the cookie HTTPS only signed set the cookie to be signed sameSite Value of SameSite A cookie can be cleared with res.clearCookie('username') 13

14.Work with HTTP headers Work with HTTP headers Learn how to access and change HTTP headers using Express Access HTTP headers values from a request You can access all the HTTP headers using the Request.headers property: app.get('/', (req, res) => { console.log(req.headers) }) Use the Request.header() method to access one individual request header value: app.get('/', (req, res) => { req.header('User-Agent') }) Change any HTTP header value of a response You can change any HTTP header value using Response.set() : res.set('Content-Type', 'text/html') There is a shortcut for the Content-Type header however: res.type('.html') // => 'text/html' res.type('html') // => 'text/html' res.type('json') // => 'application/json' res.type('application/json') // => 'application/json' res.type('png') // => image/png: 14

15.Work with HTTP headers 15

16.Redirects Redirects How to redirect to other pages server-side Redirects are common in Web Development. You can create a redirect using the Response.redirect() method: res.redirect('/go-there') This creates a 302 redirect. A 301 redirect is made in this way: res.redirect(301, '/go-there') You can specify an absolute path ( /go-there ), an absolute url ( https://anothersite.com ), a relative path ( go-there ) or use the .. to go back one level: res.redirect('../go-there') res.redirect('..') You can also redirect back to the Referer HTTP header value (defaulting to / if not set) using res.redirect('back') 16

17.Routing Routing Routing is the process of determining what should happen when a URL is called, or also which parts of the application should handle a specific incoming request. Routing is the process of determining what should happen when a URL is called, or also which parts of the application should handle a specific incoming request. In the Hello World example we used this code app.get('/', (req, res) => { /* */ }) This creates a route that maps accessing the root domain URL / using the HTTP GET method to the response we want to provide. Named parameters What if we want to listen for custom requests, maybe we want to create a service that accepts a string, and returns that uppercase, and we don't want the parameter to be sent as a query string, but part of the URL. We use named parameters: app.get('/uppercase/:theValue', (req, res) => res.send(req.params.theValue.toUpperCase())) If we send a request to /uppercase/test , we'll get TEST in the body of the response. You can use multiple named parameters in the same URL, and they will all be stored in req.params . Use a regular expression to match a path You can use regular expressions to match multiple paths with one statement: app.get(/post/, (req, res) => { /* */ }) will match /post , /post/first , /thepost , /posting/something , and so on. 17

18.CORS CORS How to allow cross site requests by setting up CORS A JavaScript application running in the browser can usually only access HTTP resources on the same domain (origin) that serves it. Loading images or scripts/styles always works, but XHR and Fetch calls to another server will fail, unless that server implements a way to allow that connection. This way is called CORS, Cross-Origin Resource Sharing. Also loading Web Fonts using @font-face has same-origin policy by default, and other less popular things (like WebGL textures and drawImage resources loaded in the Canvas API). One very important thing that needs CORS is ES Modules, recently introduced in modern browsers. If you don't set up a CORS policy on the server that allows to serve 3rd part origins, the request will fail. Fetch example: XHR example: 18

19.CORS A Cross-Origin resource fails if it's: to a different domain to a different subdomain to a different port to a different protocol and it's there for your security, to prevent malicious users to exploit the Web Platform. But if you control both the server and the client, you have all the good reasons to allow them to talk to each other. How? It depends on your server-side stack. Browser support Pretty good (basically all except IE<10): Example with Express If you are using Node.js and Express as a framework, use the CORS middleware package. Here's a simple implementation of an Express Node.js server: const express = require('express') const app = express() 19

20.CORS app.get('/without-cors', (req, res, next) => { res.json({ msg: ' no CORS, no party!' }) }) const server = app.listen(3000, () => { console.log('Listening on port %s', server.address().port) }) If you hit /without-cors with a fetch request from a different origin, it's going to raise the CORS issue. All you need to do to make things work out is to require the cors package linked above, and pass it in as a middleware function to an endpoint request handler: const express = require('express') const cors = require('cors') const app = express() app.get('/with-cors', cors(), (req, res, next) => { res.json({ msg: 'WHOAH with CORS it works! ' }) }) /* the rest of the app */ I made a simple Glitch example. Here is the client working, and here's its code: https://glitch.com/edit/#!/flavio-cors-client. This is the Node.js Express server: https://glitch.com/edit/#!/flaviocopes-cors-example-express Note how the request that fails because it does not handle the CORS headings correctly is still received, as you can see in the Network panel, where you find the message the server sent: 20

21.CORS Allow only specific origins This example has a problem however: ANY request will be accepted by the server as cross- origin. As you can see in the Network panel, the request that passed has a response header access- control-allow-origin: * : You need to configure the server to only allow one origin to serve, and block all the others. Using the same cors Node library, here's how you would do it: const cors = require('cors') const corsOptions = { origin: 'https://yourdomain.com' } app.get('/products/:id', cors(corsOptions), (req, res, next) => { //... }) You can serve more as well: const whitelist = ['http://example1.com', 'http://example2.com'] const corsOptions = { origin: function(origin, callback) { if (whitelist.indexOf(origin) !== -1) { callback(null, true) } else { callback(new Error('Not allowed by CORS')) } } } 21

22.CORS Preflight There are some requests that are handled in a "simple" way. All GET requests belong to this group. Also some POST and HEAD requests do as well. POST requests are also in this group, if they satisfy the requirement of using a Content-Type of application/x-www-form-urlencoded multipart/form-data text/plain All other requests must run through a pre-approval phase, called preflight. The browser does this to determine if it has the permission to perform an action, by issuing an OPTIONS request. A preflight request contains a few headers that the server will use to check permissions (irrelevant fields omitted): OPTIONS /the/resource/you/request Access-Control-Request-Method: POST Access-Control-Request-Headers: origin, x-requested-with, accept Origin: https://your-origin.com The server will respond with something like this(irrelevant fields omitted): HTTP/1.1 200 OK Access-Control-Allow-Origin: https://your-origin.com Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE We checked for POST, but the server tells us we can also issue other HTTP request types for that particular resource. Following the Node.js Express example above, the server must also handle the OPTIONS request: var express = require('express') var cors = require('cors') var app = express() //allow OPTIONS on just one resource app.options('/the/resource/you/request', cors()) //allow OPTIONS on all resources app.options('*', cors()) 22

23.CORS 23

24.Templating Templating Express is capable of handling server-side template engines. Template engines allow us to add data to a view, and generate HTML dynamically. Express is capable of handling server-side template engines. Template engines allow us to add data to a view, and generate HTML dynamically. Express uses Jade as the default. Jade is the old version of Pug, specifically Pug 1.0. The name was changed from Jade to Pug due to a trademark issue in 2016, when the project released version 2. You can still use Jade, aka Pug 1.0, but going forward, it's best to use Pug 2.0 Although the last version of Jade is 3 years old (at the time of writing, summer 2018), it's still the default in Express for backward compatibility reasons. In any new project, you should use Pug or another engine of your choice. The official site of Pug is https://pugjs.org/. You can use many different template engines, including Pug, Handlebars, Mustache, EJS and more. Using Pug To use Pug we must first install it: npm install pug and when initializing the Express app, we need to set it: const express = require('express') const app = express() app.set('view engine', 'pug') We can now start writing our templates in .pug files. Create an about view: app.get('/about', (req, res) => { res.render('about') }) 24

25.Templating and the template in views/about.pug : p Hello from Flavio This template will create a p tag with the content Hello from Flavio . You can interpolate a variable using app.get('/about', (req, res) => { res.render('about', { name: 'Flavio' }) }) p Hello from #{name} This is a very short introduction to Pug, in the context of using it with Express. Look at the Pug guide for more information on how to use Pug. If you are used to template engines that use HTML and interpolate variables, like Handlebars (described next), you might run into issues, especially when you need to convert existing HTML to Pug. This online converter from HTML to Jade (which is very similar, but a little different than Pug) will be a great help: https://jsonformatter.org/html-to-jade Also see the differences between Jade and Pug Using Handlebars Let's try and use Handlebars instead of Pug. You can install it using npm install hbs . Put an about.hbs template file in the views/ folder: Hello from {{name}} and then use this Express configuration to serve it on /about : const express = require('express') const app = express() const hbs = require('hbs') app.set('view engine', 'hbs') app.set('views', path.join(__dirname, 'views')) app.get('/about', (req, res) => { 25

26.Templating res.render('about', { name: 'Flavio' }) }) app.listen(3000, () => console.log('Server ready')) You can also render a React application server-side, using the express-react-views package. Start with npm install express-react-views react react-dom . Now instead of requiring hbs we require express-react-views and use that as the engine, using jsx files: const express = require('express') const app = express() app.set('view engine', 'jsx') app.engine('jsx', require('express-react-views').createEngine()) app.get('/about', (req, res) => { res.render('about', { name: 'Flavio' }) }) app.listen(3000, () => console.log('Server ready')) Just put an about.jsx file in views/ , and calling /about should present you an "Hello from Flavio" string: const React = require('react') class HelloMessage extends React.Component { render() { return <div>Hello from {this.props.name}</div> } } module.exports = HelloMessage 26

27.The Pug Guide The Pug Guide How to use the Pug templating engine Introduction to Pug How does Pug look like Install Pug Setup Pug to be the template engine in Express Your first Pug template Interpolating variables in Pug Interpolate a function return value Adding id and class attributes to elements Set the doctype Meta tags Adding scripts and styles Inline scripts Loops Conditionals Set variables Incrementing variables Assigning variables to element values Iterating over variables Including other Pug files Defining blocks Extending a base template Comments Visible Invisible Introduction to Pug What is Pug? It's a template engine for server-side Node.js applications. Express is capable of handling server-side template engines. Template engines allow us to add data to a view, and generate HTML dynamically. Pug is a new name for an old thing. It's Jade 2.0. 27

28.The Pug Guide The name was changed from Jade to Pug due to a trademark issue in 2016, when the project released version 2. You can still use Jade, aka Pug 1.0, but going forward, it's best to use Pug 2.0 Also see the differences between Jade and Pug Express uses Jade as the default. Jade is the old version of Pug, specifically Pug 1.0. Although the last version of Jade is 3 years old (at the time of writing, summer 2018), it's still the default in Express for backward compatibility reasons. In any new project, you should use Pug or another engine of your choice. The official site of Pug is https://pugjs.org/. How does Pug look like p Hello from Flavio This template will create a p tag with the content Hello from Flavio . As you can see, Pug is quite special. It takes the tag name as the first thing in a line, and the rest is the content that goes inside it. If you are used to template engines that use HTML and interpolate variables, like Handlebars (described next), you might run into issues, especially when you need to convert existing HTML to Pug. This online converter from HTML to Jade (which is very similar, but a little different than Pug) will be a great help: https://jsonformatter.org/html-to-jade Install Pug Installing Pug is as simple as running npm install : npm install pug Setup Pug to be the template engine in Express and when initializing the Express app, we need to set it: const path = require('path') const express = require('express') const app = express() 28

29.The Pug Guide app.set('view engine', 'pug') app.set('views', path.join(__dirname, 'views')) Your first Pug template Create an about view: app.get('/about', (req, res) => { res.render('about') }) and the template in views/about.pug : p Hello from Flavio This template will create a p tag with the content Hello from Flavio . Interpolating variables in Pug You can interpolate a variable using app.get('/about', (req, res) => { res.render('about', { name: 'Flavio' }) }) p Hello from #{name} Interpolate a function return value You can interpolate a function return value using app.get('/about', (req, res) => { res.render('about', { getName: () => 'Flavio' }) }) p Hello from #{getName()} Adding id and class attributes to elements 29