Codewithajoydas LogoCodewithajoydas.
  • Home
  • About
  • Skills
  • Projects
  • Lab
  • Experience
  • Articles
  • Contact
Codewithajoydas LogoCodewithajoydas.
  • Home
  • About
  • Skills
  • Projects
  • Lab
  • Experience
  • Articles
  • Contact

© 2026 Codewithajoydas

All articles
typescript12 min read

Building CWAD Image Resizer with Next.js and Sharp

A detailed look at how I built CWAD Image Resizer, a full-stack image processing tool for resizing, converting, optimizing, downloading, and optionally hosting images.

4 October 2026
Next.jsSharpTypeScriptImage ProcessingCloudinaryUpstashFull Stack
Building CWAD Image Resizer with Next.js and Sharp

Building CWAD Image Resizer with Next.js and Sharp

Image processing looks simple from the outside. Upload an image, change its dimensions, select a format, and download the result. But building a reliable image processing application quickly becomes more interesting when you need to handle different formats, dimensions, quality settings, validation, file sizes, API security, and cloud storage.

That was the idea behind CWAD Image Resizer.

CWAD Image Resizer is a full-stack web application that allows users to upload images, resize them, change their output format, control image quality, download the processed result, and optionally host the processed image through Cloudinary.

The project was built primarily with Next.js, TypeScript, and Sharp, with additional services such as Cloudinary and Upstash Redis handling image hosting and API rate limiting.

Why I Built It

Image resizing is something developers frequently need when working with websites, portfolios, social media assets, thumbnails, profile images, and other digital content.

Usually, developers have to depend on external image editing applications or online tools for relatively simple operations such as changing an image from one format to another or reducing its dimensions.

I wanted to build a tool that could handle these operations directly inside a web application while also giving me an opportunity to understand how image processing works on the backend.

The goal was not simply to create a frontend where users select an image. The important part was building the backend processing pipeline that receives the uploaded file, validates the request, processes the image, generates the requested output, and returns the result efficiently.

What the Application Does

CWAD Image Resizer provides several image-processing capabilities in a single workflow.

Users can upload an image and inspect its original dimensions before processing it. They can then provide custom width and height values, maintain the original aspect ratio when required, select an output format, control image quality, and process the image.

Codewithajoydas

Software developer building applications, developer tools, experiments, and systems while continuously learning.

Explore

ProjectsSkillsExperience

About

About MeContact

Connect

The application supports common formats including:

  • PNG
  • JPEG
  • WebP
  • AVIF

After processing, the user can download the generated image directly.

The application can also optionally upload the processed image to Cloudinary and generate a permanent URL that can be used to access or share the image.

The Technology Stack

Next.js

Next.js provides the application framework and handles both the frontend and backend portions of the project.

Instead of building a separate frontend and backend application, the project uses Next.js Route Handlers to expose server-side APIs for image processing.

This keeps the application structure relatively simple while still allowing the heavy image-processing work to happen on the server.

TypeScript

TypeScript is used throughout the project to make the application easier to maintain and reduce errors caused by unexpected data types.

Image processing involves several pieces of structured data such as dimensions, output formats, quality values, upload information, and processing options. Strong typing makes these structures easier to reason about.

Sharp

Sharp is the core image-processing library behind the application.

This is where most of the actual image transformation happens.

Instead of implementing image resizing or format conversion manually, the backend passes the uploaded file buffer to Sharp and applies the requested transformations.

Sharp can handle operations such as resizing, format conversion, compression, and quality configuration while providing efficient server-side image processing.

Cloudinary

Cloudinary is used when the user wants to host the processed image instead of only downloading it.

The application can send the processed image to Cloudinary and receive a hosted URL. This turns the image-processing workflow into something more useful for developers who need a publicly accessible image rather than a local download.

Upstash Redis and Upstash Ratelimit

Image processing is a resource-intensive operation compared with a normal API request.

Without protection, an API endpoint could potentially be abused by repeatedly sending image-processing requests.

To address this, the application uses Upstash Redis together with Upstash Ratelimit to implement IP-based API rate limiting.

This limits how frequently a client can call the processing API and helps protect server resources.

Zod

Zod is used for server-side validation.

The backend should never blindly trust values received from the client. Width, height, quality, format, and other processing parameters need to be validated before they are passed to the image-processing layer.

Using a schema-based validation approach makes those rules explicit and keeps invalid requests away from the processing pipeline.

How the Image Processing Flow Works

The complete workflow can be thought of as a pipeline.

User selects image
        ↓
Client validates image
        ↓
Image sent to API
        ↓
Server validates request
        ↓
Rate limit checked
        ↓
Sharp processes image
        ↓
Output generated
        ↓
Download OR Cloudinary upload
        ↓
Result returned to user

This separation is important because the browser should not be responsible for performing the main image transformation.

The client collects the user's configuration, while the server performs the actual processing.

Uploading the Image

The first step is selecting an image.

The application supports a normal file-selection workflow as well as drag-and-drop interaction.

Once a file is selected, the application can inspect basic information such as the original image dimensions and use that information to populate the interface.

This gives the user immediate feedback about the image before processing begins.

Client-side validation is also useful at this stage because obviously invalid files can be rejected before making an unnecessary API request.

However, client-side validation is not considered sufficient security by itself.

The server performs its own validation before processing the image.

Resizing Images

The core feature is image resizing.

The user can provide a target width and height, depending on the desired output.

One important part of the implementation is aspect-ratio handling.

If the user wants to preserve the original aspect ratio, changing one dimension should allow the other dimension to be calculated automatically rather than stretching the image.

This prevents common problems such as distorted faces, logos, icons, and other visual elements.

The backend then passes the requested dimensions to Sharp and generates the resized image.

Image Quality Control

Image dimensions are only one part of optimization.

Two images can have exactly the same dimensions while having significantly different file sizes because of their compression settings and output formats.

The application therefore provides quality control so the user can choose how aggressively the image should be compressed.

This is particularly useful when the goal is to reduce image size for websites while maintaining acceptable visual quality.

A higher quality setting generally preserves more visual information but can result in a larger file.

A lower quality setting can significantly reduce the output size but may introduce visible compression artifacts.

This creates a practical trade-off between image quality and file size.

Format Conversion

Another important feature is converting an image from one format to another.

For example, a user may upload a PNG image but want a WebP version for use on a website.

The application supports multiple output formats including PNG, JPEG, WebP, and AVIF.

This is particularly useful because modern image formats can provide better compression and performance depending on the use case.

The backend does not simply rename the file extension. Sharp actually decodes the source image and encodes it into the requested output format.

That distinction is important because changing .png to .webp as a filename would not actually convert the underlying image data.

Downloading the Result

Once processing is complete, the generated image can be downloaded by the user.

The backend returns the processed image data instead of requiring the user to manually interact with a temporary server file.

This keeps the workflow simple:

Upload → Configure → Process → Download

For a utility application, keeping this workflow straightforward is important. The user should not have to understand what is happening internally to successfully process an image.

Cloudinary Integration

Downloading the image is useful, but there are situations where a developer needs a URL instead.

For example, a developer may want to use the processed image inside a website, README, blog post, documentation page, or another application.

This is where the optional Cloudinary integration becomes useful.

Instead of only returning the processed image to the browser, the application can upload the generated output to Cloudinary.

Cloudinary then provides a hosted URL that can be stored or shared.

The workflow becomes:

Upload Image
     ↓
Process with Sharp
     ↓
Generate Output
     ↓
Upload to Cloudinary
     ↓
Return Hosted URL

This makes the application more than a simple image-resizing utility. It can also act as part of an image delivery workflow.

API Rate Limiting

One of the important lessons from building this project was that an API performing expensive operations should not be left completely unprotected.

Image processing consumes server resources. A malicious or overly aggressive client could repeatedly submit large images and cause unnecessary CPU and memory usage.

To reduce this risk, the application uses IP-based rate limiting with Upstash Redis and Upstash Ratelimit.

The basic idea is:

Request
   ↓
Identify client
   ↓
Check rate limit
   ↓
Allowed? ── No ──→ Reject request
   │
  Yes
   ↓
Process image

This provides an additional layer of protection around the image-processing endpoint.

The API can also return rate-limit-related response information so clients can understand when they have reached their request limits.

Server-Side Validation

Another important part of the application is validation.

It is tempting to rely entirely on the frontend because the frontend already contains the form fields.

That is not enough.

Any client can send a request directly to an API endpoint without using the application's interface.

For this reason, the backend validates the incoming data using Zod before processing the image.

Validation can cover values such as:

  • Width
  • Height
  • Quality
  • Output format
  • Processing options
  • Upload-related data

This keeps invalid values from reaching the image-processing layer.

Handling Different Image Formats

Supporting multiple formats introduces another layer of complexity.

Different formats have different encoding characteristics and different use cases.

PNG is useful when lossless quality and transparency are important.

JPEG is widely supported and is often useful for photographic images where smaller file sizes are preferred.

WebP provides modern compression and is widely useful for web applications.

AVIF can provide strong compression efficiency and is useful when modern browser support and smaller assets are priorities.

Supporting all of these formats means the backend has to treat the requested output format as part of the processing configuration rather than simply changing a filename.

The Importance of Processing on the Backend

One of the major architectural decisions was keeping the primary image-processing logic on the server.

There are several reasons for this.

First, Sharp is designed for efficient server-side image processing.

Second, keeping processing on the backend makes it easier to control validation and resource usage.

Third, Cloudinary credentials and other server-side configuration must not be exposed to the browser.

The browser should essentially be responsible for collecting user input and displaying results, while the backend controls the actual processing pipeline.

Challenges I Faced

Building the project was not simply about calling Sharp's resize method.

Several parts required more thought.

Managing User Input

Width, height, quality, and output format all come from user-controlled input.

The application therefore needs to handle missing values, invalid values, unrealistic dimensions, and unsupported formats safely.

Preserving Aspect Ratio

Allowing users to freely modify both dimensions can easily result in distorted images.

Implementing aspect-ratio-aware resizing makes the application much more practical.

Balancing Quality and File Size

There is no universal quality setting that works perfectly for every image.

The application therefore exposes quality as a configurable option rather than forcing a single compression level.

Protecting the API

Because image processing can be expensive, rate limiting became an important part of the architecture rather than an optional afterthought.

Cloud Storage

Adding Cloudinary introduced another workflow that needed to be handled correctly: process the image first, upload the generated output, and then return the resulting hosted URL.

Keeping Validation Consistent

Client-side validation improves the user experience, but server-side validation is still mandatory.

Maintaining both layers requires careful thinking so that the API remains trustworthy without making the frontend unnecessarily complicated.

What I Learned

This project helped me understand that a seemingly simple utility application can involve many backend engineering concepts.

I gained practical experience working with Sharp and learned how server-side image transformations actually fit into an API workflow.

I also became more comfortable handling file uploads and processing binary data inside a Next.js application.

The project gave me experience integrating third-party services such as Cloudinary and Upstash while keeping their responsibilities separate from the core image-processing logic.

Another important lesson was API protection. Rate limiting is easy to overlook when building a small project, but it becomes important as soon as an endpoint performs expensive operations.

I also learned the difference between validating data in the browser for user experience and validating it on the server for security and correctness.

Architecture Overview

At a high level, the application can be divided into several responsibilities:

Frontend
   │
   ├── Image selection
   ├── Preview
   ├── Dimension controls
   ├── Quality controls
   └── Output configuration
          │
          ↓
      API Layer
          │
          ├── Validation
          ├── Rate Limiting
          └── Processing Configuration
                 │
                 ↓
               Sharp
                 │
                 ├── Resize
                 ├── Compress
                 └── Convert
                 │
                 ↓
          ┌──────┴──────┐
          ↓             ↓
       Download      Cloudinary

Keeping these responsibilities separated makes the application easier to reason about and gives each part a clear purpose.

Why This Project Is Useful

Although the application started as an image-resizing project, it demonstrates several concepts that are useful far beyond image processing.

It combines frontend interaction, API development, file handling, validation, third-party integrations, cloud storage, rate limiting, and server-side processing in a single project.

That makes it a practical example of how a relatively focused product can still require full-stack engineering knowledge.

Future Improvements

There are several areas that could be expanded in the future.

A batch-processing feature could allow users to upload multiple images and process them in one operation.

A target file-size mode could allow users to specify a desired output size, such as 100 KB, and automatically adjust compression parameters until the generated image is close to that target.

Additional image transformations such as cropping, rotation, sharpening, blur, and metadata removal could also be added.

A more advanced optimization mode could automatically choose an appropriate output format and compression level based on the source image.

These improvements would turn the project from a simple image utility into a more complete image optimization platform.

Final Thoughts

CWAD Image Resizer started with a straightforward idea: make image resizing and conversion simple.

But building it properly required much more than creating a form and calling an image-processing library.

The project became an opportunity to work with server-side file processing, image transformation, validation, rate limiting, cloud storage, and API design.

The biggest takeaway was that even small developer tools can become excellent engineering projects when the focus is placed on reliability, security, and a clean processing workflow.

CWAD Image Resizer is therefore not just an image resizing tool. It is also a practical demonstration of how different parts of a modern full-stack application can work together to solve a focused problem.

GitHub

© 2026 Codewithajoydas. All rights reserved.

Built with Next.js & Tailwind CSS