Node.js/Express

Middleware

WebDevLee 2021. 11. 18. 14:47

이 글은 Express의 Middleware에 관한 개념 및 사용법을 정리하기 위해 작성하였습니다.



< Introduction >

Writing code is a creative process. Programmers will be quick to differ in opinion on whether the solution to a problem should be implemented in one way or another. The problems programmers face most frequently will have several different solutions, all correct but all written differently with various factors considered.
Because “correct” code can take so many different forms, developers have cultural notions of code quality that is somewhat independent of these decisions.

In this course, we will investigate several ways to avoid replication and reduce complexity. In programming in general, this often means putting the reused code into reusable containers like functions and objects.
In Express specifically, this will also mean composing our desired functionality into a series of middleware functions.

 

  • Central notion of quality code
    1. readability of code
    2. code duplication (Avoid duplication of code)

 

Reference : What is a Middleware?

https://jamischarles.com/posts/what-is-middleware-a-simple-explanation

 

What is Middleware? A simple explanation. - Jamis Charles' Digital Garden

What is Middleware? A simple explanation. What is Middleware? A simple explanation. If you’ve used Ruby on Rails, Sinatra, Express.js or any other modern web framework, you’ve likely encountered the term Middleware. In this short post I’ll explain wh

jamischarles.com

 

 


< DRYing code with Functions >

The quality coder’s credo is “Don’t Repeat Yourself” (DRY).

 

ex)

// Repetitive Code
const addFive = number => {
  const fiveAdded = number + 5;
  console.log(`Your number plus 5 is ${fiveAdded}`);
}
 
const addTen = number => {
  const tenAdded = number + 10;
  console.log(`Your number plus 10 is ${tenAdded}`);
}
 
const addTwenty = number => {
  const twentyAdded = number + 20;
  console.log(`Your number plus 20 is ${twentyAdded}`);
}

// DRY code
const addNumber = (number, addend) => {
  const numAdded = number + addend;
  console.log(`Your number plus ${addend} is ${numAdded}`);
}

 

 


< DRYing Routes With app.use( ) >

To get code to run every time one of our Express routes is called, Use middleware and app.use( ) method.

 

< Middleware >
1.
Middleware is code that executes between a server receiving a request and sending a response. It operates on the boundary, so to speak, between those two HTTP actions.
2. In Express, middleware is a function with three parameter : (req, res, next)

-> Middleware can perform logic on the request and response objects, such as: inspecting a request, performing some logic based on the request, attaching information to the response, attaching a status to the response, sending the response back to the user, or simply passing the request and response to another middleware. Middleware can do any combination of those things or anything else a Javascript function can do.

-> An Express application is essentially a series of middleware function calls.

 

  • app.use( ) takes a callback function that it will call for every ereceived request.

 

ex)

app.get('/beans/', (req, res, next) => {
  console.log('Request received');
  //
  //
});

app.post('/beans/:id', (req, res, next) => {
  console.log('Request received');
  //
  //
});

// -> Drying with app.use()
app.use((req, res, next) => {
  console.log('Request received');
})

 

+. Express routes are also middleware!

 

 


< next( ) >

To pass control to the next middleware, use next( ) method.

 

ex)

app.use((req, res, next) => {
  console.log("A sorcerer approaches!");
  next();
});
 
app.get('/magic/:spellname', (req, res, next) => {
  console.log("The sorcerer is casting a spell!");
  next();
});
 
app.get('/magic/:spellname', (req, res, next) => {
  console.log(`The sorcerer has cast ${req.params.spellname}`);
  res.status(200).send();
});
 
app.get('/magic/:spellname', (req, res, next) => {
  console.log("The sorcerer is leaving!");
});

// Accessing http://localhost:4001/magic/fireball 
// Console Output:
// "A sorcerer approaches!"
// "The sorcerer is casting a spell!"
// "The sorcerer has cast fireball"

: The last route was not executed. This is because the previous middleware did not invoke the next() function to run the following middleware.

 

 


< Path in app.use( ) >

app.use( ) can takes an optional path parameter as its first argument!

 

-> this means app.use( ) can takes an optional path parameter as its first argument!

 

ex)

// example 1
app.use('/sorcerer', (req, res, next) => {
  console.log('User has hit endpoint /sorcerer');
  next();
})


// example 2
app.use(['/beans/', '/beans/:beanName'], (req, res, next) => {
  // ~~~
})

 

 


< Middleware Stacks >

Methods such as app.use( ), app.get( ), app.post( ), and so on all can take multiple callbacks as additional parameters.

 

ex)

const authenticate = (req, res, next) => {
  ...
};
 
const validateData = (req, res, next) => {
  ...
};
 
const getSpell = (req, res, next) => {
  res.status(200).send(getSpellById(req.params.id));
};
 
const createSpell = (req, res, next) => {
  createSpellFromRequest(req);
  res.status(201).send();
};
 
const updateSpell = (req, res, next) => {
  updateSpellFromRequest(req);
  res.status(204).send();
}
 
app.get('/spells/:id', authenticate, getSpell);
 
app.post('/spells', authenticate, validateData, createSpell);
 
app.put('/spells/:id', authenticate, validateData, updateSpell);

: Callback functions execute in order.

 

 


< Open-Source Middleware : Logging >

In the workspace you’ll see what code looks like using unnecessary custom solutions and lots of lines calling 
console.log(). It’s not bad code, but it introduces complexity that could be avoided. Time spent thinking about and writing code that accomplishes common tasks is time that could be better spent on thinking about and writing code that is unique to your application.

We will replace the logging code in the workspace with morgan, an open-source library for logging information about the HTTP request-response cycle in a server application.

 

Morgan : https://github.com/expressjs/morgan

 

GitHub - expressjs/morgan: HTTP request logger middleware for node.js

HTTP request logger middleware for node.js. Contribute to expressjs/morgan development by creating an account on GitHub.

github.com

 

 


< Open-Source Middleware : Body Parsing >

When we implement middleware, we take in the req object, so that we can see information about the request. This object includes a good deal of important information about the request that we can use to inform our response, however for some requests it misses a fundamental piece. An HTTP request can include a body, a set of information to be transmitted to the server for processing. This is useful when the end user needs to send information to the server. If you’ve ever uploaded a post onto a social media website or filled out a registration form chances are you’ve sent an HTTP request with a body.

The lucky thing about using open-source middleware is that even though parsing the body of an HTTP request is a tricky operation requiring knowledge about network data transfer concepts, we easily manage it by importing a library to do it for us with body-Parser.

 

 

GitHub - expressjs/body-parser: Node.js body parsing middleware

Node.js body parsing middleware. Contribute to expressjs/body-parser development by creating an account on GitHub.

github.com

 

 


< Error-Handling Middleware >

To handle error in middleware.

Error handling middleware needs to be the last app.use( ) in your file. If an error happens in any of our routes, we want to make sure it gets passed to our error handler.
We do this by passing an error object as an argument to next( ). Usually, next( ) is called without arguments and will proceed through the middleware stack as expected. When called with an error as the first argument, however, it will call any applicable error-handling middleware.

 

ex)

app.use((req, res, next) => {
  const newValue = possiblyProblematicOperation();
  if (newValue === undefined) {
    let undefinedError = new Error('newValue was not defined!');
    return next(undefinedError);
  }
  next();
});
 
app.use((err, req, res, next) => {
  const status = err.status || 500;
  res.status(status).send(err.message);
});

: we can see that error-handling middleware is written much like other kinds of middleware. The biggest difference is that there is an additional parameter in our callback function, err. This represents the error object, and we can use it to investigate the error and perform different tasks depending on what kind of error was thrown.