codejump

AcademyDesign Patterns

Proxy pattern

Controlling access to, or deferring the cost of, an object behind its own interface, the extra hop easy to forget, and the moment it carries logic and becomes a Decorator.

7 min readUpdated

3 exercises

Waiting at the end of this page.

Proxy looks almost identical to Adapter and Decorator, an object standing in front of another behind the same interface, but exists to control access to, or defer the cost of, the real object, not to translate or extend it.

A Proxy wraps an object behind the exact same interface the object already has, so every call still looks identical to the caller, and adds a check, a cache, or a deferred load in front of the real work instead of running it immediately. Where an Adapter translates a mismatched interface and a Decorator adds new behavior, a Proxy changes neither: it controls when, or whether, the real call actually happens.

The problem it solves

Your page shows a gallery of 200 high-resolution images. Creating a RealImage loads the whole file from disk or the network. If the gallery creates all 200 up front, the page freezes for seconds, even though the user sees only the first four.

  • The cost is paid before it is needed. Most of the images may never be displayed.
  • The caller should not have to care. The gallery code just calls display() and wants it to work.
  • Loading logic should not leak into the image class. RealImage should stay simple.

The fix is a stand-in with the same interface that creates the real image only the first time someone actually asks it to display.

Structure

The client calls display() as usual. The proxy creates the real image only on the first call and reuses it after
The client calls display() as usual. The proxy creates the real image only on the first call and reuses it after

Three parts:

  • The subject interface (display()): shared by the real object and the proxy.
  • The real subject (RealImage): does the actual, possibly expensive, work.
  • The proxy (ImageProxy): holds a reference to the real subject, or creates it lazily, and decides when and whether to forward calls.
class RealImage {
  constructor(filename) {
    this.filename = filename;
    this.loadCount = 0;
    this.load();
  }
  load() { this.loadCount += 1; }
  display() { return 'displaying:' + this.filename; }
}

class ImageProxy {
  constructor(filename) {
    this.filename = filename;
    this.realImage = null;
  }
  display() {
    if (!this.realImage) this.realImage = new RealImage(this.filename);
    return this.realImage.display();
  }
}

// new ImageProxy('cat.png')       → nothing loaded yet
// .display()  1st call            → creates RealImage, loads, displays
// .display()  2nd call            → reuses the same RealImage, no reload

Because the proxy exposes the same interface, the caller cannot tell the difference. Swapping a RealImage for an ImageProxy requires no change in the gallery code.

The four classic kinds

KindWhat it controlsExample
Virtual proxyWhen the real object is createdLazy-loading an image or a heavy module.
Protection proxyWho is allowed to callRejecting a call unless the user has the right role.
Caching proxyWhether the real work runs at allReturning a stored result for a repeated request.
Remote proxyWhere the real object livesA local object that forwards calls to a server.

A fifth is often added: a logging or metrics proxy, recording every call before forwarding it.

Where it is used in real systems

Vue 3 wraps state in a JavaScript Proxy. Reads register a dependency, writes notify subscribers, and both reach the real object
Vue 3 wraps state in a JavaScript Proxy. Reads register a dependency, writes notify subscribers, and both reach the real object
Use caseThe proxy's job
Lazy loadingImages, routes, code-split components, database relations.
Access controlChecking a plan or role server-side before calling a service.
CachingA repository wrapper serving repeated reads from Redis.
Rate limitingCounting calls and refusing beyond a quota.
API clientsA local object whose methods make HTTP calls to a remote service.
ReactivityIntercepting reads and writes on state to update the UI.
Test doublesA stand-in that records how it was called.
Logging and tracingTiming and recording calls without touching the real code.

Proxies you already use

  • The JavaScript `Proxy` object. new Proxy(target, { get, set }) is the pattern built into the language. Vue 3 reactivity is built on it.
  • Lazy relations in ORMs. Some ORMs return a proxy for a related record and only query the database when you read a field from it.
  • CDN and reverse proxies. Nginx or Cloudflare stand in front of your server, cache responses and shield it, while keeping the same HTTP interface.
  • `React.lazy`. A stand-in for a component that loads its code on first render.
  • tRPC and generated API clients. Local method calls that are forwarded over the network.
  • Prisma client extensions and query middleware, which intercept operations before they reach the database.

Proxy, Decorator and Adapter

All three wrap an object. Interviewers ask for the difference constantly.

InterfaceIntent
ProxySameControl access or defer the cost, often manages the real object's lifecycle itself
DecoratorSameAdd behavior, stackable, wrapped by the client
AdapterDifferentTranslate an incompatible interface

The interface alone does not tell Proxy from Decorator. Look at the intent: a Proxy decides whether and when the real object does its work, a Decorator makes it do more.

When not to use it

  • The real object is cheap and always needed. A proxy would only add a hop.
  • The caller must know it is a proxy. If callers need to handle "not loaded yet" states, the abstraction leaks. Expose that explicitly instead of hiding it.
  • Identity checks. A proxy is not the same object as its target, so === comparisons and instanceof may surprise you.
  • Debugging. A stack trace passes through the proxy, and behavior that seems to come from nowhere can be hard to trace.
  • Security by proxy alone. A protection proxy is only as good as the guarantee that nobody can reach the real object another way. Check access on the server, not just in the proxy layer.

What interviewers ask

  • "What is the Proxy pattern?" A stand-in with the same interface that controls access to another object.
  • "Types of proxy?" Virtual (lazy), protection (access), caching, remote.
  • "Proxy versus Decorator?" Same interface. Proxy controls access and lifecycle, Decorator adds behavior.
  • "How is the JavaScript `Proxy` used in practice?" Reactivity in Vue, validation, logging and default values through traps like get and set.
  • "How would you lazy-load something expensive?" A virtual proxy that creates the real object on first use.

The mistake to avoid

A Proxy that changes the interface or the result. The whole promise is that the caller cannot tell the difference. A proxy that returns a placeholder value, swallows an error or changes a method signature has become an Adapter or a Decorator, and callers written against the real object may break. Forward faithfully and only decide when, or whether, to do the work.

The mechanism above, an object standing in for another behind the same interface, is the same in JavaScript, Python or Java. Only the syntax changes.

Now practice it

1 challenge · 2 quizzes

Reading this page is the cheap half. These are the exercises that make you use it.