Express Web Framework for Nodejs

Express Web Framework for Node.js

Prerequisites: Node.js

https://expressjs.com/en/starter/installing.html

After node is installed, run npm init -y

It will set-up a basic package.json for you.

Then run npm i express

const express = require('express')
const app = express()

Just by calling the express function, we create an application which allows us to set-up our entire server.

To make the server actually run this. You pass in a port number.

app.listen(3000)

The next step is to set-up routes.

The first parameter is a path ("/") and the second parameter is a function. The function takes three different parameters: req, res and next.

app.get('/', (req, res, next))

99% of the time, for get requests, we don’t care about ’next’. So you can remove that.

app.get('/', (req, res) => {
  console.log('Here')
  res.status(200).send('Hi')
})

“send” basically whatever we pass into it to the user.

We can chain HTTP statuses with other things that we want to send to the user.

To send a JSON to the user, res.status(200).json({ message: 'Hi'}) or res.json({ message: 'Hi'})

To pass a file to the user to download, do res.download('pathToTheFile')

To pass a file to the user to render, do res.render('pathToTheFile')

Router

https://expressjs.com/en/guide/routing.html

If we have hundreds of different routes defined in server.js, this file will become huge and difficult to maintain. The solution to this problem is a router.

A router is a way for you to create another instance of your app (application) that is its own mini application that has its own logic applied to it. And you can just insert it into the main application.

const express = require('express')
const router = express.Router()

router.get('/users', (req, res) => {
  res.send("user list")
})

router.get('/users/new', (req, res) => {
  res.status("new user form")
})

Router works exactly the same way as the ‘app’.

A nice about the router is that you can nest it inside a parent route. So the above code block can be changed to this:

const express = require('express')
const router = express.Router()

router.get('/, (req, res) => {
  res.send("user list")
})

router.get('/new', (req, res) => {
  res.status("new user form")
})

module.exports = router

Export the router from the routes file and import it in server.js

app.get('/', (req, res) => {
  console.log('Here')
  res.status(200).send('Hi')
})

const userRouter = require('./routes/users')

app.use('/users', userRouter)

To link the routes defined in users.js to the main app, we need to use app.use

This will help keep the server.js file clean.

Cleaning up the routes

In users.js,


app.post('/', (req, res) => {
  res.send("User created")
})

app.get('/:id', (req, res) => {
  res.send(`Get User with ID ${req.params.id}`)
})

app.put('/:id', (req, res) => {
  res.send(`Update User with ID ${req.params.id}`)
})

app.delete('/:id', (req, res) => {
  res.send(`Delete User with ID ${req.params.id}`)
})

This is to make the get operation work with dynamic values that can be passed in the url.

To access them in the route, we need to use req.params.id

Static routes always need to go above dynamic routes in the list of routes.

** router.route

You can chain repetitive routes using router.route. You can define the route only in one location and chain different request paths on to it.


router
  .route('/:id')
    .get((req, res) => {
      res.send(`Get User with ID ${req.params.id}`)
    })
    .put((req, res) => {
      res.send(`Update User with ID ${req.params.id}`)
    })
    .delete((req, res) => {
      res.send(`Delete User with ID ${req.params.id}`)
    })
    .post((req, res) => {
      res.send("User created")
    })

Route parameters

This function is going to run anytime it finds a param that matches the name you pass in.

We need to call the next() function inside of it. Otherwise, it will not run anything else after running the code inside of it.

router.param('id', (req, res, next, id) => {
  console.log(id)
  next()
})

param is a type of middleware.

next

The way it works is, when I call the next() function, run the next thing in line.

Call next() to move on to the next middleware, allowing for stacking and fall-backs.

As soon as the function that next() calls is done running, it will get back to that specific point and the next lines will be executed.

If you do not want that to happen, you need to do something like this. If you don’t put the return statement there, No auth will always be run - which is not what we want when req.query.admin is true.

function auth(req, res, next) {
  if (req.query.admin === 'true') {
    req.admin = true
    next()
    return
  }
  res.send('No auth')
}

middleware

https://expressjs.com/en/guide/writing-middleware.html

https://expressjs.com/en/guide/using-middleware.html

Middleware in express js is code that runs between when a request is sent to the server and an actual response being returned to the user.

The code inside of the get, delete, put and post operations for the user is run before a response that will be sent to the user. So, it is middleware.

So, from this block,

app.get('/', logger, (req, res) => {
  console.log('Here')
  res.status(200).send('Hi')
})

this is middleware:

(req, res) => {
  console.log('Here')
  res.status(200).send('Hi')
})

But typically, when thinking about middleware, we don’t think about the regular code that is supposed to be run for each specific route. Instead, we think about other functions (like loggers) when thinking about middleware.

Middleware functions help by saving us from writing a ton of code inside of each route to implement the same functionality. Write code in one single place and use it wherever it is needed.

One common example of this is to log out something.

function  logger(req, res, next) {
  console.log(req.originalUrl)
  next()
}

If you want the entire application to use the middleware function, in server.js, you can use this by saying, app.use(logger)

If you want only a router to use the middleware function, in the router, use this: router.use(loggerMiddleware)

Similar to other routes, if you need the middleware to be used by all the routes in a file, you need to put it at the top. Functions defined before the .use(logger) line will not be able to use the middleware function because they do not know about it.

There can be scanarios where you do not want to use a middleware function everywhere. You want to use it only on specific functions.

e.g. if you want logger to be used only by app.get, you do it like this:

app.get('/', logger, (req, res) => {
  console.log('Here')
  res.status(200).send('Hi')
})

When you do this, it will run the logger function first and then do whatever is defined in app.get

You can pass multiple middleware functions into app.get, like this:

app.get('/', logger, logger, logger, (req, res) => {
  console.log('Here')
  res.status(200).send('Hi')
})

In this case, it will run that middleware function three times. If you pass three different middleware functions into app.get, all three of them will be run in sequence.


Links to this note