← Back to publications
SoftwareArticleDesign Patterns

Proxy Design Pattern In JavaScript (TypeScript)

6 min read
design-patternsproxyjavascripttypescriptstructural-patterns
Abstract illustration representing the Proxy design pattern showing a client, proxy, and server connected in sequence

Photo by Brett Jordan on Unsplash

Table of Content

What is Proxy Pattern (Diagram)

By definition, the proxy pattern "in its most general form, is a class functioning as an interface to something else." [1]

Although this gives a relatable definition, An elaborate explanation is quite useful for profound understanding.

Proxy design pattern is one of the design patterns proposed by the Gang of Four (GoF) [1] back in 1994, it was classified as a structural pattern judging from its usage. The main purpose of proxy pattern is one, being a medium of communication between the client (the function/object requesting) and the server (the function/object distributing).

Although the pattern was discussed in OOP concept (by GoF), we would be discussing it using functional programming concept (with an example in OOP) in this article.

The client communicates with the proxy object which in turn forwards the request to the server with or without additional data/instruction and vice versa.

For example if the client wants to get a value from the server, the client would talk to the proxy then the proxy will pass the data/instruction to the server with/without addition of new data, if the server, in return, wants to send a response back to the client, it will have to send it first to the proxy which in turn would forward it to the client.

Publications site preview

Components of Proxy Pattern

Client

This is the function/object that makes request(s) to the proxy. The client can be a function, an expression.

  • Calls the proxy object
  • Sends request to the proxy object
  • Receives the response from the proxy object

Proxy Object

This is the function that serves as a medium of communication between the client and the server.

  • Receives the client request
  • Processes the client request
  • Sends the client's request to the server
  • Receives the server's response
  • Processes the server's response
  • Sends the server's response to the client

Server (Original Object)

This is the function from whom the proxy object gets the response for the client.

  • Receives the client's request from the proxy object
  • Sends the response to the proxy object

NB: Sometimes, the proxy does nothing to the client's data, and sends the request to the server directly, and also, sometimes, the proxy may not need to reach out to the server to get a response for the client depending on the implementation.

Types of Proxies

The types of proxies are classified based on their use case, example of which would be given in the examples section of this article. First, let's understand them [1].

Remote proxy

This type of proxy serves as the representative of a remote object. Suppose you need data that's not locally available on your current machine, a remote proxy is that object that helps you communicate with the remote object, fetch data from it and return the data to you.

An example is that of an ATM and the bank.

The ATM does not really have your account details, it only help you fetch those data from the bank so that you won't have to go to the bank to do what you want to do, whether it's to check your account balance, withdraw or send money, the POS machine also does something similar to this.

Virtual proxy

Virtual proxy prevents unnecessary creation of an object until it's necessary or needed. Think of virtual proxy as a mechanism used for managing resources (that's what it basically aimed to achieve).

When you think of lazy loading or some other mechanism that prevents the creation of an object or performance of an action until required, that's the function virtual proxy does.

Another function of virtual proxy is caching, which prevents repeated performance of a particular action (usually an heavy instruction), so instead of repeating that action, you can cache the response of the action and return it when called again.

Protection proxy

Just as the name suggests, protection proxy protects the object behind it from unauthorized/discouraged access, think of login in or signing up, the function of protection proxy is similar to this.

The proxy performs some validation before actually looking up or passing a request to the server (original object).

Implementation

Basic Structure

// Basic Structure

const OriginalObject = {};

const handler = {};

const proxyObject = new Proxy(OriginalObject, handler);

Example

Remote Proxy

// Remote Proxy
type BankType = {
  accountDetails: number;
  getAccountDetails: () => number;
  withdrawMoney: (amountToWidthdraw: number) => number;
};

const Bank: BankType = {
  accountDetails: 200,
  getAccountDetails() {
    return this.accountDetails;
  },
  withdrawMoney(amountToWithdraw: number) {
    this.accountDetails = this.accountDetails - amountToWithdraw;
    return amountToWithdraw;
  },
};

const bankProxyHandler = {
  get(target: BankType, key: keyof BankType) {
    const property = target[key];
    if (!property) {
      return;
    }
    if (typeof property !== "function") {
      return property;
    } else {
      return property.bind(target);
    }
  },
};

const bankRemoteProxy = new Proxy(Bank, bankProxyHandler);

console.log(`Account balance before withdraw - ${bankRemoteProxy.accountDetails}`);
// Account balance before withdraw - 200

console.log(`Withdrawing - ${bankRemoteProxy.withdrawMoney(20)}`);
// Withdrawing - 20

console.log(`Account balance after withdrawing - ${bankRemoteProxy.accountDetails}`);
// Account balance after withdrawing - 180

Virtual Proxy

class DataFetcher {
  constructor() {}

  getData = function (key: string) {
    if (key === "name") {
      return "Ibrahim";
    }
    if (key === "City") {
      return "User city";
    }
    return null;
  };
}

class GetUserDataProxy {
  dataFetcher: any = new DataFetcher();
  userDataCache: any = {};

  getUserData(key: string) {
    if (!this.userDataCache[key]) {
      console.log("has not exist");
      this.userDataCache[key] = this.dataFetcher.getData(key);
    }
    return this.userDataCache[key];
  }
}

const getUserDataProxy = new GetUserDataProxy();

console.log("..");
console.log("...1st calling..");
console.log(getUserDataProxy.getUserData("name"));

console.log("..");
console.log("...2nd calling..");
console.log(getUserDataProxy.getUserData("name"));

/*
-- Result --
..
...1st calling..
has not exist
Ibrahim
..
...2nd calling..
Ibrahim

*/

NB: The proxy design pattern does not necessarily have to be implemented with the Proxy keyword, you can implement using basic JavaScript concepts like function or class.

Use Cases

Validation

When you need to validate user input or selection.

Control Repetition

Control the repetition of function call and instead cache the result of that function.

Improve Response Time

With virtual proxy, you can improve the response time of your app instead of calling the server always, you can cache some regular requested data.

Pros of Proxy Pattern

Improve performance: Enlisting the aid of virtual proxy would allow you to reduce repetition of costly tasks.

Add Security: With protection proxy, you add a layer of security to your app.

Reduce Memory Consumption: With virtual proxy you'd reduce the instantiation of objects that are not required.

Cons of Proxy Pattern

Increase Complexity Due To Overhead: Using proxy increase complexity of the codebase due to the addition of extra layer of code.

Extra Time & Effort: Extra time and effort is required to add and implement proxy.

References