[NodeJS] Using Multer and Cls-Hooked together

Theekshana Wijesinghe
2 min readMay 24, 2020

Multer and Cls-hooked are two important modules in the NodeJS ecosystem. Multer,

Multer is a node.js middleware for handling multipart/form-data, which is primarily used for uploading files.

Cls-Hooked is an extension of CLS which used to preserve the context with async/await,

Continuation-local storage works like thread-local storage in threaded programming, but is based on chains of Node-style callbacks instead of threads.

Let’s consider a general use case of these modules in an Express application.

The following web app, exposes POST /upload endpoint to upload a file. We use Multer middleware to read the file named userfile in the request. Cls-hooked is used to generate a unique loggerId for each request (We print it in the controller).

const express = require('express');
const multer = require('multer');
const createNamespace = require('cls-hooked').createNamespace;
const { v4: uuidv4 } = require('uuid');
const namespace = createNamespace('EXPRESS_APP');const app = express();const upload = multer();// generate loggerId
app.use((req, res, next) => {
namespace.run(() => {
// loggerId is set in the context
namespace.set('loggerId', uuidv4());
next();
});
});
app.post('/upload', upload.single('userfile') , async (req, res) => {
console.log('request received', namespace.get('loggerId'));
// do the processing with file
// const result = await processFile(req.file);
res.send('OK');
});
app.listen(3000, () => {
console.log('App started');
});

Notes: uuid is used to generate a random UUID. Good oldconsole.log is used here for logging, for sophisticated logging in your application, use a module like pino.

If you run this code what you’ll see in the console is,

request received undefined

Why?

When multer middleware is used, the CLS context is lost (https://github.com/expressjs/multer/issues/814). I wasted a good amount of time when I faced this issue not knowing what the real problem is.

How to Fix?

The way I fixed issue is why wrapping the Multer’s upload with a Promise

Here’s the code. (Only the diff is given below)

...const upload = multer().single('userfile'); ...// Promise wrapping the Multer upload
const multerPromise = (req, res) => {
return new Promise((resolve, reject) => {
upload(req, res, (err) => {
if(!err) resolve();
reject(err);
});
});
};
const uploadMiddleware = async (req, res, next) => {
try {
await multerPromise(req, res);
next();
} catch(e) {
next(e);
}
};
app.post('/upload', uploadMiddleware , async (req, res) => { ...

If multerPromise is resolved without any errors, req.file will contain the file (Usual Multer behavior). Now you’ll see something like following in the Console.

request received 3e8d43b5-83fa-4c39-bb0e-5828fb79d197

Hope this will be useful to anyone struggling to get Multer and Cls-Hooked work together.

--

--