cv.cm

Coding · Design patterns

Proxy pattern explained with examples

5 min read

Short answer

Proxy is a structural design pattern that provides a substitute object with the same interface as a real object, so that the proxy can control access to it. The client talks to the proxy as if it were the real object, and the proxy decides when and how to forward each call. Common kinds are virtual proxies (lazy creation), protection proxies (access checks), caching proxies and remote proxies.

Intent and the problem it solves

Intent: provide a surrogate or placeholder for another object to control access to it. Proxy is one of the structural patterns in the Gang of Four (GoF) book.

Suppose an app calls a weather service over the network. Each call takes hundreds of milliseconds and costs money, and the same city is often requested many times per minute. You cannot change the service class, and you do not want to add caching code to every place that calls it. A proxy solves this: it implements the same interface, keeps a cache, and only calls the real service on a cache miss. Callers do not change at all.

The same idea covers other needs: create an expensive object only when it is first used, check permissions before a call, log every call, or hide the fact that the real object lives on another machine.

Structure and participants

RoleResponsibility
SubjectThe interface shared by the real object and the proxy, so the client can use either one
Real subjectThe object that does the actual work
ProxyImplements the subject interface, holds or creates a reference to the real subject, and adds control logic before or after forwarding
ClientWorks only with the subject interface and does not know whether it has a proxy
Kind of proxyWhat it controls
Virtual proxyDelays creating an expensive object until it is needed
Protection proxyChecks whether the caller is allowed to perform an operation
Caching proxyStores results and returns them for repeated requests
Remote proxyRepresents an object in another process or machine and handles the communication
Logging proxyRecords calls and arguments without changing the real object

TypeScript implementation: a caching proxy

WeatherService is the subject. CachedWeatherService is the proxy: it has the same method and forwards to the real service only when the cached value is missing or too old.

interface WeatherService {
  getTemperature(city: string): Promise<number>;
}
class RemoteWeatherService implements WeatherService {
  calls = 0;
  async getTemperature(city: string): Promise<number> {
    this.calls++; // imagine a slow HTTP request here
    return city.length * 3;
  }
}
class CachedWeatherService implements WeatherService {
  private cache = new Map<string, { value: number; time: number }>();
  constructor(private real: WeatherService, private maxAgeMs = 60_000) {}
  async getTemperature(city: string): Promise<number> {
    const hit = this.cache.get(city);
    if (hit && Date.now() - hit.time < this.maxAgeMs) return hit.value;
    const value = await this.real.getTemperature(city);
    this.cache.set(city, { value, time: Date.now() });
    return value;
  }
}
async function main() {
  const real = new RemoteWeatherService();
  const weather: WeatherService = new CachedWeatherService(real);
  await weather.getTemperature("Paris");
  await weather.getTemperature("Paris");
  console.log(await weather.getTemperature("Hanoi"), "real calls:", real.calls);
  // 15 real calls: 2
}
main();

The client variable is typed as WeatherService, so you can switch between the real service and the proxy in one place, for example in a dependency injection setup.

A virtual and protection proxy with JavaScript Proxy

JavaScript has a built-in Proxy object (ES2015) that intercepts operations such as property reads, writes and function calls through handler functions called traps. It is a convenient way to build a proxy without writing a class for each interface.

type Account = { owner: string; balance: number };
function readOnly<T extends object>(target: T): T {
  return new Proxy(target, {
    set(_obj, prop) {
      throw new Error("Cannot change " + String(prop) + ": read-only");
    },
  });
}
const account = readOnly<Account>({ owner: "Ana", balance: 100 });
console.log(account.balance); // 100
try {
  account.balance = 0;
} catch (e) {
  console.log((e as Error).message); // Cannot change balance: read-only
}

When to use it and when not to

  • Use it to add lazy loading, access control, caching, logging or remote access without changing the real class or its callers.
  • Use it when the real object is outside your control, such as a library class or a network client.
  • Do not use it when a plain function wrapper is enough. For one function, a higher-order function such as memoize(fn) is simpler than a class.
  • Do not hide slow or failing remote calls behind a proxy that looks local. Callers still need to handle latency and errors, so make that visible in the interface (for example by returning a Promise).
  • Be careful with identity: a proxy is a different object from the real subject, so proxy === real is false.

Real-world uses

  • Vue 3 builds its reactivity system on JavaScript Proxy: reading a property records a dependency and writing it triggers updates.
  • Java provides java.lang.reflect.Proxy to create dynamic proxy classes at runtime; frameworks use it to add transactions, security checks or remote calls around interfaces.
  • Object-relational mappers such as Hibernate return lazy proxies for related entities and load the real data from the database on first access.
  • Remote procedure call libraries generate client stubs that look like local objects but send each call over the network.

Related patterns

PatternHow it differs from Proxy
DecoratorSame structure, different goal: Decorator adds new behavior and is often stacked; Proxy controls access and usually manages the real object's life cycle.
AdapterAdapter gives an object a different interface; Proxy keeps exactly the same interface.
FacadeFacade offers a new, simpler interface to a whole subsystem; Proxy stands in for one object.
FlyweightFlyweight shares objects to save memory; a virtual proxy delays creating an object to save time and memory.

FAQ

What is the difference between Proxy and Decorator?

Both wrap an object with the same interface. A decorator adds responsibilities and is usually composed by the client, while a proxy controls access to the real object and often creates or manages that object itself.

Is the JavaScript Proxy object the same as the Proxy pattern?

It is a language feature that makes the pattern easy to implement. A JavaScript Proxy intercepts operations on a target object through traps, which is exactly what a proxy in the GoF sense does.

What is a virtual proxy?

A virtual proxy stands in for an object that is expensive to create, such as a large image. It creates the real object only when a method that needs it is first called.

Is a reverse proxy server an example of the Proxy pattern?

It follows the same idea at the network level: a server such as nginx sits in front of other servers with the same HTTP interface and adds caching, access control or load balancing. The GoF pattern describes the same idea for objects in one program.

All coding tutorials