Proxy Design Pattern

The Proxy Pattern is a structural design pattern that provides a proxy (intermediary) to control access to an object. This proxy can perform additional operations such as lazy initialisation, access control, logging, security checks, or performance optimization before delegating the request to the actual object. It acts as a surrogate for another object, allowing controlled access to the original object.

Key characteristics of Proxy Pattern

  1. Encapsulation of Control Logic – The proxy acts as an intermediary and can enforce validation, security, and logging.

  2. Delays Object Instantiation (Lazy Initialisation) – The real object is created only when it's actually needed.

  3. Access Control & Security – Restricts direct access to the real object based on permissions or conditions.

  4. Performance Optimization – Can cache or optimize resource-heavy operations like database queries or API calls.

  5. Logging and Monitoring – Can log requests before passing them to the real object.

Benefits of Proxy Pattern

  1. Improved Performance The proxy reduces unnecessary calls to expensive operations (eg. API requests).

  2. Security & Access Control: The proxy ensures only valid requests reach the actual object.

  3. Lazy Initialization: The real object is created only when needed, saving memory and processing time.

  4. Abstraction & Decoupling: The client interacts with the proxy instead of directly dealing with the real object.

  5. Resource Management: Controls file access, network connections, or database queries efficiently.

UIN Form validation use-case using Proxy Pattern

Let us design a form for Unique Identification Number or UIN (Example:Passport or PAN in the Indian context) and upload an image. We will use the proxy pattern for the frontend form validation and API requests.

Step 1 : Requirements

  1. User Input

    1. The user enters a UIN (Example passport or PAN or GST - in Indian scenarios. It could be SSN in the United States).

    2. This UIN should be validated using Regex

  2. Backend validation

    1. The UIN must be checked against an API that returns whether it is trusted or permitted.

    2. If the UIN is invalid, the process stops.

  3. Image Upload Conditions:

    1. The image file size must be less than or equal to 1.5 MB

    2. If the image matches the step 1 requirement above, then it will be converted to base 64 string before upload. The image sizing limitation has been imposed as base 64 increases the image size by approximately 33%

    3. The API request will include both UIN and the base 64 string.

  4. Usage of Proxy Pattern

    1. The proxy acts a middleman between the frontend and the actual validation logic.

    2. It controls access to the API request and image upload, ensuring validation are met before proceeding.

    3. This helps in lazy initialization, access control and performance optimization by preventing unnecessary API calls and uploads.

Step 2: Application of Proxy Pattern

The proxy pattern acts as an interceptor to prevent invalid UINs and oversized images from API calls.Instead of calling validation and upload logic directly, we use a proxy class to delegate and restrict access.

Code Snippets

Step 1: Implementing the Proxy for UIN validation

//Step 1: Define an interface for UIN validation

interface UINValidator {
    validateUIN(uin: string): Promise<boolean>;
}

//Step 2: Implement the interface using a real validator for API calls

class ApiUINValidator implements UINValidator {

    private api: any = 'https://api.example.com/validateUIN';

    async validateUIN(uin: string): Promise<boolean> {
        // Call the API to validate the UIN

        try {
            const response = await fetch(`${this.api}?uin=${uin}`);

            if (!response.ok) {
                console.error(`API request failed with status: ${response.status}`);
                return false;
            }

            const data: { validListUINs: string[] } = await response.json();

            console.log(`Validating UIN: ${uin}`);
            return data.validListUINs.includes(uin);
        } catch (error) {
            console.error("Error while validating UIN: " + uin, error);
            return false;
        }

    }
}

//Step 3: Implement a proxy class to validate
class ProxyUINValidator implements UINValidator {

    private apiValidator: ApiUINValidator;
    private regexPattern: RegExp = /^[A-Z]{3}\d{7}$/; // Example UIN format: ABC1234567 of 10 characters

    constructor() {
        this.apiValidator = new ApiUINValidator();
      }

    async validateUIN(uin: string): Promise<boolean> {
        //step 3A: Validate the UIN using the regex pattern
        if (!this.regexPattern.test(uin)) {
            console.error(`Invalid UIN format: ${uin}`);
            return false;
        }

        //step 3B: If the UIN is in the correct format, call the API to validate it
        return await this.apiValidator.validateUIN(uin);
    }
}

Step 2: Image size validation after base 64 conversion

//step 1: Define an interface for image upload

interface ImageUploader{
    uploadImage(imagebase64: string): Promise<boolean>;
}

//step 2: Implement the interface using a real uploader for API calls
export class RealUploader implements ImageUploader {

    private uploadURL: string = "https://example.com/api/upload-image"; // Replace with actual API

    async uploadImage(base64Image: string): Promise<boolean> {
        // Call the API to upload base 64 image

        try {
            const response = await fetch(this.uploadURL, {
              method: "POST",
              headers: { "Content-Type": "application/json" },
              body: JSON.stringify({ image: base64Image }),
            });

            if (!response.ok) {
              console.error(`Upload failed with status: ${response.status}`);
              return false;
            }

            console.log(`Image uploaded successfully.`);
            return true;
          } catch (error) {
            console.error("Error during image upload:", error);
            return false;
          }
    }
}

//step 3: proxy image uploader - validates before upload
export class ProxyImageUploader implements ImageUploader {
    private realUploader: RealUploader;
    private MAX_SIZE_MB = 1.5; // Maximum allowed size (1.5 MB)

    constructor() {
        this.realUploader = new RealUploader();
    }

    async uploadImage(imagebase64: string): Promise<boolean> {
       //calculate size of base 64 image

       const imageSizeMB = (imagebase64.length * (3 / 4)) / (1024 * 1024);

       console.log(`Image size: ${imageSizeMB.toFixed(2)}MB`);

       // Check if size exceeds limit
       if (imageSizeMB > this.MAX_SIZE_MB) {
         console.error(`Error: Image exceeds the allowed ${this.MAX_SIZE_MB}MB limit.`);
         return false;
       }

       // Proceed with upload
       return await this.realUploader.uploadImage(imagebase64);

    }
}

Step 3: Combining both proxies

We can now combine ProxyUINValidator and ProxyImageUploader inside a main() function to simulate UIN validation followed by image upload.

import { ProxyUINValidator } from "./RegexValidator";
import { ProxyImageUploader } from "./UploadValidator";

async function main(){

    //we are integrating UIN and image validation
    const uinValidator = new ProxyUINValidator();
    const imageUploader = new ProxyImageUploader();

    //step 1: validate UIN
    const uinSample = "ABC1234567";

    const isUINValid = uinValidator.validateUIN(uinSample);

    if (!isUINValid) {
        console.error("UIN Validation Failed. Image Upload Not Allowed.");
        return;
      }


      console.log("UIN is valid. Proceeding to image upload...");

      // Step 2: Upload Image (Mock Base64)
  const smallImage = "iVBORw0KGgoAAAANSUhEUgAA..."; // Small image base64
  const largeImage = "iVBORw0KGgoAAAANSUhEUgA..." + "A".repeat(3 * 1024 * 1024); // Large image


  console.log("\n--- Attempting to Upload Small Image ---");
  console.log(await imageUploader.uploadImage(smallImage)); // Should upload

  console.log("\n--- Attempting to Upload Large Image ---");
  console.log(await imageUploader.uploadImage(largeImage)); 
}

main();

Final Workflow:

  1. User enters UIN → Validate regex + API check.

  2. If valid, allow image upload.

  3. Image upload validation → Ensure ≤ 1.5MB before API call.

  4. Upload to server if all checks pass.

Final Summary

Proxy Pattern Applied in Two Places:

  1. UIN Validation → First checks regex, then calls API.

  2. Image Upload → Ensures size ≤ 1.5MB before uploading.

Performance Benefits:

  • Lazy Execution: API calls only when required.

  • Access Control: Image upload allowed only if UIN is valid.

  • Optimization: Prevents unnecessary large file uploads.

The GITHUB Code Link is here

2
Subscribe to my newsletter

Read articles from Ganesh Rama Hegde directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ganesh Rama Hegde
Ganesh Rama Hegde

Passionate Developer | Code Whisperer | Innovator Hi there! I'm a senior software developer with a love for all things tech and a knack for turning complex problems into elegant, scalable solutions. Whether I'm diving deep into TypeScript, crafting seamless user experiences in React Native, or exploring the latest in cloud computing, I thrive on the thrill of bringing ideas to life through code. I’m all about creating clean, maintainable, and efficient code, with a strong focus on best practices like the SOLID principles. My work isn’t just about writing code; it’s about crafting digital experiences that resonate with users and drive impact. Beyond the code editor, I’m an advocate for continuous learning, always exploring new tools and technologies to stay ahead in this ever-evolving field. When I'm not coding, you'll find me blogging about my latest discoveries, experimenting with side projects, or contributing to open-source communities. Let's connect, share knowledge, and build something amazing together!