DEV Community

Maneet Goyal for Manufac Analytics Private Limited

Posted on • Edited on

2

Implementing Multer Storage Engine in TypeScript

Multer is pretty popular ExpressJS middleware, primarily used to handle file uploads from client to server. However, a lot of other options are available too depending on what fits your use case.

Multer plays along really well with ExpressJS and have out-of-the-box support for storing file in-disk and in-memory, on the server side. Interestingly, Multer can also be used for file streaming applications where you don't need to store the incoming file on server side and directly redirect the incoming file stream to some cloud storage service instead (say, AWS S3). When you need to support the uploads of bulky/large files (say, tens or hundreds of MBs or anything beyond that), streaming can come across as the most practical solution.

The file streaming logic can vary depending on your cloud storage service provide but in this post I'll simply present the storage engine implementation logic in generalized way which you can expand upon based on your use cases. This logic is already presented by the maintainers of Multer here but it uses object prototypes and vanilla JS. For those of us who have gotten the taste of syntactic sugar by using JS classes might find it hard to comprehend that logic right away. Moreover, owing to the popularity of TypeScript, a TS implementation of that storage engine logic may also be handy for the web dev community.

So, here's TS Classes based approach for implementing Multer's Storage Engine:

import { createWriteStream, unlink } from "fs";
import type { PathLike } from "fs";
import type { Request } from "express";
type Maybe<T> = T | null | undefined;
type ResponseCallback<T> = (err: Error | null, payload?: Maybe<T>) => void;
type RequestHandler<T> = (req: Request, file: Express.Multer.File, responseCb: ResponseCallback<T>) => void;
/**
* Custom Multer Storage Engine for uploading file to AWS S3
* Ref: https://github.com/expressjs/multer/blob/master/StorageEngine.md
* Example Usage:
* ```ts
* const storageEngine = new MulterS3StorageEngine((_req, file, responseCb) => {
* responseCb(null, `/var/www/uploads/${file.originalname}`);
* });
* ```
*/
export class MulterS3StorageEngine {
/**
* Tells the engine where to save the file.
* @param _req
* @param _file
* @param responseCb
*/
getDestination: RequestHandler<PathLike> = (_req, _file, responseCb) => {
responseCb(null, "/dev/null");
};
/**
* Stores the file and returns information on how to access the file in the future.
* The information you provide in the callback will be merged with multer's file object, and then presented to the user via `req.files`.
* @param req
* @param file
* @param responseCb
*/
_handleFile: RequestHandler<unknown> = (
req: Request,
file: Express.Multer.File,
responseCb: ResponseCallback<unknown>
) => {
this.getDestination(req, file, (err, path) => {
if (err !== null) {
responseCb(err);
}
if (path !== null && path !== undefined) {
const outStream = createWriteStream(path);
file.stream.pipe(outStream);
outStream.on("error", responseCb);
outStream.on("finish", () => {
responseCb(null, { path, size: outStream.bytesWritten });
});
}
});
};
/**
* Removes the file if an error is encounter during file stream handling.
* Multer will decide which files to delete and when.
* @param _req
* @param file
* @param responseCb
*/
_removeFile: RequestHandler<unknown> = (
_req: Request,
file: Express.Multer.File,
responseCb: ResponseCallback<unknown>
) => {
unlink(file.path, responseCb);
};
constructor(destination?: RequestHandler<string>) {
this.getDestination = destination ?? this.getDestination;
}
}

Playwright CLI Flags Tutorial

5 Playwright CLI Flags That Will Transform Your Testing Workflow

  • 0:56 --last-failed: Zero in on just the tests that failed in your previous run
  • 2:34 --only-changed: Test only the spec files you've modified in git
  • 4:27 --repeat-each: Run tests multiple times to catch flaky behavior before it reaches production
  • 5:15 --forbid-only: Prevent accidental test.only commits from breaking your CI pipeline
  • 5:51 --ui --headed --workers 1: Debug visually with browser windows and sequential test execution

Learn how these powerful command-line options can save you time, strengthen your test suite, and streamline your Playwright testing experience. Click on any timestamp above to jump directly to that section in the tutorial!

Watch Full Video 📹️

Top comments (0)

Playwright CLI Flags Tutorial

5 Playwright CLI Flags That Will Transform Your Testing Workflow

  • --last-failed: Zero in on just the tests that failed in your previous run
  • --only-changed: Test only the spec files you've modified in git
  • --repeat-each: Run tests multiple times to catch flaky behavior before it reaches production
  • --forbid-only: Prevent accidental test.only commits from breaking your CI pipeline
  • --ui --headed --workers 1: Debug visually with browser windows and sequential test execution

Learn how these powerful command-line options can save you time, strengthen your test suite, and streamline your Playwright testing experience. Practical examples included!

Watch Video 📹️

👋 Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay