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
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.
RealImageshould 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
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 reloadBecause 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
| Kind | What it controls | Example |
|---|---|---|
| Virtual proxy | When the real object is created | Lazy-loading an image or a heavy module. |
| Protection proxy | Who is allowed to call | Rejecting a call unless the user has the right role. |
| Caching proxy | Whether the real work runs at all | Returning a stored result for a repeated request. |
| Remote proxy | Where the real object lives | A 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
| Use case | The proxy's job |
|---|---|
| Lazy loading | Images, routes, code-split components, database relations. |
| Access control | Checking a plan or role server-side before calling a service. |
| Caching | A repository wrapper serving repeated reads from Redis. |
| Rate limiting | Counting calls and refusing beyond a quota. |
| API clients | A local object whose methods make HTTP calls to a remote service. |
| Reactivity | Intercepting reads and writes on state to update the UI. |
| Test doubles | A stand-in that records how it was called. |
| Logging and tracing | Timing 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.
| Interface | Intent | |
|---|---|---|
| Proxy | Same | Control access or defer the cost, often manages the real object's lifecycle itself |
| Decorator | Same | Add behavior, stackable, wrapped by the client |
| Adapter | Different | Translate 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 andinstanceofmay 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
getandset. - "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 quizzesReading this page is the cheap half. These are the exercises that make you use it.
- ProxyProDefer an expensive load behind the real object's own interface and prove the caller can't tell the difference — the transparency this page insists on.ChallengeSolve · MediumChallengeSolve · Medium
- Design Patterns · Structural PatternsTen quick questions across Adapter, Decorator, Facade, Proxy, Bridge, Composite and Flyweight, to check the mental model before this one.QuizTake · MediumQuizTake · Medium
- Design Patterns · Structural Patterns — Series 3ProA second, tougher pass on the same structural family, for once the first set feels easy.QuizTake · MediumQuizTake · Medium
Read next
Decorator pattern
Stacking behavior onto one object at a time without subclassing, the layers it costs to read through when debugging, and the fixed wrap order every decorator must respect.
6 min
Facade pattern
Hiding a multi-step subsystem behind one simple call, the second thing to maintain the facade can become, and the wall it becomes once it is the only way in.
5 min