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
Encapsulation of Control Logic – The proxy acts as an intermediary and can enforce validation, security, and logging.
Delays Object Instantiation (Lazy Initialisation) – The real object is created only when it's actually needed.
Access Control & Security – Restricts direct access to the real object based on permissions or conditions.
Performance Optimization – Can cache or optimize resource-heavy operations like database queries or API calls.
Logging and Monitoring – Can log requests before passing them to the real object.
Benefits of Proxy Pattern
Improved Performance The proxy reduces unnecessary calls to expensive operations (eg. API requests).
Security & Access Control: The proxy ensures only valid requests reach the actual object.
Lazy Initialization: The real object is created only when needed, saving memory and processing time.
Abstraction & Decoupling: The client interacts with the proxy instead of directly dealing with the real object.
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
User Input
The user enters a UIN (Example passport or PAN or GST - in Indian scenarios. It could be SSN in the United States).
This UIN should be validated using Regex
Backend validation
The UIN must be checked against an API that returns whether it is trusted or permitted.
If the UIN is invalid, the process stops.
Image Upload Conditions:
The image file size must be less than or equal to 1.5 MB
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%
The API request will include both UIN and the base 64 string.
Usage of Proxy Pattern
The proxy acts a middleman between the frontend and the actual validation logic.
It controls access to the API request and image upload, ensuring validation are met before proceeding.
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:
User enters UIN → Validate regex + API check.
If valid, allow image upload.
Image upload validation → Ensure ≤ 1.5MB before API call.
Upload to server if all checks pass.
Final Summary
Proxy Pattern Applied in Two Places:
UIN Validation → First checks regex, then calls API.
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
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!