# ExpressJS

## Introduction

![](/files/lDQQkQruhsMMf2SmKhXT)

* It is a framework that allows user to focus on developing business logic instead of typing boilerplate code for defining route and handling request and response
* It is all about middleware that the incoming request will go through all the funnels and then send the response back to client side
* The default query string parser is using `qs` library

```javascript
const express = require("express");
const path = require("path");
const app = express();

// define middleware to parse body of request

// usally used for rest api, as the content-type is mainly application/json
// for parsing the request body as json
app.use(express.json());

// used to parse form data, as the content type is mainly 
// application/x-www-form-urlencoded
app.use(express.urlencoded({extended: false});

// override the query parser setting, the default value is "extended"
// however, the default depth of qs is 5, so needed to be increased when needed
app.set('query parser', (queryString) => {
    const qs = require('qs');
    return qs.parse(queryString, {
      depth: 100, // Increase depth limit from default 5
      arrayLimit: 100, // Optional: increase array items limit
      parameterLimit: 2000, // Optional: increase parameter count limit
    });
  });

// server static file, such as image
app.use(express.static(path.join("__dirname","public")));
// defining middleware globally
app.use((req,res,next)=>{
  console.log("middleware 1");
  // go to next middleware
  next();
});

app.use((req,res,next)=>{
  console.log("middleware 2");
  next();
});

// only applicable to "/halo" path
app.use("/halo",(req,res,next)=>{
  console.log("halo");
});

// only applicable to "/halo" path
app.use("/halo/:id",(req,res,next)=>{
  console.log("halo",req.params.id);
});

app.use("/halo",(req,res,next)=>{
  console.log("halo");
});

// only applicable for post method and path "/post"
app.post("/post", (req,res,next)=>{
  console.log(req.body);
});

// The code behind is to create http server, ...
app.listen(8080, function () { 
  console.log("Server Start "); 
});
```

## Separating Routes

* Express Router allows to divide paths into multiple files

```javascript
// app.js

// Filtering the path to go into middleware
app.use("/auth", require("./routes/auth"));
app.use("/user", require("./routes/user"));

app.use((req,res)=>{
    res.send("Page not found");
});
```

```javascript
// routes/user.js
const express = require("express");
const router = express.Router();

// only get methods and exact match to /user/test is applicable to this middleware
router.get("/test", function (req, res, next) {
  res.send("test");
});

module.exports = router;
```

## Error Handling

```javascript
// Define error middleware
app.use((error, req , res , next) =>{
        console.log(error.StatusCode);
        // ...
})

app.get("/test",(req,res,next)=>{
    try{
        // ...
    } catch (err){
        // automatically go to error middleware
        const error = new Error(err);
        error.StatusCode = 500;
        return next(error);
    }
})
```

## Template Engine

![](/files/cIfu7TbIQVrltjH3dUmj)

![](/files/DLkIueZqJCP6WrkBdgz1)

* The template engine is used to understand the certain syntax, and scan the  html-ish template and then replace the placeholder from server
* Finally, it will generate the html file&#x20;

```javascript

// Define which template template is using
app.set('view engine','ejs');
// Define the directory of template file, default: view
app.set('views','views');


// Automatically set the data to the view for every request
app.use(function(req,res,next){
     res.locals.success_msg = "...";
     res.locals.user_msg = "...";
     next();
});

app.get("/test", (req,res,next)=>{
   // output as test.html from test.ejs in view file
   res.render('test', {
        layout: null,
        username_msg: forgotusername
   }
});
```

```ejs
<body>
  <center>
    <h1 style="font-size:40px">Hi!! <%=username_msg%></h1>
    <h2 style="font-size:30px">Reset Password</h2>
    Here is the link to reset your password:
    <a href="http://127.0.0.1:5000/users/resetpw?username=<%=username_msg%>&email=<%=email_msg%>">Reset</a>
  </center>
</body>
```

## Session & Cookie

```javascript
const session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session);
const app = express();
const MONGODB_URI =
  'mongodb+srv://maximilian:9u4biljMQc4jjqbe@cluster0-ntrwp.mongodb.net/shop';
  
// Define the collection that store sessions  
const store = new MongoDBStore({
  uri: MONGODB_URI,
  collection: 'sessions'
});

// Define the session middleware
// Automatically parse the related cookie and find the related session information
// Automatically generate cookie, if the related cookie doesn't existed
app.use(
  session({
   // used to encrypt the session id, 
   // to make sure that only server can decrypt the id
    secret: 'my secret',
    // Prevent update session on each request
    resave: false,
    saveUninitialized: false,
    // Define the source of session storage, default is to using memory
    store: store,
    // The related cookies setting
    cookies:{
     // ...
    }
  })
);

// Login
// Set the session information and save into document
req.session.isLoggedIn = true;
req.session.user = user;
req.session.save(err => {
  console.log(err);
  res.redirect('/');
});

// Logout
// Delete the session from the storage
// Even the cookie contains session id, the related session will not be found
req.session.destroy(err => {
    console.log(err);
    res.redirect('/');
 });


```

![](/files/8MEE8EMyBfCSbHwoS057)

## Flash message

* In order to provide user feedback while redirecting the response, flash message will be used
* The message will be stored into session temporarily, when message has been used, the message will be pop up from the session

```javascript
const flash = require('connect-flash');

app.get('/logout', function (req, res) {
  // add the message into storage
  req.flash('success_msg', 'You are successfully logged out');
  res.redirect('/users/login');
});

app.get("/login", (req,res) =>{ 
  //  get back the message from session and pop out
  let message = req.flash('error');
  if (message.length > 0) {
    message = message[0];
  } else {
    message = null;
  }
  res.render('auth/login', {
    path: '/login',
    pageTitle: 'Login',
    errorMessage: message
  });
})
```

## CSRF Token

```javascript
const csrf = require('csurf');
const csrfProtection = csrf();

// check the non-get route to see whether the csrf token is existed on request or not
app.use(csrfProtection);

app.use((req, res, next) => {
 // pass csrf token into view
  res.locals.csrfToken = req.csrfToken();
  next();
});
```

<pre class="language-ejs"><code class="lang-ejs">&#x3C;form class="login-form" action="/login" method="POST">
<strong>    &#x3C;input type="hidden" name="_csrf" value="&#x3C;%= csrfToken %>">
</strong><strong>&#x3C;/form>
</strong></code></pre>

## File Handling

### Upload

```javascript
const multer = require('multer');

// To parse the request which type is multpart/form-data and contain file binary data
// define the storage location and stream the binary data to destination automatically
// decide which file type that will be processed
// set to req.file automatically
const fileStorage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'images');
  },
  filename: (req, file, cb) => {
    cb(null, new Date().toISOString() + '-' + file.originalname);
  }
});

// pass to the multer middleware to save the image first
app.use(
  multer({ storage: fileStorage, fileFilter: fileFilter }).single('image')
);

// serve the static file 
app.use('/images', express.static(path.join(__dirname, 'images')));

app.post("/image", (req,res,next) => {
  const title = req.body.title;
  // handle the file data which is returned from multer middleware
  const image = req.file;
  const imageUrl = image.path;
  // ...
});

```

### Download

```javascript
app.get("/download", (req,res,next) =>{
     const invoiceName = 'invoice-' + orderId + '.pdf';
     const invoicePath = path.join('data', 'invoices', invoiceName);
     // create stream to get file chunk data one by one
     const file = fs.createReadStream(invoicePath);
     
     res.setHeader('Content-Type', 'application/pdf');
     // to notify the browser that it is used to download file with specific name
     // to show save as dialog
      res.setHeader(
        'Content-Disposition',
        'attachment; filename="' + invoiceName + '"'
      );
     // res itself is a writeable stream, and forward the chunks of data 
     // from read stream, finally the all the buffers will be concat 
     // and form the final file on the browser
     file.pipe(res);
})
```

## Deployment

### Pre-action

* Get the ubuntu server from Vultr / DigitalOcean / AWS
* Change Password :`sudo passwd`
* Install nodejs and update its version: &#x20;

```bash
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt-get install -y nodejs
```

* Install mysql :

```bash
sudo apt-get update
sudo apt-get install mysql-server
```

```sql
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'your-password';
```

* Install forever / pm2&#x20;

```bash
npm install -g forever
```

### Https

* Register the domain in goDaddy or other
* Link to your ip address
* Add Certbot PPA &#x20;

```bash
sudo apt-get update &&
sudo apt-get install software-properties-common &&
sudo add-apt-repository universe &&
sudo add-apt-repository ppa:certbot/certbot &&
sudo apt-get update
```

* Install Certbot

```bash
sudo apt-get install certbot
```

* Register to obtain the key, cert and ca

```bash
sudo certbot certonly --standalone
```

* Read the key ,cert and ca into server.js in production and be https

```javascript
const https = require("https");
const fs = require("fs");
const IS_PROD = process.env.NODE_ENV === "production";
const http = require("http");
if (IS_PROD) {
  server = https.createServer(
    {
      key: fs.readFileSync(
        "/etc/letsencrypt/live/friendchats.xyz/privkey.pem",
        "utf8"
      ),
      cert: fs.readFileSync(
        "/etc/letsencrypt/live/friendchats.xyz/cert.pem",
        "utf8"
      ),
      ca: fs.readFileSync(
        "/etc/letsencrypt/live/friendchats.xyz/chain.pem",
        "utf8"
      ),
    },
    app
  );
} else {
  server = http.createServer(app);
}

server.listen(process.env.PORT, function () {
  console.log("Server Start ");
});

```

### Forever&#x20;

* Install forever to execute the nodejs express program

```javascript
npm install forever -g 
```

* Execute npm command by using forever

```javascript
forever start -c "npm run start:prod" ./
```

* Find the uid by using:

```javascript
forever list
```

* Stop the program by using uid

```javascript
forever stop {uid}
```

* Find the uid who is using the port (e.g 8080)

```bash
lsof -i :8080
```

* Kill the process by using uid&#x20;

```bash
kill -9 uid
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://petercheng7788.gitbook.io/developer-note/backend/nodejs/expressjs.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
