NestJS: Creating a pipe to optimize uploaded images.
Tags: nestjs, node, backend, javascript
Published: 2022-03-23 23:15:56

---
title: NestJS: Creating a pipe to optimize uploaded images.
published: true
description: Pipes in NestJS are very useful to transform input data going into the route handler. Let's build one of these pipes to optimize uploaded images.
tags: nestjs, node, backend, javascript
cover_image: https://images.pexels.com/photos/942316/pexels-photo-942316.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940
---
> Cover image by Tim Mossholder on [Pexels](https://www.pexels.com/photo/eight-electrical-metric-meters-942316/)

## Introduction.

In NestJS Context, pipes are intermediary between the incoming request and the request handled by the route handler.

![Pipes to Handler diagram.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w7zjle48esh00jkthvcc.png)

Pipes have 2 common use cases:

1. Validation
2. Transformation

In the case of transformation, pipes take care of transforming input data in a specific format to be received by the route handler.

An example of this would be converting a `String` to an `Int`, which is the case of `ParseIntPipe`.

In this post, we're going to build a pipe that takes an incoming image and transforms it into a size and web-friendly format.

## Preparing Multer.

Nest uses by default Multer middleware to handle data sent with `multipart/form-data` which is used principally to upload files via HTTP POST.

First, we need to install Multer typings

```
npm i -D @types/multer
```

Then let's import it to our root module.

```ts
// app.module.ts
@Module({
  // importing MulterModule and use memory storage to use the buffer within the pipe
  imports: [MulterModule.register({
    storage: memoryStorage()
  })],
  controllers: [AppController],
  providers: [AppService],
})
```

The route handler needs to receive the uploaded file by the client, so we need to add the interceptor `FileInterceptor()` to extract the file from the request.

```ts
// app.controller.ts
  @Post()
  @UseInterceptors(FileInterceptor('image'))
  uploadImage(@UploadedFile() image: Express.Multer.File) {
    this.service.uploadImage(image);
  }
```

## Building the pipe.

To handle the image transformation we're going to use Sharp. Sharp is a high performance image processing module, it's very useful to convert large images to smaller formats.

Let's install this module along with its typings

```
npm install sharp
```

```
npm i -D @types/sharp
```

We can now create our pipe by creating the file `sharp.pipe.ts`

A pipe must implement the `PipeTransform` interface and must be annotated with the `@Injectable()` decorator.

`PipeTransform<T, R>` is a generic interface, where `T` is the input type and `R` is the type returned by the `transform()` method.

In this case, we expect to receive an `Express.Multer.File` and after the transform process, we're going to return a `string` with the name of the file.

```ts
import { Injectable, PipeTransform } from '@nestjs/common';

@Injectable()
export class SharpPipe implements PipeTransform<Express.Multer.File, Promise<string>> {

  async transform(image: Express.Multer.File): Promise<string> {

  }

}
```

With these lines, our pipe fulfills the `PipeTransform` interface. We're ready to begin the implementation.

The final code looks like this:

```ts
import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common';
import * as path from 'path';
import * as sharp from 'sharp';

@Injectable()
export class SharpPipe implements PipeTransform<Express.Multer.File, Promise<string>> {

  async transform(image: Express.Multer.File): Promise<string> {
    const originalName = path.parse(image.originalname).name;
    const filename = Date.now() + '-' + originalName + '.webp';

    await sharp(image.buffer)
      .resize(800)
      .webp({ effort: 3 })
      .toFile(path.join('uploads', filename));

    return filename;
  }

}
```

Let's explain some of the lines from the above code.

`image.originalname` contains the file's original name, including its extension. We're planning to convert this file into a .WEBP file, so the original extension is not useful in this case. We only extract the file name with the `path` module.

```ts
const originalName = path.parse(image.originalname).name;
```

Then we create a new file name, to avoid duplicated collisions. Finally, we add the new extension: `.webp`

```ts
const filename = Date.now() + '-' + originalName + '.webp';
```

To finally convert our image, we execute `sharp` with the image buffer, we resize it to 800x800 and convert it to `webp`. Sharp has an extensive API to manipulate quality and sizes, you can find more options in their [oficial docs](https://sharp.pixelplumbing.com)

We finish by calling `.toFile()` with the path where this image is going to be saved. In this case it will be saved in `./uploads/<filename>.webp`

```ts
await sharp(image.buffer)
  .resize(800)
  .webp({ effort: 3 })
  .toFile(path.join('uploads', filename));
```

Our pipe is ready to be used, now we need to integrate it into our route handler. To do that, it's simple as passing our new pipe as an argument to the `UploadedFile` decorator.

As SharpPipe returns a `string` we need to change the image typing in the route handler. So we replace `Express.Multer.File` with `string`.

```ts
// app.controller.ts
  @Post()
  @UseInterceptors(FileInterceptor('image'))
  //                        vvv Our pipe
  uploadImage(@UploadedFile(SharpPipe) image: string) {
    this.service.uploadImage(image);
  }
```
## Conclusion.
And in that way, we already have a flow to optimize uploaded images by the client.

I made a quick test and tried to upload a 5MB image, after getting through the pipe the image size was 15.5KB. **That's a decrease of ~ 99%!**

If you liked this tutorial, please share and hit ❤

## Further Reading

- [https://sharp.pixelplumbing.com/api-constructor](https://sharp.pixelplumbing.com/api-constructor)
- [https://docs.nestjs.com/pipes](https://docs.nestjs.com/pipes)
- [https://docs.nestjs.com/techniques/file-upload](https://docs.nestjs.com/techniques/file-upload)
- [https://github.com/expressjs/multer](https://github.com/expressjs/multer)
- [https://wanago.io/2021/11/08/api-nestjs-uploading-files-to-server/](https://wanago.io/2021/11/08/api-nestjs-uploading-files-to-server/)