Intro


    mkdir myapp
    cd myapp
    npm init
    # ... command prompts you for a number of things, for
      entry point: (index.js) # enter app.js as main file OR leave index.js
    # ...

    npm install express --save
    # or install temporarily and not add it to the dependencies list
    npm install express --no-save

    # app.js - simplest Express app
    const express = require('express')
    const app = express()
    const port = 3000
    app.get(
      '/',
      (req, res) => res.send('Hello World!')
    )
    app.listen(
      port,
      () => console.log("Example app listening on port ${port}!")
    )
    # run: node app.js, then load http://localhost:3000
  

Generator


    Usage: express [options] [dir]
    Options:
      -h, --help          output usage information
          --version       output the version number
      -e, --ejs           add ejs engine support
          --hbs           add handlebars engine support
          --pug           add pug engine support
      -H, --hogan         add hogan.js engine support
          --no-view       generate without view engine
      -v, --view <engine> add view <engine> support
                          (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

    #-------------------------------------------------

    .
    ├── app.js
    ├── bin
    │   └── www
    ├── package.json
    ├── public
    │   ├── images
    │   ├── javascripts
    │   └── stylesheets
    │       └── style.css
    ├── routes
    │   ├── index.js
    │   └── users.js
    └── views
        ├── error.pug
        ├── index.pug
        └── layout.pug
    # 7 directories, 9 files
  

Routing

methods


    // route definition structure:
    express_app.http_req_METHOD(PATH_on_server, HANDLER_fn)

    // homepage with "Hello World!"
    app.get('/', function (req, res) {
      res.send('Hello World!')
    })

    // --- app.get(), app.post(), app.put(), and so on
    // POST request on the root route (/)
    app.post('/', function (req, res) {
      res.send('Got a POST request')
    })
    // PUT request to the /user route
    app.put('/user', function (req, res) {
      res.send('Got a PUT request at /user')
    })
    // DELETE request to the /user route
    app.delete('/user', function (req, res) {
      res.send('Got a DELETE request at /user')
    })
    // special method app.all() - load middleware functions at a path for all HTTP request methods
    // handler is executed for requests to the route "/secret" whether using GET, POST, PUT, DELETE,
    // or any other HTTP request method supported in the http module
    app.all('/secret', function (req, res, next) {
      console.log('Accessing the secret section ...')
      next() // pass control to the next handler
    })
  

paths


    // in combination with a request method, define the endpoints at which requests can be made
    // can be strings, string patterns, or regular expressions.
    // characters ?, +, *, and () are subsets of their regular expression counterparts
    // hyphen (-) and the dot (.) are interpreted literally by string-based paths
    // to use the dollar character ($) in a path string, enclose it escaped within ([ and ])
    // the path string for requests at “/data/$book”, would be “/data/([\$])book”

    // /
    app.get('/', function (req, res) {
      res.send('root')
    })
    // /about
    app.get('/about', function (req, res) {
      res.send('about')
    })
    // /random.text
    app.get('/random.text', function (req, res) {
      res.send('random.text')
    })

    // --- paths based on string patterns
    // acd and abcd
    app.get('/ab?cd', function (req, res) {
      res.send('ab?cd')
    })
    // abcd, abbcd, abbbcd, and so on
    app.get('/ab+cd', function (req, res) {
      res.send('ab+cd')
    })
    // abcd, abxcd, abRANDOMcd, ab123cd, and so on
    app.get('/ab*cd', function (req, res) {
      res.send('ab*cd')
    })
    // /abe and /abcde
    app.get('/ab(cd)?e', function (req, res) {
      res.send('ab(cd)?e')
    })

    // --- paths based on regular expressions
    // match anything with an "a" in it
    app.get(/a/, function (req, res) {
      res.send('/a/')
    })
    // match butterfly and dragonfly, but not butterflyman, dragonflyman, and so on
    app.get(/.*fly$/, function (req, res) {
      res.send('/.*fly$/')
    })
  

parameters


    // named URL segments that are used to capture the values specified at their position in the URL
    // captured values are populated in the req.params object, with the name of the parameter
    // must be made up of "word characters" ([A-Za-z0-9_])

    // --- route path: /users/:userId/books/:bookId
    app.get('/users/:userId/books/:bookId', function (req, res) {
      res.send(req.params)
    })
    // request URL: http://localhost:3000/users/34/books/8989
    // req.params: { "userId": "34", "bookId": "8989" }

    app.get('/flights/:from-:to', function (req, res) {
      res.send(req.params)
    })
    // request URL: http://localhost:3000/flights/LAX-SFO
    // req.params: { "from": "LAX", "to": "SFO" }

    app.get('/plantae/:genus.:species', function (req, res) {
      res.send(req.params)
    })
    // request URL: http://localhost:3000/plantae/Prunus.persica
    // req.params: { "genus": "Prunus", "species": "persica" }

    // --- regular expression in parentheses, control over the exact string
    app.get('/user/:userId(\d+)', function (req, res) { // with an additional backslash !
      res.send(req.params)
    })
    // request URL: http://localhost:3000/user/42
    // req.params: {"userId": "42"}

    // in Express 4.x, the * character in regular expressions is not interpreted in the usual way
    // as a workaround, use {0,} instead of *. This will likely be fixed in Express 5
  

handlers


    // provide multiple callback functions that behave like middleware to handle a request
    // only exception: these callbacks might invoke next("route") to bypass the remaining route callbacks
    // use this mechanism to impose pre-conditions on a route,
    // then pass control to subsequent routes if there is no reason to proceed with the current route.
    // can be in the form of a function, an array of functions, or combinations of both

    // --- single callback
    app.get('/example/a', function (req, res) {
      res.send('Hello from A!')
    })

    // --- more than one callback, specify the next object
    app.get('/example/b', function (req, res, next) {
      console.log('the response will be sent by the next function ...')
      next()
    }, function (req, res) {
      res.send('Hello from B!')
    })

    // --- array of callbacks
    var cb0 = function (req, res, next) { console.log('CB0'); next(); }
    var cb1 = function (req, res, next) { console.log('CB1'); next(); }
    var cb2 = function (req, res) { res.send('Hello from C!') }
    app.get('/example/c', [cb0, cb1, cb2])

    // --- combination of independent functions and arrays of functions
    var cb0 = function (req, res, next) { console.log('CB0'); next(); }
    var cb1 = function (req, res, next) { console.log('CB1'); next(); }
    app.get('/example/d', [cb0, cb1], function (req, res, next) {
      console.log('the response will be sent by the next function ...')
      next()
    }, function (req, res) {
      res.send('Hello from D!')
    })
  

response methods


    // methods on the response object (res) can send a response to the client,
    // and terminate the request-response cycle
    // if none of these methods are called from a route handler, the client request will be left hanging
    res.download()    // prompt a file to be downloaded
    res.end()         // end the response process
    res.json()        // send a JSON response
    res.jsonp()       // send a JSON response with JSONP support
    res.redirect()    // redirect a request
    res.render()      // render a view template
    res.send()        // send a response of various types
    res.sendFile()    // send a file as an octet stream
    res.sendStatus()  // set the response status code and send its string representation as the response body

    // ...

  

app.route()


    // chainable route handlers for a route path
    // specified at a single location, creating modular routes is helpful,
    // is reducing redundancy and typos
    app.route('/book')
      .get(function (req, res) {
        res.send('Get a random book')
      })
      .post(function (req, res) {
        res.send('Add a book')
      })
      .put(function (req, res) {
        res.send('Update the book')
      })
  

express.Router


    // create modular, mountable route handlers
    // instance is a complete middleware and routing system
    // it is often referred to as a "mini-app"

    // 1 - create a router as a module
    var express = require('express')
    var router = express.Router()
    // 2 - load a middleware function in it, specific to this router
    router.use(function timeLog (req, res, next) {
      console.log('Time: ', Date.now())
      next()
    })
    // 3 - define some routes, home page route
    router.get('/', function (req, res) {
      res.send('Birds home page')
    })
    // about route
    router.get('/about', function (req, res) {
      res.send('About birds')
    })
    module.exports = router

    // 4 - mount the router module on a path in the main app
    var birds = require('./birds')
    // ...
    app.use('/birds', birds)

    // app will now be able to handle requests to /birds and /birds/about,
    // as well as call the timeLog middleware function that is specific to the route
  

routing methods corresponding to the HTTP methods of the same names

Middleware


    var express = require('express')
    var app = express()
    var myLogger = function (req, res, next) {
      console.log('LOGGED')
      next()
    }
    var requestTime = function (req, res, next) {
      req.requestTime = Date.now()
      next()
    }
    app.use(myLogger)
    app.use(requestTime)
    app.get('/', function (req, res) {
      var responseText = 'Hello World!<br>'
      responseText += '<small>Requested at: ' + req.requestTime + '</small>'
      res.send(responseText)
    })
    app.listen(3000)

    // --- configurable middleware
    // my-middleware.js
    module.exports = function (options) {
      return function (req, res, next) {
        // Implement the middleware function based on the options object
        next()
      }
    }
    // can now be used
    var mw = require('./my-middleware.js')
    app.use(mw({ option1: '1', option2: '2' }))
  

using middleware

application-level

    // bind application-level middleware to an instance with app.use() and app.METHOD()
    // where METHOD is the HTTP method of the request that the middleware function handles
    // (such as GET, PUT, or POST) in lowercase

    // --- no mount path
    // executed every time the app receives a request
    var app = express()
    app.use(function (req, res, next) {
      console.log('Time:', Date.now())
      next()
    })
    // catch 404 and forward to error handler
    // very bottom of the stack (below all other functions) to handle
    app.use(function(req, res, next) {
      next(createError(404));
    });

    // --- mounted on the /user/:id path
    // executed for any type of HTTP request on the /user/:id path
    app.use('/user/:id', function (req, res, next) {
      console.log('Request Type:', req.method)
      next()
    })

    // --- route and its handler function (middleware system)
    // handles GET requests to the /user/:id path
    app.get('/user/:id', function (req, res, next) {
      res.send('USER')
    })

    // --- loading a series of middleware functions at a mount point, with a mount path
    // a middleware sub-stack that prints request info for any type of HTTP request
    app.use('/user/:id', function (req, res, next) {
      console.log('Request URL:', req.originalUrl)
      next()
    }, function (req, res, next) {
      console.log('Request Type:', req.method)
      next()
    })

    // --- multiple routes for a path
    // two routes for GET requests to the /user/:id path
    app.get('/user/:id', function (req, res, next) {
      console.log('ID:', req.params.id)
      next()
    }, function (req, res, next) {
      res.send('User Info')
    })
    // second route will not cause any problems,
    // but it will never get called because the first route ends the request-response cycle
    // handler for the /user/:id path, which prints the user ID
    app.get('/user/:id', function (req, res, next) {
      res.end(req.params.id)
    })

    // --- skip the rest of the middleware functions from a router middleware stack,
    // call next('route') to pass control to the next route
    // will work only in middleware loaded by app.METHOD() or router.METHOD()
    app.get('/user/:id', function (req, res, next) {
      if (req.params.id === '0') next('route') // if the user ID is 0, skip to the next route
      else next() // or pass the control to the next middleware in this stack
    }, function (req, res, next) {
      // send a regular response
      res.send('regular')
    })
    // handler for the /user/:id path, which sends a special response
    app.get('/user/:id', function (req, res, next) {
      res.send('special')
    })

    // --- declared in an array for reusability
    function logOriginalUrl (req, res, next) {
      console.log('Request URL:', req.originalUrl)
      next()
    }
    function logMethod (req, res, next) {
      console.log('Request Type:', req.method)
      next()
    }
    var logStuff = [logOriginalUrl, logMethod]
    app.get('/user/:id', logStuff, function (req, res, next) {
      res.send('User Info')
    })
  
router-level

    // works same as application-level, but bound to an instance of express.Router()
    // using the router.use() and router.METHOD() functions
    var router = express.Router()

    // --- replicate shown above
    var app = express()
    var router = express.Router()
    // no mount path - executed for every request to the router
    router.use(function (req, res, next) {
      console.log('Time:', Date.now())
      next()
    })
    // a middleware sub-stack shows request info for any type of HTTP request to the /user/:id path
    router.use('/user/:id', function (req, res, next) {
      console.log('Request URL:', req.originalUrl)
      next()
    }, function (req, res, next) {
      console.log('Request Type:', req.method)
      next()
    })
    // a middleware sub-stack that handles GET requests to the /user/:id path
    router.get('/user/:id', function (req, res, next) {
      // if the user ID is 0, skip to the next router
      if (req.params.id === '0') next('route')
      // otherwise pass control to the next middleware function in this stack
      else next()
    }, function (req, res, next) {
      // render a regular page
      res.render('regular')
    })
    // handler for the /user/:id path, which renders a special page
    router.get('/user/:id', function (req, res, next) {
      console.log(req.params.id)
      res.render('special')
    })
    // mount the router on the app
    app.use('/', router)

    // --- skip the rest of the router middleware functions,
    // call next('router') to pass control back out of the router instance
    // handle GET requests to the /user/:id path
    var app = express()
    var router = express.Router()
    // predicate the router with a check and bail out when needed
    router.use(function (req, res, next) {
      if (!req.headers['x-auth']) return next('router')
      next()
    })
    router.get('/', function (req, res) {
      res.send('hello, user!')
    })
    // use the router and 401 anything falling through
    app.use('/admin', router, function (req, res) {
      res.sendStatus(401)
    })
  
error-handling

    // always takes four arguments, identify it as an error-handling middleware function
    app.use(function (err, req, res, next) {
      console.error(err.stack)
      res.status(500).send('Something broke!')
    })
  
third-party

    // node.js module for the required functionality,
    // load it in your app at the application level or at the router level

    // $ npm install cookie-parser

    var express = require('express')
    var app = express()
    var cookieParser = require('cookie-parser')

    // load the cookie-parsing middleware
    app.use(cookieParser())
  

Template engines

own template engine

    var fs = require('fs') // this engine requires the fs module
    app.engine('ntl', function (filePath, options, callback) { // define the template engine
      fs.readFile(filePath, function (err, content) {
        if (err) return callback(err)
        // this is an extremely simple template engine
        var rendered = content.toString()
          .replace('#title#', '<title>' + options.title + '</title>')
          .replace('#message#', '<h1>' + options.message + '</h1>')
        return callback(null, rendered)
      })
    })
    app.set('views', './views') // specify the views directory
    app.set('view engine', 'ntl') // register the template engine

    // app will now be able to render .ntl files
    // create a file named index.ntl in the views directory with the following content
    //
    //   #title#
    //   #message#

    // then, create the following route
    app.get('/', function (req, res) {
      res.render('index', { title: 'Hey', message: 'Hello there!' })
    })
  

Database integration

MySQL

    // --- npm install mysql
    var mysql = require('mysql')
    var connection = mysql.createConnection({
      host: 'localhost',
      user: 'dbuser',
      password: 's3kreee7',
      database: 'my_db'
    })
    connection.connect()
    connection.query('SELECT 1 + 1 AS solution', function (err, rows, fields) {
      if (err) throw err
      console.log('The solution is: ', rows[0].solution)
    })
    connection.end()
  
MongoDB

    // --- npm install mongodb
    // for an object model driver for MongoDB, look at Mongoose

    // example (v2.*)
    var MongoClient = require('mongodb').MongoClient
    MongoClient.connect('mongodb://localhost:27017/animals', function (err, db) {
      if (err) throw err
      db.collection('mammals').find().toArray(function (err, result) {
        if (err) throw err
        console.log(result)
      })
    })

    // example (v3.*)
    var MongoClient = require('mongodb').MongoClient
    MongoClient.connect('mongodb://localhost:27017/animals', function (err, client) {
      if (err) throw err
      var db = client.db('animals')
      db.collection('mammals').find().toArray(function (err, result) {
        if (err) throw err
        console.log(result)
      })
    })
  
PostgreSQL

    // --- npm install pg-promise

    var pgp = require('pg-promise')(/* options */)
    var db = pgp('postgres://username:password@host:port/database')
    db.one('SELECT $1 AS value', 123)
      .then(function (data) {
        console.log('DATA:', data.value)
      })
      .catch(function (error) {
        console.log('ERROR:', error)
      })
  
Redis

    // --- npm install redis
    var redis = require('redis')
    var client = redis.createClient()
    client.on('error', function (err) {
      console.log('Error ' + err)
    })
    client.set('string key', 'string val', redis.print)
    client.hset('hash key', 'hashtest 1', 'some value', redis.print)
    client.hset(['hash key', 'hashtest 2', 'some other value'], redis.print)
    client.hkeys('hash key', function (err, replies) {
      console.log(replies.length + ' replies:')
      replies.forEach(function (reply, i) {
        console.log('    ' + i + ': ' + reply)
      })
      client.quit()
    })
  
SQL Server

    // --- npm install tedious
    var Connection = require('tedious').Connection
    var Request = require('tedious').Request
    var config = {
      userName: 'your_username', // update me
      password: 'your_password', // update me
      server: 'localhost'
    }
    var connection = new Connection(config)
    connection.on('connect', function (err) {
      if (err) {
        console.log(err)
      } else {
        executeStatement()
      }
    })
    function executeStatement () {
      request = new Request(
        "select 123, 'hello world'",
        function (err, rowCount) {
          if (err) {
            console.log(err)
          } else {
            console.log(rowCount + ' rows')
          }
          connection.close()
        }
      )
      request.on('row', function (columns) {
        columns.forEach(function (column) {
          if (column.value === null) {
            console.log('NULL')
          } else {
            console.log(column.value)
          }
        })
      })
      connection.execSql(request)
    }
  
ElasticSearch

    // --- npm install elasticsearch
    var elasticsearch = require('elasticsearch')
    var client = elasticsearch.Client({
      host: 'localhost:9200'
    })
    client.search({
      index: 'books',
      type: 'book',
      body: {
        query: {
          multi_match: {
            query: 'express js',
            fields: ['title', 'description']
          }
        }
      }
    }).then(function (response) {
      var hits = response.hits.hits
    }, function (error) {
      console.trace(error.message)
    })
  

Static Files


    # express.static signature
    express.static(root, [options])

    # serve images, CSS files, and JS files in a directory named public
    app.use(express.static('public'))
    # load the files that are in the directory
    http://localhost:3000/images/kitten.jpg
    http://localhost:3000/css/style.css
    http://localhost:3000/js/app.js
    http://localhost:3000/images/bg.png
    http://localhost:3000/hello.html

    # for multiple static assets directories middleware multiple times
    app.use(express.static('public'))
    app.use(express.static('files'))

    # virtual path prefix, where the path does not actually exist in the file system
    # for files that are served by the express.static function,
    # specify a mount path for the static directory, as shown below
    app.use('/static', express.static('public'))
    # load the files that are in the public directory from the /static path prefix
    http://localhost:3000/static/images/kitten.jpg
    http://localhost:3000/static/css/style.css
    http://localhost:3000/static/js/app.js
    http://localhost:3000/static/images/bg.png
    http://localhost:3000/static/hello.html

    # if you run the express app from another directory
    # use the absolute path of the directory that you want to serve
    app.use('/static', express.static(path.join(__dirname, 'public')))
  

Error Handling


    // --- errors in SYNC code inside route handlers and middleware require no extra work
    app.get('/', function (req, res) {
      throw new Error('BROKEN') // Express will catch this on its own
    })

    // --- errors in ASYNC functions invoked by route handlers and middleware,
    // pass them to the next() function, where Express will catch and process them
    app.get('/', function (req, res, next) {
      fs.readFile('/file-does-not-exist', function (err, data) {
        if (err) {
          next(err) // Pass errors to Express.
        } else {
          res.send(data)
        }
      })
    })
    // pass anything to the next() function (except the string 'route')
    // Express regards the current request as being an error
    // skips any remaining non-error handling routing and middleware functions.
    // callback in a sequence provides no data, only errors, simplify this code as follows
    app.get('/', [
      function (req, res, next) {
        fs.writeFile('/inaccessible-path', 'data', next)
      },
      function (req, res) {
        res.send('OK')
      }
    ])
    // catch errors invoked by route handlers or middleware
    // and pass them to Express for processing
    app.get('/', function (req, res, next) {
      setTimeout(function () {
        // if the try...catch block were omitted,
        // error would remain uncatched since it is not part of the synchronous handler code
        try {
          throw new Error('BROKEN')
        } catch (err) {
          next(err)
        }
      }, 100)
    })
    // avoid the overhead of the try..catch block or when using promises
    app.get('/', function (req, res, next) {
      Promise.resolve().then(function () {
        throw new Error('BROKEN')
      }).catch(next) // Errors will be passed to Express.
    })
    // promises automatically catch both synchronous errors and rejected promises
    // simply provide next as the final catch handler and Express will catch errors.
    // use a chain of handlers to rely on synchronous error catching,
    // by reducing the asynchronous code to something trivial
    app.get('/', [
      function (req, res, next) {
        fs.readFile('/maybe-valid-file', 'utf-8', function (err, data) {
          res.locals.data = data
          next(err)
        })
      },
      function (req, res) {
        res.locals.data = res.locals.data.split(',')[1]
        res.send(res.locals.data)
      }
    ])
  

default error handler


    function errorHandler (err, req, res, next) {
      if (res.headersSent) {
        return next(err)
      }
      res.status(500)
      res.render('error', { error: err })
    }
  

writing error handlers


    // same way as other middleware functions
    // except with four arguments instead of three: (err, req, res, next)
    app.use(function (err, req, res, next) {
      console.error(err.stack)
      res.status(500).send('Something broke!')
    })

    // define last, after other app.use() and routes calls
    var bodyParser = require('body-parser')
    var methodOverride = require('method-override')
    app.use(bodyParser.urlencoded({
      extended: true
    }))
    app.use(bodyParser.json())
    app.use(methodOverride())
    app.use(function (err, req, res, next) {
      // logic
    })

    // --- catch 404 and forward to error handler
    // very bottom of the stack (below all other functions) to handle
    app.use(function(req, res, next) {
      next(createError(404));
    });

    // --- define several
    // error-handler for requests made by using XHR and those without
    var bodyParser = require('body-parser')
    var methodOverride = require('method-override')
    app.use(bodyParser.urlencoded({
      extended: true
    }))
    app.use(bodyParser.json())
    app.use(methodOverride())
    app.use(logErrors)
    app.use(clientErrorHandler)
    app.use(errorHandler)
    // might write request and error information to stderr
    function logErrors (err, req, res, next) {
      console.error(err.stack)
      next(err)
    }
    // here, error is explicitly passed along to the next one
    function clientErrorHandler (err, req, res, next) {
      if (req.xhr) {
        res.status(500).send({ error: 'Something failed!' })
      } else {
        next(err)
      }
    }
    // catch-all errorHandler function
    function errorHandler (err, req, res, next) {
      res.status(500)
      res.render('error', { error: err })
    }

    // --- route handler with multiple callback functions
    // use the "route" parameter to skip to the next route handler
    app.get('/a_route_behind_paywall',
      function checkIfPaidSubscriber (req, res, next) {
        if (!req.user.hasPaid) {
          // continue handling this request
          next('route')
        } else {
          next()
        }
      }, function getPaidContent (req, res, next) {
        PaidContent.find(function (err, doc) {
          if (err) return next(err)
          res.json(doc)
        })
      })
    // getPaidContent handler will be skipped
    // any remaining handlers in app for /a_route_behind_paywall would continue to be executed
  

Debug

Process managers


    // npm install --save-dev nodemon
    // npm install -g nodemon

    // ...
    "devDependencies": {
      "nodemon": "^1.18.10"
    }
    // ...
    "scripts": {
      "start": "node ./bin/www",
      "devstart": "nodemon ./bin/www"
    },
    // ...

    // SET DEBUG=express-locallibrary-tutorial:* & npm run devstart
    // DEBUG=express-locallibrary-tutorial:* npm run devstart
  

API

express()

      var express = require('express')
      var app = express()
    
--- express.json([options])
properties of the optional options object
Property Description Type Default
inflate Enables or disables handling deflated (compressed) bodies; when disabled, deflated bodies are rejected. Boolean true
limit Controls the maximum request body size. If this is a number, then the value specifies the number of bytes; if it is a string, the value is passed to the bytes library for parsing. Mixed "100kb"
reviver The reviver option is passed directly to JSON.parse as the second argument. You can find more information on this argument in the MDN documentation about JSON.parse. Function null
strict Enables or disables only accepting arrays and objects; when disabled will accept anything JSON.parse accepts. Boolean true
type This is used to determine what media type the middleware will parse. This option can be a string, array of strings, or a function. If not a function, type option is passed directly to the type-is library and this can be an extension name (like json), a mime type (like application/json), or a mime type with a wildcard (like */* or */json). If a function, the type option is called as fn(req) and the request is parsed if it returns a truthy value. Mixed "application/json"
verify This option, if supplied, is called as verify(req, res, buf, encoding), where buf is a Buffer of the raw request body and encoding is the encoding of the request. The parsing can be aborted by throwing an error. Function undefined
--- express.static(root_dir, [options])
properties of the options object
Property Description Type Default
dotfiles Determines how dotfiles (files or directories that begin with a dot “.”) are treated. allow - no special treatment for dotfiles, deny - deny a request for a dotfile, respond with 403, then call next(), ignore - act as if the dotfile does not exist, respond with 404, then call next() String ignore
etag Enable or disable etag generation

NOTE: express.static always sends weak ETags.
Boolean true
extensions Sets file extension fallbacks: If a file is not found, search for files with the specified extensions and serve the first one found. Example: ['html', 'htm']. Mixed false
fallthrough Let client errors fall-through as unhandled requests, otherwise forward a client error. When this option is true, client errors such as a bad request or a request to a non-existent file will cause this middleware to simply call next() to invoke the next middleware in the stack. When false, these errors (even 404s), will invoke next(err). Set this option to true so you can map multiple physical directories to the same web address or for routes to fill in non-existent files. Use false if you have mounted this middleware at a path designed to be strictly a single file system directory, which allows for short-circuiting 404s for less overhead. This middleware will also reply to all methods Boolean true
immutable Enable or disable the immutable directive in the Cache-Control response header. If enabled, the maxAge option should also be specified to enable caching. The immutable directive will prevent supported clients from making conditional requests during the life of the maxAge option to check if the file has changed. Boolean false
index Sends the specified directory index file. Set to false to disable directory indexing. Mixed “index.html”
lastModified Set the Last-Modified header to the last modified date of the file on the OS. Boolean true
maxAge Set the max-age property of the Cache-Control header in milliseconds or a string in ms format. Number 0
redirect Redirect to trailing “/” when the pathname is a directory. Boolean true
setHeaders fn(res, path, stat) - Function for setting HTTP headers to serve with the file. Set custom response headers. Alterations to the headers must occur synchronously. Arguments: res - response object, path - file path that is being sent, stat - stat object of the file that is being sent Function  

      var options = {
        dotfiles: 'ignore',
        etag: false,
        extensions: ['htm', 'html'],
        index: false,
        maxAge: '1d',
        redirect: false,
        setHeaders: function (res, path, stat) {
          res.set('x-timestamp', Date.now())
        }
      }
      app.use(express.static('public', options))
    
--- express.Router([options])
optional options parameter
Property Description Default Availability
caseSensitive Enable case sensitivity. Disabled by default, treating “/Foo” and “/foo” as the same.  
mergeParams Preserve the req.params values from the parent router. If the parent and the child have conflicting param names, the child’s value take precedence. false 4.5.0+
strict Enable strict routing. Disabled by default, “/foo” and “/foo/” are treated the same by the router.  

      var router = express.Router([options])
    
--- express.urlencoded([options])
properties of the optional options object
Property Description Type Default
extended This option allows to choose between parsing the URL-encoded data with the querystring library (when false) or the qs library (when true). The “extended” syntax allows for rich objects and arrays to be encoded into the URL-encoded format, allowing for a JSON-like experience with URL-encoded. For more information, please see the qs library. Boolean true
inflate Enables or disables handling deflated (compressed) bodies; when disabled, deflated bodies are rejected. Boolean true
limit Controls the maximum request body size. If this is a number, then the value specifies the number of bytes; if it is a string, the value is passed to the bytes library for parsing. Mixed "100kb"
parameterLimit This option controls the maximum number of parameters that are allowed in the URL-encoded data. If a request contains more parameters than this value, an error will be raised. Number 1000
type This is used to determine what media type the middleware will parse. This option can be a string, array of strings, or a function. If not a function, type option is passed directly to the type-is library and this can be an extension name (like urlencoded), a mime type (like application/x-www-form-urlencoded), or a mime type with a wildcard (like */x-www-form-urlencoded). If a function, the type option is called as fn(req) and the request is parsed if it returns a truthy value. Mixed "application/x-www-form-urlencoded"
verify This option, if supplied, is called as verify(req, res, buf, encoding), where buf is a Buffer of the raw request body and encoding is the encoding of the request. The parsing can be aborted by throwing an error. Function undefined
app

      var express = require('express')
      var app = express()
      app.get('/', function (req, res) {
        res.send('hello world')
      })
      app.listen(3000)
    
--- properties

      // --- app.locals - local variables within the application
      // once set, persist throughout the life of the application,
      // in contrast with res.locals properties
      // that are valid only for the lifetime of the request
      console.dir(app.locals.title) // => 'My App'
      console.dir(app.locals.email) // => 'me@myapp.com'
      // access local variables in templates rendered within the application
      app.locals.title = 'My App'
      app.locals.strftime = require('strftime')
      app.locals.email = 'me@myapp.com'
      // providing helper functions to templates, as well as application-level data
      // available in middleware via req.app.locals

      // --- app.mountpath - one or more path patterns on which a sub-app was mounted
      // is an instance of express that may be used for handling the request to a route
      var express = require('express')
      var app = express() // the main app
      var admin = express() // the sub app
      admin.get('/', function (req, res) {
        console.log(admin.mountpath) // /admin
        res.send('Admin Homepage')
      })
      app.use('/admin', admin) // mount the sub app
      // similar to the baseUrl property of the req object,
      // except req.baseUrl returns the matched URL path, instead of the matched patterns

      // if a sub-app is mounted on multiple path patterns,
      // app.mountpath returns the list of patterns it is mounted on
      var admin = express()
      admin.get('/', function (req, res) {
        console.dir(admin.mountpath) // [ '/adm*n', '/manager' ]
        res.send('Admin Homepage')
      })
      var secret = express()
      secret.get('/', function (req, res) {
        console.log(secret.mountpath) // /secr*t
        res.send('Admin Secret')
      })
      // load the 'secret' router on '/secr*t', on the 'admin' sub app
      admin.use('/secr*t', secret)
      // load the 'admin' router on '/adm*n' and '/manager', on the parent app
      app.use(['/adm*n', '/manager'], admin)
    
--- events

      // --- app.on('mount', callback(parent))
      // fired on a sub-app, when it is mounted on a parent app
      // parent app is passed to the callback function
      // sub-apps will:
      //   not inherit the value of settings that have a default value
      //     you must set the value in the sub-app
      //   inherit the value of settings with no default value
      var admin = express()
      admin.on('mount', function (parent) {
        console.log('Admin Mounted')
        console.log(parent) // refers to the parent app
      })
      admin.get('/', function (req, res) {
        res.send('Admin Homepage')
      })
      app.use('/admin', admin)
    
--- methods

      // --- app.all(path, callback [, callback ...]) - matches all HTTP verbs
      app.all('/secret', function (req, res, next) {
        console.log('Accessing the secret section ...')
        next() // pass control to the next handler
      })
      // all routes from following point require authentication,
      // and automatically load a user
      // these callbacks do not have to act as end-points:
      // loadUser can perform a task, then call next() to continue matching subsequent routes
      app.all('*', requireAuthentication, loadUser)
      // or
      app.all('*', requireAuthentication)
      app.all('*', loadUser)
      // similar but but it only restricts paths that start with “/api”
      app.all('/api/*', requireAuthentication)

      // --- app.METHOD(path, callback [, callback ...])
      // routes an HTTP request, where METHOD is the HTTP method of the request,
      // such as GET, PUT, POST, and so on, in lowercase
      // path: string, path pattern, regular expression, array of combinations
      // callback: middleware,
      //           series of middleware (separated by commas), array of middleware,
      //           a combination of all of the above
      app.get('/', function (req, res) { // HTTP GET
        res.send('GET request to homepage')
      })
      app.post('/', function (req, res) {
        res.send('POST request to homepage')
      })
      app.put('/', function (req, res) {
        res.send('PUT request to homepage')
      })
      app.delete('/', function (req, res) { // HTTP DELETE
        res.send('DELETE request to homepage')
      })

      // --- app.route(path)
      // returns an instance of a single route,
      // which you can then use to handle HTTP verbs with optional middleware
      // use to avoid duplicate route names (and thus typo errors)
      var app = express()
      app.route('/events')
        .all(function (req, res, next) {
          // runs for all HTTP verbs first
          // think of it as route specific middleware!
        })
        .get(function (req, res, next) {
          res.json({})
        })
        .post(function (req, res, next) {
          // maybe add a new event...
        })

      // --- app.param([name], callback)
      // add callback triggers to route parameters,
      // name - name of the parameter or an array of them,
      // callback - callback function, parameters are:
      // request, response, next middleware, value and name of the parameter.
      // if name is an array - callback trigger is registered for each parameter declared in it,
      // in the order in which they are declared
      // for each declared parameter except the last one,
      // a call to next inside the callback will call the callback for the next declared parameter
      // for the last parameter, a call to next will call the next middleware in place
      // for the route currently being processed, just like it would if name were just a string.
      // map user loading logic when :user is present in a route path
      // to automatically provide req.user to the route, or perform validations on the parameter input
      app.param('user', function (req, res, next, id) {
        // try to get the user details from the User model and attach it to the request object
        User.find(id, function (err, user) {
          if (err) {
            next(err)
          } else if (user) {
            req.user = user
            next()
          } else {
            next(new Error('failed to load user'))
          }
        })
      })
      // param callback functions are local to the router on which they are defined
      // they are not inherited by mounted apps or routers
      // will be triggered only by route parameters defined on app routes.
      // all param callbacks will be called before any handler of any route in which the param occurs,
      // and they will each be called only once in a request-response cycle,
      // even if the parameter is matched in multiple routes
      app.param('id', function (req, res, next, id) {
        console.log('CALLED ONLY ONCE')
        next()
      })
      app.get('/user/:id', function (req, res, next) {
        console.log('although this matches')
        next()
      })
      app.get('/user/:id', function (req, res) {
        console.log('and this matches too')
        res.end()
      })
      // GET /user/42
      //
      //     CALLED ONLY ONCE
      //     although this matches
      //     and this matches too
      //
      app.param(['id', 'page'], function (req, res, next, value) {
        console.log('CALLED ONLY ONCE with', value)
        next()
      })
      app.get('/user/:id/:page', function (req, res, next) {
        console.log('although this matches')
        next()
      })
      app.get('/user/:id/:page', function (req, res) {
        console.log('and this matches too')
        res.end()
      })
      // GET /user/42/3
      //
      //     CALLED ONLY ONCE with 42
      //     CALLED ONLY ONCE with 3
      //     although this matches
      //     and this matches too
      //
      // . - character cant be used to capture a character in your capturing regexp
      // cant use '/user-.+/' to capture 'users-gami',
      // use [\\s\\S] or [\\w\\W] instead (as in '/user-[\\s\\S]+/'
      // captures '1-a_6' but not '543-azser-sder'
      router.get('/[0-9]+-[[\\w]]*', function(){});
      // captures '1-a_6' and '543-az(ser"-sder' but not '5-a s'
      router.get('/[0-9]+-[[\\S]]*', function(){});
      // captures all (equivalent to '.*')
      router.get('[[\\s\\S]]*', function(){});

      // --- app.get(name)
      // --- app.set(name, value)
      // assigns and get value of app settings table
      // certain names can be used to configure the behavior of the server
      app.get('title') // => undefined
      app.set('title', 'My Site')
      app.get('title') // => "My Site"
      // --- app.disable(name)
      // --- app.disabled(name)
      // --- app.enable(name)
      // --- app.enabled(name)
      // set Boolean setting in app settings table
      app.disable('trust proxy')
      app.get('trust proxy') // => false
      app.disabled('trust proxy') // => true
      app.enable('trust proxy')
      app.disabled('trust proxy') // => false
      app.enable('trust proxy')
      app.get('trust proxy') // => true
      app.enabled('trust proxy') // => false
      app.enable('trust proxy')
      app.enabled('trust proxy') // => true

      // --- app.render(view, [locals], callback)
      // returns the rendered HTML of a view via the callback function
      // accepts an optional parameter that is an object containing local variables
      // is like res.render(), except it cannot send the rendered view to the client on its own.
      // utility function for generating rendered view strings.
      // internally res.render() uses app.render() to render views.
      // local variable cache is reserved for enabling view cache,
      // set it to true, if you want to cache view during development
      // view caching is enabled in production by default
      app.render('email', function (err, html) {
        // ...
      })
      app.render('email', { name: 'Tobi' }, function (err, html) {
        // ...
      })

      // --- app.engine(ext, callback) - registers the given template engine callback as ext
      app.engine('pug', require('pug').__express)
      // for engines that do not provide .__express out of the box,
      // or to map a different extension to the template engine
      app.engine('html', require('ejs').renderFile)
      // in this case, EJS provides a .renderFile() method
      // with the same signature that Express expects: (path, options, callback),
      // though note that it aliases this method as ejs.__express internally
      // so if you’re using ".ejs" extensions you dont need to do anything
      var engines = require('consolidate')
      app.engine('haml', engines.haml)
      app.engine('html', engines.hogan)

      // --- app.listen(path, [callback])
      // starts a UNIX socket and listens for connections on the given path
      // identical to Node http.Server.listen()
      var express = require('express')
      var app = express()
      app.listen('/tmp/sock')

      // --- app.listen([port[, host[, backlog]]][, callback])
      // binds and listens for connections on the specified host and port
      // identical to Node http.Server.listen()
      // if port is omitted or is 0, the operating system will assign an arbitrary
      // useful for cases like automated tasks (tests, etc.)
      var express = require('express')
      var app = express()
      app.listen(3000)
      // app returned by express() is in fact a JavaScript Function,
      // designed to be passed to Node HTTP servers as a callback to handle requests
      // this makes it easy to provide both HTTP and HTTPS versions of app
      // with the same code base, app does not inherit from these (it is simply a callback)
      var express = require('express')
      var https = require('https')
      var http = require('http')
      var app = express()
      http.createServer(app).listen(80)
      https.createServer(options, app).listen(443)
      // method returns an http.Server object and (for HTTP)
      // is a convenience method for the following
      app.listen = function () {
        var server = http.createServer(this)
        return server.listen.apply(server, arguments)
      }
      // all the forms of Node http.Server.listen() method are in fact actually supported
    
app.use([path,] callback [, callback...]) - mounts middleware(s) at the specified path

      // path defaults to "/", middleware mounted without a path
      // will be executed for every request to the app
      app.use(function (req, res, next) {
        console.log('Time: %d', Date.now())
        next()
      })

      // middleware functions are executed sequentially,
      // order of middleware inclusion is important
      // this middleware will not allow the request to go beyond it
      app.use(function (req, res, next) {
        res.send('Hello World')
      })
      // requests will never reach this route
      app.get('/', function (req, res) {
        res.send('Welcome')
      })

      // error-handling middleware always takes four arguments
      // provide four arguments to identify it as an error-handling middleware function
      // otherwise, the next object will be interpreted as regular middleware
      // and will fail to handle errors
      app.use(function (err, req, res, next) {
        console.error(err.stack)
        res.status(500).send('Something broke!')
      })
    
--- path examples

      // --- path
      // paths starting with "/abcd"
      app.use('/abcd', function (req, res, next) {
        next();
      });

      // --- path pattern
      // paths starting with "/abcd" and "/abd"
      app.use('/abc?d', function (req, res, next) {
        next();
      });
      // paths starting with "/abcd", "/abbcd", "/abbbbbcd", and so on
      app.use('/ab+cd', function (req, res, next) {
        next();
      });
      // paths starting with "/abcd", "/abxcd", "/abFOOcd", "/abbArcd", and so on
      app.use('/ab\*cd', function (req, res, next) {
        next();
      });
      // paths starting with "/ad" and "/abcd"
      app.use('/a(bc)?d', function (req, res, next) {
        next();
      });

      // --- regular Expression
      // paths starting with "/abc" and "/xyz"
      app.use(/\/abc|\/xyz/, function (req, res, next) {
        next();
      });

      // --- array
      // paths starting with "/abcd", "/xyza", "/lmn", and "/pqr"
      app.use(['/abcd', '/xyza', /\/lmn|\/pqr/], function (req, res, next) {
        next();
      });
    
--- middleware callback function examples

      // --- single middleware
      // define and mount a middleware function locally
      app.use(function (req, res, next) {
        next();
      });
      // router is valid middleware
      var router = express.Router();
      router.get('/', function (req, res, next) {
        next();
      });
      app.use(router);
      // Express app is valid middleware
      var subApp = express();
      subApp.get('/', function (req, res, next) {
        next();
      });
      app.use(subApp);

      // --- series of middleware
      // specify more than one middleware function at the same mount path
      var r1 = express.Router();
      r1.get('/', function (req, res, next) {
        next();
      });
      var r2 = express.Router();
      r2.get('/', function (req, res, next) {
        next();
      });
      app.use(r1, r2);

      // --- array
      // to group middleware logically
      // if you pass an array of middleware as the first or only middleware parameters,
      // specify the mount path
      var r1 = express.Router();
      r1.get('/', function (req, res, next) {
        next();
      });
      var r2 = express.Router();
      r2.get('/', function (req, res, next) {
        next();
      });
      app.use('/', [r1, r2]);

      // --- combination
      // combine all the above ways of mounting middleware
      function mw1(req, res, next) { next(); }
      function mw2(req, res, next) { next(); }
      var r1 = express.Router();
      r1.get('/', function (req, res, next) { next(); });
      var r2 = express.Router();
      r2.get('/', function (req, res, next) { next(); });
      var subApp = express();
      subApp.get('/', function (req, res, next) { next(); });
      app.use(mw1, [mw2, r1, r2], subApp);
    
--- express.static

      // serve static content for the app from the "public" directory in the application directory
      // GET /style.css etc
      app.use(express.static(path.join(__dirname, 'public')))
      // mount the middleware at "/static" to serve static content
      // only when their request path is prefixed with "/static"
      // GET /static/style.css etc.
      app.use('/static', express.static(path.join(__dirname, 'public')))
      // disable logging for static content request, load logger middleware after the static middleware
      app.use(express.static(path.join(__dirname, 'public')))
      app.use(logger())
      // serve static files from multiple directories, but give precedence to "./public" over the others
      app.use(express.static(path.join(__dirname, 'public')))
      app.use(express.static(path.join(__dirname, 'files')))
      app.use(express.static(path.join(__dirname, 'uploads')))
    
application settings
PropertyTypeDescriptionDefault

case sensitive routing

Boolean

Enable case sensitivity. When enabled, "/Foo" and "/foo" are different routes. When disabled, "/Foo" and "/foo" are treated the same.

NOTE: Sub-apps will inherit the value of this setting.

N/A (undefined)

env

String Environment mode. Be sure to set to "production" in a production environment; see Production best practices: performance and reliability.

process.env.NODE_ENV (NODE_ENV environment variable) or “development” if NODE_ENV is not set.

etag

Varied

Set the ETag response header. For possible values, see the etag options table.

More about the HTTP ETag header.

weak

jsonp callback name

String Specifies the default JSONP callback name.

“callback”

json escape

Boolean

Enable escaping JSON responses from the res.json ,res.jsonp, and res.send APIs. This will escape the characters < ,>, and & as Unicode escape sequences in JSON. The purpose of this it to assist with mitigating certain types of persistent XSS attacks when clients sniff responses for HTML.

NOTE: Sub-apps will inherit the value of this setting.

N/A (undefined)

json replacer

Varied The 'replacer' argument used by "JSON.stringify".

NOTE: Sub-apps will inherit the value of this setting.

N/A (undefined)

json spaces

Varied The 'space' argument used by "JSON.stringify". This is typically set to the number of spaces to use to indent prettified JSON.

NOTE: Sub-apps will inherit the value of this setting.

N/A (undefined)

query parser

Varied

Disable query parsing by setting the value to false, or set the query parser to use either “simple” or “extended” or a custom query string parsing function.

The simple query parser is based on Node’s native query parser, querystring.

The extended query parser is based on qs.

A custom query string parsing function will receive the complete query string, and must return an object of query keys and their values.

"extended"

strict routing

Boolean

Enable strict routing. When enabled, the router treats "/foo" and "/foo/" as different. Otherwise, the router treats "/foo" and "/foo/" as the same.

NOTE: Sub-apps will inherit the value of this setting.

N/A (undefined)

subdomain offset

Number The number of dot-separated parts of the host to remove to access subdomain. 2

trust proxy

Varied

Indicates the app is behind a front-facing proxy, and to use the X-Forwarded-* headers to determine the connection and the IP address of the client. NOTE: X-Forwarded-* headers are easily spoofed and the detected IP addresses are unreliable.

When enabled, Express attempts to determine the IP address of the client connected through the front-facing proxy, or series of proxies. The "req.ips" property, then contains an array of IP addresses the client is connected through. To enable it, use the values described in the trust proxy options table.

The "trust proxy" setting is implemented using the proxy-addr package. For more information, see its documentation.

NOTE: Sub-apps will inherit the value of this setting, even though it has a default value.

false (disabled)

views

String or Array A directory or an array of directories for the application's views. If an array, the views are looked up in the order they occur in the array.

process.cwd() + '/views'

view cache

Boolean

Enables view template compilation caching.

NOTE: Sub-apps will not inherit the value of this setting in production (when "NODE_ENV" is "production").

true in production, otherwise undefined.

view engine

String The default engine extension to use when omitted.

NOTE: Sub-apps will inherit the value of this setting.

N/A (undefined)

x-powered-by

Boolean Enables the "X-Powered-By: Express" HTTP header.

true

--- options for "trust proxy" setting
TypeValue
Boolean

If true, the client’s IP address is understood as the left-most entry in the X-Forwarded-* header.

If false, the app is understood as directly facing the Internet and the client’s IP address is derived from req.connection.remoteAddress. This is the default setting.

String
String containing comma-separated values
Array of strings

An IP address, subnet, or an array of IP addresses, and subnets to trust. Pre-configured subnet names are:

  • loopback - 127.0.0.1/8 ,::1/128
  • linklocal - 169.254.0.0/16 ,fe80::/10
  • uniquelocal - 10.0.0.0/8 ,172.16.0.0/12 ,192.168.0.0/16 ,fc00::/7

Set IP addresses in any of the following ways:

Specify a single subnet:


        app.set('trust proxy', 'loopback')
        

Specify a subnet and an address:


        app.set('trust proxy', 'loopback, 123.123.123.123')
        

Specify multiple subnets as CSV:


        app.set('trust proxy', 'loopback, linklocal, uniquelocal')
        

Specify multiple subnets as an array:


        app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal'])
        

When specified, the IP addresses or the subnets are excluded from the address determination process, and the untrusted IP address nearest to the application server is determined as the client’s IP address.

Number

Trust the nth hop from the front-facing proxy server as the client.

Function

Custom trust implementation. Use this only if you know what you are doing.


        app.set('trust proxy', function (ip) {
          if (ip === '127.0.0.1' || ip === '123.123.123.123') return true // trusted IPs
          else return false
        })
        
--- options for "etag" setting
TypeValue
Boolean

true enables weak ETag. This is the default setting.
false disables ETag altogether.

String If "strong", enables strong ETag.
If "weak", enables weak ETag.
Function

Custom ETag function implementation. Use this only if you know what you are doing.


        app.set('etag', function (body, encoding) {
          return generateHash(body, encoding) // consider the function is defined
        })
        
request
--- properties

      // --- req.app
      // holds a reference to the instance of the Express app that is using the middleware.
      // create a module that just exports a middleware and require() it in your main file,
      // then the middleware can access the Express instance via req.app
      // index.js
      app.get('/viewdirectory', require('./mymiddleware.js'))
      // mymiddleware.js
      module.exports = function (req, res) {
        res.send('The views directory is ' + req.app.get('views'))
      }

      // --- req.baseUrl
      // URL path on which a router instance was mounted
      // similar to the mountpath property of the app object,
      // except app.mountpath returns the matched path pattern(s)
      var greet = express.Router()
      greet.get('/jp', function (req, res) {
        console.log(req.baseUrl) // /greet
        res.send('Konichiwa!')
      })
      app.use('/greet', greet) // load the router on '/greet'
      // even if you use a path pattern or a set of path patterns to load the router,
      // baseUrl property returns the matched string, not the pattern(s).
      // greet router is loaded on two path patterns
      app.use(['/gre+t', '/hel{2}o'], greet) // load the router on '/gre+t' and '/hel{2}o'
      // when a request is made to /greet/jp, req.baseUrl is "/greet"
      // when a request is made to /hello/jp, req.baseUrl is “/hello”

      // --- req.cookies
      // https://github.com/expressjs/cookie-parser
      // when using cookie-parser middleware, this property is an object
      // that contains cookies sent by the request
      // if the request contains no cookies, it defaults to {}
      // Cookie: name=tj
      console.dir(req.cookies.name) // => 'tj'
      // if the cookie has been signed, use req.signedCookies

      // --- req.fresh
      // https://github.com/jshttp/fresh
      // whether the request is "fresh", opposite of req.stale
      // true, if the cache-control request header doesnt have a no-cache directive
      // and any of the following are true:
      //   if-modified-since request header is specified and last-modified request header
      //     is equal to or earlier than the modified response header
      //   if-none-match request header is *
      //   if-none-match request header, after being parsed into its directives,
      //     does not match the etag response header
      console.dir(req.fresh) // => true

      // --- req.stale
      // whether the request is "stale" and is the opposite of req.fresh
      console.dir(req.stale) // => true

      // --- req.hostname
      // hostname derived from the Host HTTP header or X-Forwarded-Host
      // can be set by the client or by the proxy
      // when more than one X-Forwarded-Host header in the request, first is used
      // Host: "example.com:3000"
      console.dir(req.hostname) // => 'example.com'

      // --- req.ip
      // remote IP address of the request
      // if trust proxy setting does not evaluate to false,
      // value of this property is derived from the left-most entry in the X-Forwarded-For header
      // header can be set by the client or by the proxy
      console.dir(req.ip) // => '127.0.0.1'

      // --- req.ips
      // when the trust proxy setting does not evaluate to false,
      // contains an array of IP addresses specified in the X-Forwarded-For request header
      // or an empty array
      // this header can be set by the client or by the proxy.
      // if X-Forwarded-For is client, proxy1, proxy2,
      // req.ips would be ["client", "proxy1", "proxy2"], where proxy2 is the furthest downstream

      // --- req.method
      // a string corresponding to the HTTP method of the request: GET, POST, PUT, and so on

      // --- req.originalUrl
      // is much like req.url
      // retains the original request URL,
      // allowing you to rewrite req.url freely for internal routing purposes
      // for example, "mounting" feature of app.use() will rewrite req.url to strip the mount point
      // GET /search?q=something
      console.dir(req.originalUrl) // => '/search?q=something'
      // in a middleware function, req.originalUrl is a combination of req.baseUrl and req.path
      app.use('/admin', function (req, res, next) { // GET 'http://www.example.com/admin/new'
        console.dir(req.originalUrl) // '/admin/new'
        console.dir(req.baseUrl) // '/admin'
        console.dir(req.path) // '/new'
        next()
      })

      // --- req.params
      // object containing properties mapped to the named route parameters
      // if you have the route /user/:name, "name" property is available as req.params.name
      // defaults to {}
      // GET /user/tj
      console.dir(req.params.name) // => 'tj'
      // when using a regular expression for the route definition,
      // capture groups are provided in the array using req.params[n],
      // where n is the nth capture group
      // this rule is applied to unnamed wild card matches with string routes such as /file/*
      // GET /file/javascripts/jquery.js
      console.dir(req.params[0]) // => 'javascripts/jquery.js'
      // if you need to make changes to a key in req.params, use the app.param handler
      // changes are applicable only to parameters already defined in the route path
      // any changes made to the req.params object in a middleware or route handler will be reset

      // --- req.path
      // path part of the request URL
      // example.com/users?sort=desc
      console.dir(req.path) // => '/users'

      // --- req.protocol
      // request protocol string: either http or (for TLS requests) https
      // when the trust proxy setting does not evaluate to false
      // will use the value of the X-Forwarded-Proto header field if present.
      // can be set by the client or by the proxy
      console.dir(req.protocol) // => 'http'

      // --- req.query
      // object containing a property for each query string parameter in the route
      // or empty object, {}
      // all properties and values in this object are untrusted and should be validated
      // req.query.foo.toString() may fail in multiple ways:
      //   foo may not be there or may not be a string,
      //   toString may not be a function and instead a string or other user-input
      // GET /search?q=tobi+ferret
      console.dir(req.query.q) // => 'tobi ferret'
      // GET /shoes?order=desc&shoe[color]=blue&shoe[type]=converse
      console.dir(req.query.order) // => 'desc'
      console.dir(req.query.shoe.color) // => 'blue'
      console.dir(req.query.shoe.type) // => 'converse'
      // GET /shoes?color[]=blue&color[]=black&color[]=red
      console.dir(req.query.color) // => ['blue', 'black', 'red']

      // --- req.route
      // currently-matched route, a string
      app.get('/user/:id?', function userIdHandler (req, res) {
        console.log(req.route)
        res.send('GET')
      })
      // output from the previous snippet
      {
        path: '/user/:id?',
        stack: [{
          handle: [Function: userIdHandler],
          name: 'userIdHandler',
          params: undefined,
          path: undefined,
          keys: [],
          regexp: /^\/?$/i,
          method: 'get'
        }],
        methods: { get: true }
      }

      // --- req.secure
      // Boolean property that is true if a TLS connection is established, equivalent to:
      console.dir(req.protocol === 'https') // => true

      // --- req.signedCookies
      // https://github.com/expressjs/cookie-parser
      // when using cookie-parser middleware, this property contains signed cookies
      // sent by the request, unsigned and ready for use
      // signed cookies reside in a different object to show developer intent;
      // otherwise, a malicious attack could be placed on req.cookie values (which are easy to spoof).
      // signing a cookie does not make it "hidden" or encrypted;
      // but simply prevents tampering (because the secret used to sign is private).
      // if no signed cookies are sent, the property defaults to {}
      // Cookie: user=tobi.CP7AWaXDfAKIRfH49dQzKJx7sKzzSoPq7/AcBBRVwlI3
      console.dir(req.signedCookies.user) // => 'tobi'

      // --- req.subdomains
      // array of subdomains in the domain name of the request
      // Host: "tobi.ferrets.example.com"
      console.dir(req.subdomains) // => ['ferrets', 'tobi']
      // application property "subdomain offset", which defaults to 2,
      // is used for determining the beginning of the subdomain segments

      // --- req.xhr
      // true, if X-Requested-With header field is “XMLHttpRequest”
      console.dir(req.xhr) // => true
    
--- methods

      // --- req.accepts(types)
      // https://github.com/expressjs/accepts
      // checks if the specified content types are acceptable,
      // based on the request Accept HTTP header field
      // returns the best match, or if none of the specified content types is acceptable, false
      // (in which case, the application should respond with 406 "Not Acceptable")
      // type value may be a single MIME type string (such as "application/json"),
      // an extension name such as "json", a comma-delimited list, or an array
      // Accept: text/html
      req.accepts('html') // => "html"
      // Accept: text/*, application/json
      req.accepts('html') // => "html"
      req.accepts('text/html') // => "text/html"
      req.accepts(['json', 'text']) // => "json"
      req.accepts('application/json') // => "application/json"
      // Accept: text/*, application/json
      req.accepts('image/png')
      req.accepts('png') // => undefined
      // Accept: text/*;q=.5, application/json
      req.accepts(['html', 'json']) // => "json"
      // --- req.acceptsCharsets(charset [, ...])
      // --- req.acceptsEncodings(encoding [, ...])
      // --- req.acceptsLanguages(lang [, ...])
      // first accepted charset/encoding/language of the specified character sets,
      // based on the request Accept-Charset/Encoding/Language HTTP header field
      // if none of the specified charsets is accepted, returns false

      // --- req.get(field)
      // specified HTTP request header field (case-insensitive match)
      // Referrer and Referer fields are interchangeable
      req.get('Content-Type') // => "text/plain"
      req.get('content-type') // => "text/plain"
      req.get('Something') // => undefined
      // aliased as req.header(field)

      // --- req.is(type)
      // https://github.com/expressjs/type-is
      // matching content type
      // if "Content-Type" HTTP header matches the MIME type specified by the type parameter
      // if the request has no body, returns null, returns false otherwise
      // with Content-Type: text/html; charset=utf-8
      req.is('html') // => 'html'
      req.is('text/html') // => 'text/html'
      req.is('text/*') // => 'text/*'
      // when Content-Type is application/json
      req.is('json') // => 'json'
      req.is('application/json') // => 'application/json'
      req.is('application/*') // => 'application/*'
      req.is('html') // => false

      // --- req.range(size[, options])
      // range header parser
      // size parameter is the maximum size of the resource
      // options parameter is an object that can have the following properties
      // property "combine" - Boolean, specify if overlapping & adjacent ranges should be combined
      //   defaults to false, when true, ranges will be combined and returned
      // array of ranges will be returned or negative numbers indicating an error parsing
      // -2 signals a malformed header string
      // -1 signals an unsatisfiable range
      // parse header from request
      var range = req.range(1000)
      // the type of the range
      if (range.type === 'bytes') {
        range.forEach(function (r) { // the ranges
          // do something with r.start and r.end
        })
      }
    
response
--- properties

      // --- res.app
      // holds a reference to the instance of the Express application
      // res.app is identical to the req.app property in the request object

      // --- res.headersSent
      // if the app sent HTTP headers for the response
      app.get('/', function (req, res) {
        console.dir(res.headersSent) // false
        res.send('OK')
        console.dir(res.headersSent) // true
      })

      // --- res.locals
      // object that contains response local variables scoped to the request,
      // available only to the view(s) rendered during that request/response cycle (if any)
      // otherwise, this property is identical to app.locals
      // useful for exposing request-level information such as the request path name,
      // authenticated user, user settings, and so on
      app.use(function (req, res, next) {
        res.locals.user = req.user
        res.locals.authenticated = !req.user.anonymous
        next()
      })
    
--- methods

      // --- res.append(field [, value])
      // appends the specified value to the HTTP response header field
      // if header is not set, creates header with specified value (string or an array)
      // calling res.set() after res.append() will reset the previously-set header value
      res.append('Link', ['<http://localhost/>', ''<http://localhost:3000/>'])
      res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly')
      res.append('Warning', '199 Miscellaneous warning')

      // --- res.attachment([filename])
      // sets the HTTP response Content-Disposition header field to "attachment"
      // if a filename is given, then it sets the Content-Type based on the extension name
      // via res.type(), and sets the Content-Disposition "filename=" parameter
      // Content-Disposition: attachment
      res.attachment()
      // Content-Disposition: attachment; filename="logo.png" Content-Type: image/png
      res.attachment('path/to/logo.png')

      // --- res.cookie(name, value [, options])
      // sets cookie name to value, may be a string or object converted to JSON
      // options parameter is an object that can have the following properties:
      //   domain - domain name for the cookie, default: domain name of the app
      //   encode - synchronous function used for cookie value encoding, default: encodeURIComponent
      //   expires - expiry date of the cookie in GMT, if not specified or 0, creates a session cookie
      //   httpOnly - flags the cookie to be accessible only by the web server
      //   maxAge - setting the expiry time relative to the current time in milliseconds
      //   path - path for the cookie, default: "/"
      //   secure - marks the cookie to be used with HTTPS only
      //   signed - indicates if the cookie should be signed
      //   sameSite - value of the "SameSite" Set-Cookie attribute
      // sets the HTTP Set-Cookie header with the options provided.
      // any option not specified defaults to the value stated in RFC 6265
      res.cookie('name', 'tobi', { domain: '.example.com', path: '/admin', secure: true })
      res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true })
      // multiple cookies in a single response by calling res.cookie multiple times
      res
        .status(201)
        .cookie('access_token', 'Bearer ' + token, {
          expires: new Date(Date.now() + 8 * 3600000) // cookie will be removed after 8 hours
        })
        .cookie('test', 'test')
        .redirect(301, '/admin')
      // encode - choose the function used for cookie value encoding
      // does not support asynchronous functions.
      // set a domain-wide cookie for another site in your organization
      // this other site (not under your administrative control) does not use URI-encoded cookie values
      // Default encoding
      res.cookie(
        'some_cross_domain_cookie',
        'http://mysubdomain.example.com',
        { domain: 'example.com' }
      )
      // Result:
      // 'some_cross_domain_cookie=http%3A%2F%2Fmysubdomain.example.com; Domain=example.com; Path=/'
      // Custom encoding
      res.cookie(
        'some_cross_domain_cookie',
        'http://mysubdomain.example.com',
        { domain: 'example.com', encode: String }
      )
      // Result: 'some_cross_domain_cookie=http://mysubdomain.example.com; Domain=example.com; Path=/;'
      // maxAge - setting "expires" relative to the current time in milliseconds
      // equivalent to the second example above
      res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
      // pass an object as the value parameter;
      // it is then serialized as JSON and parsed by bodyParser() middleware
      res.cookie('cart', { items: [1, 2, 3] })
      res.cookie('cart', { items: [1, 2, 3] }, { maxAge: 900000 })
      // when using cookie-parser middleware, this method also supports signed cookies
      // include the signed option set to true
      // then res.cookie() will use the secret passed to cookieParser(secret) to sign the value
      res.cookie('name', 'tobi', { signed: true })
      // later you may access this value through the req.signedCookie object

      // --- res.clearCookie(name [, options])
      // clears the cookie specified by name
      // web browsers and other compliant clients will only clear the cookie if given options is identical
      // to those given to res.cookie(), excluding expires and maxAge
      res.cookie('name', 'tobi', { path: '/admin' })
      res.clearCookie('name', { path: '/admin' })

      // --- res.download(path [, filename] [, options] [, fn])
      // transfers the file at path as an "attachment"
      // browsers will prompt the user for download
      // by default, the Content-Disposition header "filename=" parameter is path
      // (this typically appears in the browser dialog), override this default with the filename parameter
      // when an error occurs or transfer is complete, method calls the optional callback function fn.
      // uses res.sendFile() to transfer the file
      // optional options argument passes to the underlying res.sendFile() call, takes the same parameters
      res.download('/report-12345.pdf')
      res.download('/report-12345.pdf', 'report.pdf')
      res.download('/report-12345.pdf', 'report.pdf', function (err) {
        if (err) {
          // Handle error, but keep in mind the response may be partially-sent
          // so check res.headersSent
        } else {
          // decrement a download credit, etc.
        }
      })

      // --- res.end([data] [, encoding])
      // ends the response process, from Node core, response.end() of http.ServerResponse
      // end the response without any data
      // use methods such as res.send() and res.json() to respond with data
      res.end()
      res.status(404).end()

      // --- res.format(object)
      // content-negotiation on the Accept HTTP header on the request object,
      // when present uses req.accepts() to select a handler for the request,
      // based on the acceptable types ordered by their quality values
      // if the header is not specified, the first callback is invoked
      // when no match is found, server responds with 406 "Not Acceptable", or invokes the default
      // Content-Type response header is set when a callback is selected
      // alter this within the callback using methods such as res.set() or res.type().
      // respond with { "message": "hey" } when the Accept header field is set to "application/json"
      // or "*/json" (however if it is "*/*", then the response will be "hey")
      res.format({
        'text/plain': function () { res.send('hey') },
        'text/html': function () { res.send('hey') },
        'application/json': function () { res.send({ message: 'hey' }) },
        'default': function () {
          res.status(406).send('Not Acceptable') // log the request and respond with 406
        }
      })
      // also use extension names mapped to these types for a slightly less verbose implementation
      // in addition to canonicalized MIME types
      res.format({
        text: function () { res.send('hey') },
        html: function () { res.send('hey') },
        json: function () { res.send({ message: 'hey' }) }
      })

      // --- res.get(field)
      // HTTP response header specified by field, match is case-insensitive
      res.get('Content-Type') // => "text/plain"

      // --- res.json([body])
      // sends a JSON response (with the correct content-type)
      // that is the parameter converted to a JSON string using JSON.stringify()
      // parameter can be any JSON type: object, array, string, Boolean, number, or null
      // use it to convert other values to JSON
      res.json(null)
      res.json({ user: 'tobi' })
      res.status(500).json({ error: 'message' })

      // --- res.jsonp([body])
      // sends a JSON response with JSONP support, identical to res.json()
      // except that it opts-in to JSONP callback support
      res.jsonp(null) // => callback(null)
      res.jsonp({ user: 'tobi' }) // => callback({ "user": "tobi" })
      res.status(500).jsonp({ error: 'message' }) // => callback({ "error": "message" })
      // default JSONP callback name is simply callback, override with jsonp callback name setting
      // ?callback=foo
      res.jsonp({ user: 'tobi' }) // => foo({ "user": "tobi" })
      app.set('jsonp callback name', 'cb')
      // ?cb=foo
      res.status(500).jsonp({ error: 'message' }) // => foo({ "error": "message" })

      // --- res.links(links)
      // joins the links provided as properties of the parameter
      // to populate the response Link HTTP header field
      res.links({
        next: 'http://api.example.com/users?page=2',
        last: 'http://api.example.com/users?page=5'
      })
      // yields the following results
      // Link: http://api.example.com/users?page=2; rel="next",
      //       http://api.example.com/users?page=5; rel="last"

      // --- res.location(path)
      // sets response Location HTTP header to the specified path parameter
      res.location('/foo/bar')
      res.location('http://example.com')
      res.location('back')
      // "back" refers to the URL specified in the Referer header or "/".
      // after encoding the URL, if not encoded already,
      // specified URL is passed to the browser in the Location header, with no validation.
      // browsers take the responsibility of deriving the intended URL from the current URL
      // or the referring URL, and the URL specified in the Location header;
      // and redirect the user accordingly

      // --- res.redirect([status,] path)
      // redirects to the URL derived from the specified path, with specified status,
      // HTTP status code, if not specified, status defaults to "302 Found"
      res.redirect('/foo/bar')
      res.redirect('http://example.com')
      res.redirect(301, 'http://example.com')
      res.redirect('../login')
      // be a fully-qualified URL for redirecting to a different site
      res.redirect('http://google.com')
      // can be relative to the root of the host name
      // if the application is on http://example.com/admin/post/new,
      // following would redirect to the URL http://example.com/admin
      res.redirect('/admin')
      // relative to the current URL.
      // from http://example.com/blog/admin/ (notice the trailing slash),
      // following would redirect to the URL http://example.com/blog/admin/post/new
      res.redirect('post/new')
      // redirecting to post/new from http://example.com/blog/admin (no trailing slash),
      // will redirect to http://example.com/blog/post/new.
      // think of path segments as directories (with trailing slashes) and files.
      // path-relative redirects are also possible
      // from http://example.com/admin/post/new, redirect to http://example.com/admin/post
      res.redirect('..')
      // back redirection redirects the request back to the referer,
      // defaulting to / when the referer is missing
      res.redirect('back')

      // --- res.render(view [, locals] [, callback])
      // renders a view and sends the rendered HTML string to client
      //    locals - object whose properties define local variables for the view
      //    callback - function, method returns both the possible error and rendered string,
      //               but does not perform an automated response
      //               when an error occurs, the method invokes next(err) internally
      //    view - string, file path of the view file to render,
      //    can be an absolute path, or a path relative to the views setting
      //    view engine setting determines file extension (and load module), if not defined in path.
      //    should not contain input from the end-user
      res.render('index')
      // if a callback is specified, the rendered HTML string has to be sent explicitly
      res.render('index', function (err, html) {
        res.send(html)
      })
      // pass a local variable to the view
      res.render('user', { name: 'Tobi' }, function (err, html) {
        // ...
      })

      // --- res.send([body])
      // sends the HTTP response
      // body parameter can be a Buffer object, String, object, Array
      res.send(Buffer.from('whoop'))
      res.send({ some: 'json' })
      res.send('<p>some html</p>')
      res.status(404).send('Sorry, we cannot find that!')
      res.status(500).send({ error: 'something blew up' })
      // method performs many useful tasks for simple non-streaming responses.
      // it automatically assigns Content-Length HTTP response header (unless previously defined)
      // and provides automatic HEAD and HTTP cache freshness support
      // when the parameter is a Buffer object, method sets the Content-Type response header field
      // to "application/octet-stream", unless previously defined as shown below
      res.set('Content-Type', 'text/html')
      res.send(Buffer.from('<p>some html</p>'))
      // when the parameter is a String, the method sets the Content-Type to "text/html"
      res.send('<p>some html</p>')
      // when the parameter is an Array or Object, Express responds with the JSON representation
      res.send({ user: 'tobi' })
      res.send([1, 2, 3])

      // --- res.sendFile(path [, options] [, fn])
      // https://github.com/pillarjs/send
      // transfers the file at the given path
      // sets the Content-Type response HTTP header field based on the filename extension
      // unless the root option is set in the options object, path must be an absolute path to the file.
      // this API provides access to data on the running file system
      // ensure that either (a) the way in which the path argument was constructed into an absolute path
      // is secure if it contains user input or (b) set the root option to the absolute path
      // of a directory to contain access within.
      // when the root option is provided, the path argument is allowed to be a relative path,
      // including containing ..
      // Express will validate as path and will resolve within the given root option
      // options:
      //   maxAge (0) - sets max-age property of Cache-Control header in milliseconds, or in ms format
      //   root - root directory for relative filenames
      //   lastModified (Enabled) - sets Last-Modified header to last modified date of the file on the OS
      //                            set false to disable it
      //   headers - object containing HTTP headers to serve with the file
      //   dotfiles - option for serving dotfiles. Possible values are "allow", "deny", "ignore"
      //   acceptRanges (true) - enable or disable accepting ranged requests
      //   cacheControl (true) - enable or disable setting Cache-Control response header
      //   immutable (false) - enable/disable the immutable directive in the Cache-Control response header
      //             if enabled, maxAge option should also be specified to enable caching
      //             immutable directive will prevent supported clients from making conditional requests
      //             during the life of the maxAge option to check if the file has changed
      // invokes the callback fn(err) when the transfer is complete or when an error occurs
      // if the callback is specified and an error occurs,
      // callback must explicitly handle the response process either by ending the request-response cycle,
      // or by passing control to the next route.
      // using res.sendFile with all its arguments
      app.get('/file/:name', function (req, res, next) {
        var options = {
          root: path.join(__dirname, 'public'),
          dotfiles: 'deny',
          headers: {
            'x-timestamp': Date.now(),
            'x-sent': true
          }
        }
        var fileName = req.params.name
        res.sendFile(fileName, options, function (err) {
          if (err) { next(err) }
          else { console.log('Sent:', fileName) }
        })
      })
      // using res.sendFile to provide fine-grained support for serving files
      app.get('/user/:uid/photos/:file', function (req, res) {
        var uid = req.params.uid
        var file = req.params.file
        req.user.mayViewFilesFrom(uid, function (yes) {
          if (yes) { res.sendFile('/uploads/' + uid + '/' + file) }
          else { res.status(403).send("Sorry! You can't see that.") }
        })
      })

      // --- res.sendStatus(statusCode)
      // sets response HTTP status code and send its string representation body
      res.sendStatus(200) // equivalent to res.status(200).send('OK')
      res.sendStatus(403) // equivalent to res.status(403).send('Forbidden')
      res.sendStatus(404) // equivalent to res.status(404).send('Not Found')
      res.sendStatus(500) // equivalent to res.status(500).send('Internal Server Error')
      // if an unsupported status code is specified,
      // HTTP status is still set to statusCode and the string version of the code
      // some versions of Node.js will throw
      // when res.statusCode is set to an invalid HTTP status code (outside of the range 100 to 599)
      res.sendStatus(9999) // equivalent to res.status(9999).send('9999')

      // --- res.set(field [, value])
      // sets the response HTTP header field to value
      // to set multiple fields at once, pass an object as the parameter
      res.set('Content-Type', 'text/plain')
      res.set({
        'Content-Type': 'text/plain',
        'Content-Length': '123',
        'ETag': '12345'
      })
      // aliased as res.header(field [, value])

      // --- res.status(code)
      // sets the HTTP status for the response
      // chainable alias of Node response.statusCode
      res.status(403).end()
      res.status(400).send('Bad Request')
      res.status(404).sendFile('/absolute/path/to/404.png')

      // --- res.type(type)
      // sets the Content-Type HTTP header to the MIME type
      // as determined by mime.lookup() for the specified type
      // if type contains the "/" character, then it sets the Content-Type to type
      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'

      // --- res.vary(field)
      // adds the field to the Vary response header, if it is not there already
      res.vary('User-Agent').render('docs')
    
router

      // invoked for any requests passed to this router
      router.use(function (req, res, next) {
        // .. some logic here .. like any other middleware
        next()
      })
      // will handle any request that ends in /events
      // depends on where the router is "use()'d"
      router.get('/events', function (req, res, next) {
        // ..
      })
      // then use a router for a particular root URL
      // separating your routes into files or even mini-apps
      // only requests to /calendar/* will be sent to our "router"
      app.use('/calendar', router)
    
--- all(path, [callback, ...] callback)

      // mapping "global" logic for specific path prefixes or arbitrary matches
      // if you placed the following route at the top of all other route definitions,
      // it would require that all routes from that point on
      // would require authentication, and automatically load a user.
      // these callbacks do not have to act as end points;
      // loadUser can perform a task, then call next() to continue matching subsequent routes
      router.all('*', requireAuthentication, loadUser)
      // the equivalent
      router.all('*', requireAuthentication)
      router.all('*', loadUser)
      //  white-listed "global" functionality, only restrict paths prefixed with "/api"
      router.all('/api/*', requireAuthentication)
    
--- METHOD(path, [callback, ...] callback)

      // provide the routing functionality in Express,
      // METHOD is one of the HTTP methods, such as GET, POST, and so on, in lowercase
      // actual methods are router.get(), router.post(), router.put(), and so on.
      // router.get() automatically called for the HTTP HEAD method in addition
      // to the GET method if router.head() was not called for the path before router.get().
      // provide multiple callbacks, and all are treated equally,
      // and behave just like middleware, except that these callbacks may invoke next('route')
      // to bypass the remaining route callback(s)
      // use this mechanism to perform pre-conditions on a route then pass control
      // to subsequent routes when there is no reason to proceed with the route matched

      // most simple route definition possible
      // Express translates the path strings to regular expressions,
      // used internally to match incoming requests.
      // query strings are not considered when performing these matches,
      // for example "GET /" would match the following route, as would "GET /?name=tobi"
      router.get('/', function (req, res) {
        res.send('hello world')
      })
      // also use regular expressions
      // following would match "GET /commits/71dbb9c" and "GET /commits/71dbb9c..4c084f9"
      router.get(/^\/commits\/(\w+)(?:\.\.(\w+))?$/, function (req, res) {
        var from = req.params[0]
        var to = req.params[1] || 'HEAD'
        res.send('commit range ' + from + '..' + to)
      })
    
--- param(name, callback)

      // adds callback triggers to route parameters, where name
      // is the name of the parameter and callback is the callback function.
      // name is technically optional, using this method without it is deprecated since v4.11.0
      // parameters of the callback function are
      //   req, the request object
      //   res, the response object
      //   next, indicating the next middleware function
      //   value of the name parameter
      //   name of the parameter
      // does not accept an array of route parameters

      // when :user is present in a route path, map user loading logic
      // to automatically provide req.user to the route, or perform validations input
      router.param('user', function (req, res, next, id) {
        // try to get the user details from the User model and attach it to the request object
        User.find(id, function (err, user) {
          if (err) {
            next(err)
          } else if (user) {
            req.user = user
            next()
          } else {
            next(new Error('failed to load user'))
          }
        })
      })

      // param callback functions are local to the router on which they are defined
      // they are not inherited by mounted apps or routers
      // will be triggered only by route parameters defined on router routes
      // will be called only once in a request-response cycle,
      // even if the parameter is matched in multiple routes
      router.param('id', function (req, res, next, id) {
        console.log('CALLED ONLY ONCE')
        next()
      })
      router.get('/user/:id', function (req, res, next) {
        console.log('although this matches')
        next()
      })
      router.get('/user/:id', function (req, res) {
        console.log('and this matches too')
        res.end()
      })
      // GET /user/42
      //   CALLED ONLY ONCE
      //   although this matches
      //   and this matches too
    
--- route(path)

      // returns an instance of a single route,
      // then use to handle HTTP verbs with optional middleware
      // use to avoid duplicate route naming and thus typing errors.
      // re-use single /users/:user_id path and add handlers for various HTTP methods
      var router = express.Router()
      router.param('user_id', function (req, res, next, id) {
        // sample user, would actually fetch from DB, etc...
        req.user = {
          id: id,
          name: 'TJ'
        }
        next()
      })
      router.route('/users/:user_id')
      .all(function (req, res, next) {
        // runs for all HTTP verbs first
        // think of it as route specific middleware!
        next()
      })
      .get(function (req, res, next) {
        res.json(req.user)
      })
      .put(function (req, res, next) {
        // just an example of maybe updating the user
        req.user.name = req.params.name
        // save user ... etc
        res.json(req.user)
      })
      .post(function (req, res, next) {
        next(new Error('not implemented'))
      })
      .delete(function (req, res, next) {
        next(new Error('not implemented'))
      })
      // when you use router.route(), middleware ordering is based on when the route is created,
      // not when method handlers are added to the route
      // consider method handlers to belong to the route to which they were added
    
--- use([path], [function, ...] function)

      // uses the specified middleware function or functions,
      // with optional mount path path, that defaults to "/"
      // similar to app.use()
      // requests start at the first middleware function defined
      // and work their way "down" the middleware stack processing for each path they match
      var express = require('express')
      var app = express()
      var router = express.Router()
      // simple logger for this router requests
      // all requests to this router will first hit this middleware
      router.use(function (req, res, next) {
        console.log('%s %s %s', req.method, req.url, req.path)
        next()
      })
      // this will only be invoked if the path starts with /bar from the mount point
      router.use('/bar', function (req, res, next) {
        // ... maybe some additional /bar logging ...
        next()
      })
      // always invoked
      router.use(function (req, res, next) {
        res.send('Hello World')
      })
      app.use('/foo', router)
      app.listen(3000)

      // "mount" path is stripped and is not visible to the middleware function
      // main effect of this feature is that a mounted middleware function
      // may operate without code changes regardless of its "prefix" pathname.
      // order in which you define middleware with router.use() is very important
      // they are invoked sequentially, thus the order defines middleware precedence.
      // usually a logger is the very first middleware, so that every request gets logged
      var logger = require('morgan')
      var path = require('path')
      router.use(logger())
      router.use(express.static(path.join(__dirname, 'public')))
      router.use(function (req, res) {
        res.send('Hello')
      })

      // ignore logging requests for static files,
      // and continue logging routes and middleware defined after logger().
      // move the call to express.static() to the top, before adding the logger middleware:
      router.use(express.static(path.join(__dirname, 'public')))
      router.use(logger())
      router.use(function (req, res) {
        res.send('Hello')
      })

      // serving files from multiple directories, giving precedence to "./public"
      router.use(express.static(path.join(__dirname, 'public')))
      router.use(express.static(path.join(__dirname, 'files')))
      router.use(express.static(path.join(__dirname, 'uploads')))

      // use() method also supports named parameters
      // mount points for other routers can benefit from preloading using named parameters.
      // although these middleware functions are added via a particular router,
      // when they run is defined by the path they are attached to (not the router)
      // therefore, middleware added via one router may run for other routers if its routes match.
      // this code shows two different routers mounted on the same path:
      var authRouter = express.Router()
      var openRouter = express.Router()
      authRouter.use(require('./authenticate').basic(usersdb))
      authRouter.get('/:user_id/edit', function (req, res, next) {
        // ... Edit user UI ...
      })
      openRouter.get('/', function (req, res, next) {
        // ... List users ...
      })
      openRouter.get('/:user_id', function (req, res, next) {
        // ... View user ...
      })
      app.use('/users', authRouter)
      app.use('/users', openRouter)

      // even though the authentication middleware was added via the authRouter
      // it will run on the routes defined by the openRouter
      // as well since both routers were mounted on /users
      // to avoid this behavior, use different paths for each router
    

Security


    // using Helmet
    var helmet = require('helmet')
    app.use(helmet())

    // turn off the header
    app.disable('x-powered-by')

    // using express-session
    var session = require('express-session')
    app.set('trust proxy', 1) // trust first proxy
    app.use(session({
      secret: 's3Cur3',
      name: 'sessionId'
    }))

    // using cookie-session middleware
    var session = require('cookie-session')
    var express = require('express')
    var app = express()
    var expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour
    app.use(session({
      name: 'session',
      keys: ['key1', 'key2'],
      cookie: {
        secure: true,
        httpOnly: true,
        domain: 'example.com',
        path: 'foo/bar',
        expires: expiryDate
      }
    }))

    // examples of Evil Regular Expressions patterns
    (a+)+
    ([a-zA-Z]+)*
    (a|aa)+
    // potential input that can cause  first regular expression to run slowly
    // aaaaaaaaaaaaaaaaaaaaaaaaX
  

Performance

things to do in your code (the dev part)


    // use gzip compression
    var compression = require('compression')
    var express = require('express')
    var app = express()
    app.use(compression())

    // use try-catch
    app.get('/search', function (req, res) {
      // Simulating async operation
      setImmediate(function () {
        var jsonStr = req.query.params
        try {
          var jsonObj = JSON.parse(jsonStr)
          res.send('Success')
        } catch (e) {
          res.status(400).send('Invalid JSON string')
        }
      })
    })
    // use promises
    app.get('/', function (req, res, next) {
      // do some sync stuff
      queryDb()
        .then(function (data) {
          // handle data
          return makeCsv(data)
        })
        .then(function (csv) {
          // handle csv
        })
        .catch(next)
    })
    app.use(function (err, req, res, next) {
      // handle error
    })
    // catche rejected promises and calls next() with the error as the first argument
    const wrap = fn => (...args) => fn(...args).catch(args[2])
    app.get('/', wrap(async (req, res, next) => {
      let company = await getCompanyById(req.query.id)
      let stream = getLogoStreamById(company.id)
      stre
  

things to do in your environment/setup (the ops part)

Systemd - Linux system and service manager, adopted as default init system


    [Unit]
    Description=Awesome Express App

    [Service]
    Type=simple
    ExecStart=/usr/local/bin/node /projects/myapp/index.js
    WorkingDirectory=/projects/myapp

    User=nobody
    Group=nogroup

    # Environment variables:
    Environment=NODE_ENV=production

    # Allow many incoming connections
    LimitNOFILE=infinity

    # Allow core dumps for debugging
    LimitCORE=infinity

    StandardInput=null
    StandardOutput=syslog
    StandardError=syslog
    Restart=always

    [Install]
    WantedBy=multi-user.target
  

StrongLoop PM as a systemd service


    # install StrongLoop PM as a systemd service
    sudo sl-pm-install --systemd
    # then start the service with
    sudo /usr/bin/systemctl start strong-pm
  

Upstart - starting tasks and services during system startup


    # Upstart service is defined in a job configuration file (also called a “job”)
    # with filename ending in .conf
    # following example shows how to create a job called "myapp" for an app named "myapp"
    # with the main file located at /projects/myapp/index.js.
    # create a file named myapp.conf at /etc/init/ with the following content
    # (replace the bold text with values for your system and app)

    # When to start the process
    start on runlevel [2345]
    # When to stop the process
    stop on runlevel [016]
    # Increase file descriptor limit to be able to handle more requests
    limit nofile 50000 50000
    # Use production mode
    env NODE_ENV=production
    # Run as www-data
    setuid www-data
    setgid www-data
    # Run from inside the app dir
    chdir /projects/myapp
    # The process to start
    exec /usr/local/bin/node /projects/myapp/index.js
    # Restart the process if it is down
    respawn
    # Limit restart attempt to 10 times within 10 seconds
    respawn limit 10 10

    # use these commands
    start myapp # start the app
    restart myapp # restart the app
    stop myapp # stop the app
  

StrongLoop PM as an Upstart service


    # install StrongLoop Process Manager as an Upstart service.
    # then, when the server restarts, it will automatically restart StrongLoop PM,
    # which will then restart all the apps it is managing.
    # install StrongLoop PM as an Upstart 1.4 service
    sudo sl-pm-install
    # then run the service with
    sudo /sbin/initctl start strong-pm
  

Health/Shutdown


    // --- Terminus, basic template
    const http = require('http')
    const express = require('express')
    const { createTerminus } = require('@godaddy/terminus')
    const app = express()
    app.get('/', (req, res) => {
      res.send('ok')
    })
    const server = http.createServer(app)
    function onSignal () {
      console.log('server is starting cleanup')
      // start cleanup of resource, like databases or file descriptors
    }
    async function onHealthCheck () {
      // checks if the system is healthy, like the db connection is live
      // resolves, if health, rejects if not
    }
    createTerminus(server, {
      signal: 'SIGINT',
      healthChecks: { '/healthcheck': onHealthCheck },
      onSignal
    })
    server.listen(3000)

    // --- Lightship, basic template
    const http = require('http')
    const express = require('express')
    const {
      createLightship
    } = require('lightship')
    // Lightship will start a HTTP service on port 9000.
    const lightship = createLightship()
    const app = express()
    app.get('/', (req, res) => {
      res.send('ok')
    })
    app.listen(3000, () => {
      lightship.signalReady()
    })
    // you can signal that the service is not ready using "lightship.signalNotReady()"
  

Tools/Utility

async

native async

    const awaitHandlerFactory = (middleware) => {
      return async (req, res, next) => {
        try {
          await middleware(req, res, next)
        } catch (err) {
          next(err)
        }
      }
    }
    // and use it this way:
    app.get('/', awaitHandlerFactory(async (request, response) => {
      const result = await getContent()
      response.send(result)
    }))

    // --- parallel execution
    async function main () {
      const [user, product] = await Promise.all([
        Users.fetch(userId),
        Products.fetch(productId)
      ])
      await makePurchase(user, product)
    }
    // Promise.race - result of the fastest resolving Promise
  
async.parallel() - run multiple asynchronous operations in parallel

    import parallel from 'async/parallel';

    async.parallel({
        one: function(callback) { /* */ },
        two: function(callback) { /* */ },
        // ...
        something_else: function(callback) { /* */ }
      },
      // optional callback
      function(err, results) {
        // 'results' is now equal to: {one: 1, two: 2, ..., something_else: some_value}
      }
    );

    async.parallel([
      function(callback) {
        setTimeout(function() {
          callback(null, 'one');
        }, 200);
      },
      function(callback) {
        setTimeout(function() {
          callback(null, 'two');
        }, 100);
      }
    ],
    // optional callback
    function(err, results) {
      // the results array will equal ['one','two'] even though
      // the second function had a shorter timeout.
    });
    // an example using an object instead of an array
    async.parallel({
      one: function(callback) {
        setTimeout(function() {
          callback(null, 1);
        }, 200);
      },
      two: function(callback) {
        setTimeout(function() {
          callback(null, 2);
        }, 100);
      }
    }, function(err, results) {
      // results is now equals to: {one: 1, two: 2}
    });
  
async.series() - multiple asynchronous operations in sequence

    // when subsequent functions do not depend on the output of earlier functions
    // essentially declared and behaves in the same way as async.parallel()
    import series from 'async/series';

    async.series({
        one: function(callback) { /* */ },
        two: function(callback) { /* */ },
        // ...
        something_else: function(callback) { /* */ }
      },
      // optional callback after the last asynchronous function completes.
      function(err, results) {
        // 'results' is now equals to: {one: 1, two: 2, ..., something_else: some_value}
      }
    );

    async.series([
      function(callback) {
        // do some stuff ...
        callback(null, 'one');
      },
      function(callback) {
        // do some more stuff ...
        callback(null, 'two');
      }
    ],
    // optional callback
    function(err, results) {
      // results is now equal to ['one', 'two']
    });
    async.series({
      one: function(callback) {
        setTimeout(function() {
          callback(null, 1);
        }, 200);
      },
      two: function(callback){
        setTimeout(function() {
          callback(null, 2);
        }, 100);
      }
    }, function(err, results) {
      // results is now equal to: {one: 1, two: 2}
    });
  
async.waterfall() - multiple asynchronous operations in sequence

    // when each operation is dependent on the result of the previous operation
    import waterfall from 'async/waterfall';

    async.waterfall([
      function(callback) {
        // null for the first argument and results in subsequent arguments
        callback(null, 'one', 'two');
      },
      function(arg1, arg2, callback) {
        // arg1 now equals 'one' and arg2 now equals 'two'
        callback(null, 'three');
      },
      function(arg1, callback) {
        // arg1 now equals 'three'
        callback(null, 'done');
      }
    ], function (err, result) {
      // result now equals 'done'
    });

    async.waterfall([
      function(callback) {
        callback(null, 'one', 'two');
      },
      function(arg1, arg2, callback) {
        // arg1 now equals 'one' and arg2 now equals 'two'
        callback(null, 'three');
      },
      function(arg1, callback) {
        // arg1 now equals 'three'
        callback(null, 'done');
      }
    ], function (err, result) {
      // result now equals 'done'
    });
    // or, with named functions:
    async.waterfall([
      myFirstFunction,
      mySecondFunction,
      myLastFunction,
    ], function (err, result) {
      // result now equals 'done'
    });
    function myFirstFunction(callback) {
      callback(null, 'one', 'two');
    }
    function mySecondFunction(arg1, arg2, callback) {
      // arg1 now equals 'one' and arg2 now equals 'two'
      callback(null, 'three');
    }
    function myLastFunction(arg1, callback) {
      // arg1 now equals 'three'
      callback(null, 'done');
    }
  

Microservice


    // --- heroes.js

    const express = require('express');
    const path = require('path');
    const bodyParser = require('body-parser');
    const port = process.argv.slice(2)[0];
    const app = express();
    app.use(bodyParser.json());
    const powers = [
      { id: 1, name: 'flying' },
      { id: 2, name: 'teleporting' },
      { id: 3, name: 'super strength' },
      { id: 4, name: 'clairvoyance'},
      { id: 5, name: 'mind reading' }
    ];
    const heroes = [
      {
        id: 1,
        type: 'spider-dog',
        displayName: 'Cooper',
        powers: [1, 4],
        img: 'cooper.jpg',
        busy: false
      },
      {
        id: 2,
        type: 'flying-dogs',
        displayName: 'Jack & Buddy',
        powers: [2, 5],
        img: 'jack_buddy.jpg',
        busy: false
      },
      {
        id: 3,
        type: 'dark-light-side',
        displayName: 'Max & Charlie',
        powers: [3, 2],
        img: 'max_charlie.jpg',
        busy: false
      },
      {
        id: 4,
        type: 'captain-dog',
        displayName: 'Rocky',
        powers: [1, 5],
        img: 'rocky.jpg',
        busy: false
      }
    ];
    app.get('/heroes', (req, res) => {
      console.log('Returning heroes list');
      res.send(heroes);
    });
    app.get('/powers', (req, res) => {
      console.log('Returning powers list');
      res.send(powers);
    });
    app.post('/hero/**', (req, res) => {
      const heroId = parseInt(req.params[0]);
      const foundHero = heroes.find(subject => subject.id === heroId);
      if (foundHero) {
          for (let attribute in foundHero) {
              if (req.body[attribute]) {
                  foundHero[attribute] = req.body[attribute];
                  console.log("Set ${attribute} to ${req.body[attribute]} in hero: ${heroId}");
              }
          }
          res.status(202).header({Location: "http://localhost:${port}/hero/${foundHero.id}"}).send(foundHero);
      } else {
          console.log("Hero not found.");
          res.status(404).send();
      }
    });
    app.use('/img', express.static(path.join(__dirname,'img')));
    console.log("Heroes service listening on port ${port}");
    app.listen(port);

    // --- threats.js

    const express = require('express');
    const path = require('path');
    const bodyParser = require('body-parser');
    const request = require('request');
    const port = process.argv.slice(2)[0];
    const app = express();
    app.use(bodyParser.json());
    const heroesService = 'http://localhost:8081';
    const threats = [
      {
        id: 1,
        displayName: 'Pisa tower is about to collapse.',
        necessaryPowers: ['flying'],
        img: 'tower.jpg',
        assignedHero: 0
      },
      {
        id: 2,
        displayName: 'Engineer is going to clean up server-room.',
        necessaryPowers: ['teleporting'],
        img: 'mess.jpg',
        assignedHero: 0
      },
      {
        id: 3,
        displayName: 'John will not understand the joke',
        necessaryPowers: ['clairvoyance'],
        img: 'joke.jpg',
        assignedHero: 0
      }
    ];
    app.get('/threats', (req, res) => {
      console.log('Returning threats list');
      res.send(threats);
    });
    app.post('/assignment', (req, res) => {
      request.post({
        headers: {'content-type': 'application/json'},
        url: "${heroesService}/hero/${req.body.heroId}",
        body: "{
          "busy": true
        }"
      }, (err, heroResponse, body) => {
        if (!err) {
          const threatId = parseInt(req.body.threatId);
          const threat = threats.find(subject => subject.id === threatId);
          threat.assignedHero = req.body.heroId;
          res.status(202).send(threat);
        } else {
          res.status(400).send({problem: "Hero Service responded with issue ${err}"});
        }
      });
    });
    app.use('/img', express.static(path.join(__dirname,'img')));
    console.log("Threats service listening on port ${port}");
    app.listen(port);
  

with MongoClient


    // --- heroes.js

    const express = require('express');
    const path = require('path');
    const bodyParser = require('body-parser');
    const MongoClient = require('mongodb').MongoClient;
    const port = process.argv.slice(2)[0];
    const app = express();
    app.use(bodyParser.json());
    const dbUrl = 'Your connection string URL goes here.';
    const dbClient = new MongoClient(dbUrl, { useNewUrlParser: true});
    dbClient.connect( err => {
      if (err) throw err;
    });
    function retrieveFromDb(collectionName) {
      return new Promise(resolve => {
        dbClient.db('test')
        .collection(collectionName)
        .find({})
        .project({_id: 0})
        .toArray((err, objects) => {
          resolve(objects);
        });
      });
    }
    app.get('/heroes', (req, res) => {
      console.log('Heroes v2: Returning heroes list.');
      retrieveFromDb('heroes').then(heroes => res.send(heroes));
    });
    app.get('/powers', (req, res) => {
      console.log('Heroes v2: Returning powers list.');
      retrieveFromDb('powers').then(heroes => res.send(heroes));
    });
    app.post('/hero/**', (req, res) => {
      const heroId = parseInt(req.params[0]);
      console.log('Heroes v2: Updating hero: ' + heroId);
      const heroCollection = dbClient.db('test').collection('heroes');
      heroCollection.find({}).project({_id: 0}).toArray((err, heroes) => {
        const foundHero = heroes.find(subject => subject.id === heroId);
        if (foundHero) {
          for (let attribute in foundHero) {
            if (req.body[attribute]) {
              foundHero[attribute] = req.body[attribute];
              heroCollection.updateOne({id: heroId }, {$set: req.body});
              console.log(
                "Set ${attribute} to ${req.body[attribute]} in hero: ${heroId}"
              );
            }
          }
          res.status(202).header({
            Location: "http://localhost:8080/hero-service/hero/${foundHero.id}"
          }).send(foundHero);
        } else {
          console.log("Hero not found.");
          res.status(404).send('Hero not found.');
        }
      });
    });
    app.use('/img', express.static(path.join(__dirname,'img')));
    require('../eureka-helper/eureka-helper').registerWithEureka('heroes-service', port);
    console.log("Heroes service listening on port ${port}.");
    app.listen(port);

    // --- threats.js

    const express = require('express');
    const path = require('path');
    const bodyParser = require('body-parser');
    const request = require('request');
    const MongoClient = require('mongodb').MongoClient;
    const port = process.argv.slice(2)[0];
    const app = express();
    app.use(bodyParser.json());
    const heroesService = 'http://localhost:8080/heroes-service';
    const dbUrl = 'Your connection string URL goes here.';
    const dbClient = new MongoClient(dbUrl, { useNewUrlParser: true});
    dbClient.connect( err => {
      if (err) throw err;
    });
    app.get('/threats', (req, res) => {
      console.log('Threats v2: Returns threats list.');
      dbClient.db('test')
      .collection('threats')
      .find({})
      .project({_id: 0})
      .toArray((err, objects) => {
        res.send(objects);
      });
    });
    app.post('/assignment', (req, res) => {
      console.log('Threats v2: Assigning hero.');
      const threatsCollection = dbClient.db('test').collection('threats');
      request.post({
        headers: {'content-type': 'application/json'},
        url: "${heroesService}/hero/${req.body.heroId}",
        body: "{
          "busy": true
        }"
      }, (err, heroResponse, body) => {
        if (!err && heroResponse.statusCode === 202) {
          const threatId = parseInt(req.body.threatId);
          threatsCollection.find({}).project({_id: 0}).toArray((err, threats) => {
            const threat = threats.find(subject => subject.id === threatId);
            if (threat) {
              console.log('Threats v2: Updating threat.');
              threat.assignedHero = req.body.heroId;
              threatsCollection.updateOne({
                id: threat.id }, {$set: {assignedHero: threat.assignedHero}
              });
              res.status(202).send(threat);
            } else {
              console.log('Threats v2: Threat not found.');
              res.status(404).send('Threat not found.');
            }
          });
        } else {
          if (err) res.status(400).send({problem: "Hero Service responded with issue ${err}."});
          if (heroResponse.statusCode != 202) {
            res.status(heroResponse.statusCode).send(heroResponse.body);
          }
        }
      });
    });
    app.use('/img', express.static(path.join(__dirname,'img')));
    require('../eureka-helper/eureka-helper').registerWithEureka('threats-service', port);
    console.log("Threats service listening on port ${port}.");
    app.listen(port);
  

Back to Main Page