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
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
// 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");
});
// 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
// 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
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
// 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
}
});
<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
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('/');
});
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
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
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();
});
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
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