Optimizing and Resizing Images with Nest.js: A Comprehensive Guide
Written on
Understanding the Importance of Image Resizing
In web applications, resizing images is a crucial practice for several key reasons:
- Performance Improvement: Large images can drastically hinder page load speeds. By adjusting images to suitable dimensions and sizes, you can enhance the performance of your web application, resulting in a smoother user experience.
- Bandwidth Savings: Transmitting large images consumes significant bandwidth, which can be expensive for users and website owners alike, especially with limited data plans or hosting costs. Resizing images minimizes data transfer.
- Storage Optimization: Storing oversized images uses more server space. Resizing or compressing images can lead to reduced storage expenses.
- Uniform UI/UX: User-uploaded images can vary in size, leading to layout inconsistencies or distorted visuals. Resizing ensures that images conform to design specifications, maintaining visual harmony.
- Mobile Compatibility: Given the limited screen space and slower connections on mobile devices, resizing images for these platforms ensures optimal load times and user experience.
- Responsive Design: For websites that adapt to various screen sizes, resizing images for different breakpoints ensures consistent aesthetics across devices.
With these factors in mind, let's dive into the steps for optimizing and resizing images with Nest.js!
Step 1: Install Nest.js CLI
To get started, install the Nest.js CLI globally:
npm install -g @nestjs/cli
Step 2: Install Type Definitions for Multer
Next, install the type definitions for Multer, which is used for file uploads:
npm install --save-dev @types/multer
What is Multer?
Multer is a Node.js middleware specifically designed to handle multipart/form-data, which is mainly used for file uploads.
Step 3: Install Sharp for Image Processing
Install the Sharp module, which facilitates image processing:
npm install sharp
What is Sharp?
Sharp is a fast Node.js library aimed at converting large images into smaller, web-friendly formats like JPEG, PNG, WebP, GIF, and AVIF, in various dimensions. Besides resizing, it supports operations like rotation, extraction, compositing, and gamma correction.
Step 4: Register the Multer Module
In your app.module.ts, register the Multer module:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MulterModule } from '@nestjs/platform-express';
@Module({
imports: [MulterModule.register()],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Step 5: Set Up the Pipes Directory
Create a pipes folder in the src directory and add an image.pipe.ts file. Your folder structure should resemble this:
├── dist
├── node_modules
├── public/
│ └── images
├── src/
│ ├── pipes/
│ │ └── image.pipe.ts
│ ├── app.controller.ts
│ ├── app.service.ts
│ ├── app.module.ts
│ └── main.ts
├── .eslintrc.js
├── .gitignore
├── .prettierrc
├── nest-cli.json
├── package-lock.json
├── package.json
├── tsconfig.build.json
└── tsconfig.json
Step 6: Create the Upload Endpoint
In the app.controller.ts, set up the "upload" endpoint:
import {
Controller,
Post,
UseInterceptors,
UploadedFile,
} from '@nestjs/common';
import { AppService } from './app.service';
import { FileInterceptor } from '@nestjs/platform-express';
import { ImagePipe } from './pipes/image.pipe';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Post('upload')
@UseInterceptors(FileInterceptor('image'))
async uploadFile(@UploadedFile(ImagePipe) file: Express.Multer.File) {
console.log('Your file has been successfully saved!');
return file;
}
}
Explanation of the Code:
Here, we define the AppController class, which is essential for managing HTTP requests in Nest.js. The @Post("upload") endpoint is triggered when a file is uploaded via Postman. The UseInterceptors decorator specifies the middleware behavior for this endpoint, utilizing the FileInterceptor to handle the file uploads.
Step 7: Create the Custom Image Pipe
Now, let's implement our custom ImagePipe, which will adhere to the PipeTransform interface:
import { Injectable, PipeTransform } from '@nestjs/common';
import { accessSync } from 'node:fs';
import { parse, join } from 'path';
import * as sharp from 'sharp';
@Injectable()
export class ImagePipe implements PipeTransform {
async transform(image: Express.Multer.File): Promise<string> {
const pathToSave = 'public/images';
try {
accessSync(pathToSave);
const imageType = image.mimetype.split('/')[1];
const originalName = parse(image.originalname).name;
const filename = Date.now() + '-' + originalName + .${imageType};
// Image processing magic
await sharp(image.buffer)
.resize({ width: 200, height: 200, fit: 'fill' })
.toFile(join(pathToSave, filename));
// End of magic
return filename;
} catch (err) {
console.error('Error', err);}
}
}
Code Breakdown:
The ImagePipe class implements the PipeTransform interface, which allows for custom pipe creation in Nest.js. The transform method receives an uploaded image and processes it, ultimately returning a filename for storage.
Using Postman to Test the API
Finally, you can test the upload functionality by sending a POST request from Postman with form-data, using "image" as the key to upload a file from your local machine. Upon clicking "send," the API should return the saved filename and store the image in the public/images directory.
Hopefully, you find this guide helpful! Feel free to connect on various platforms for more insights.