Refactoring Multer instance using Javascript Closures
Recently I was coding file upload using express
and multer
to handle file uploads on the server side. This time the requirement for the app I’ve been working on won’t use AWS S3. The tasks I had to do in the following list are,
- Direct file storage on the server directory.
- File uploads from Frontend will definitely be
multipart/form-data
. - Use
multer
as it is a popular library in ExpressJS community to handle file uploads as well as Form Data. - Our app will be divided with independent Express Router Files to handle each endpoints.
- The files uploaded will be stored on different file paths on the disk based on the endpoints.
Configuring Express, Routers and multer was a breeze. The problem once came up when I had to declare many multerdiskStorage
instances across the files for separate file paths. It was a tedious task with clear violation of DRY principle. To clearly see the problem, take a glance at the prototype code below.
You can clearly see both are almost identical. Only destination
is changed. As I said above this is a violation of DRY principle. It won’t be feasible to copy + paste the multer
instance every time we create a new route with file upload handlers, plus we might have additional configurations in the future (eg: a change in file naming).
Let’s fix it.
What if we could have one multer
instance, and require them with minimal effort and pass it on as our regular middleware? My idea is to use it like,
const upload = require('./upload')('./file_destination');
This is where the concept of Closures come into play. Take a look at this diagram to understand the gist of how it will work with the whole application.
Right. Here is how new upload.js
file looks like after a refactor. Looks so simple from clustered code before, right? Keep reading and I’ll explain the technical details below.
See the significant reduced codes in cats.js
and dogs.js
? We should always keep that simplicity!
What happened when we require('multer')('./file_destination')
? This is the meat of Javascript Closures. Here is my thought process.
- Each
require
always return an object, a function, array or a variable from the exported module. - In this case, we passed on
upload
function with anonymous function withdirPath
parameter inupload.js
. - The second scope of require,
('./file_destination')
is passed into that anonymous function. ./file_destination
is then again passed intodirPath
incustomStore
function ofstorage
object (which returnsmulter.diskStorage
object), enabling to utilize the variable passed from outside of its scope, finally returning with amulter
instance with properstorage
option once initialization is done.require('multer')('./file_destination')
it in the Routers and profit.
This is one simple practical use of how Closures are used. Understanding and practically using Closures are sorcery at the start but once you’ve gripped on the concepts, it becomes your weapon.
Hope you’ve enjoyed the demonstration. Thank you for reading this far!