# Proxy — Design Patterns

Source: https://www.skillbyai.com/en/design-patterns/s-proxy

> Caching, lazy loading and access control.

## A stand-in with the same interface

**Intent:** provide a surrogate that controls access to another object while exposing the same interface. Common kinds: a **caching proxy** returns stored results instead of calling a slow service; a **virtual proxy** delays creating an expensive object until it is first used (lazy loading); a **protection proxy** checks permissions before forwarding; a **remote proxy** makes a network service look like a local object (RPC client stubs). Structurally a proxy resembles a decorator; the difference is intent: a proxy *controls access* to the subject, a decorator *adds behaviour*. JavaScript also has a built-in `Proxy` object that intercepts property access, used by some reactivity systems and ORMs. **Avoid** proxies that hide expensive network calls behind innocent-looking property reads.

## Stand-ins, trees and shared parts

More ways to compose objects: control access, model hierarchies, and separate or share state.

![Three ideas: proxies, composites, and a Bridge and Flyweight overview.](assets/figures/design-patterns/section-5-map.svg) — Figure 5.1 — Proxy, composite tree and shared flyweights.

## A caching and protection proxy

Same interface as the real product catalogue.

```typescript
interface ProductCatalog {
  getProduct(id: string): Promise<Product>;
}

class CachingCatalogProxy implements ProductCatalog {
  private cache = new Map<string, { value: Product; expires: number }>();
  constructor(private real: ProductCatalog, private ttlMs = 60_000) {}

  async getProduct(id: string) {
    const hit = this.cache.get(id);
    if (hit && hit.expires > Date.now()) return hit.value;
    const value = await this.real.getProduct(id);
    this.cache.set(id, { value, expires: Date.now() + this.ttlMs });
    return value;
  }
}

class AdminOnlyCatalogProxy implements ProductCatalog {
  constructor(private real: ProductCatalog, private user: User) {}
  getProduct(id: string) {
    if (!this.user.roles.includes('admin')) throw new ForbiddenError();
    return this.real.getProduct(id);
  }
}

const catalog: ProductCatalog = new CachingCatalogProxy(new HttpProductCatalog(baseUrl));
```

## Plan cache invalidation

A caching proxy needs a clear answer to "when does stale data become wrong?": a time-to-live, explicit invalidation on writes, or both.

**Quiz:** Which is an example of a virtual proxy?

- [ ] Converting one interface into another
- [x] Delaying loading a large image or object until it is first used
- [ ] Notifying subscribers of changes
- [ ] Copying an existing object

*Answer:* Delaying loading a large image or object until it is first used. Virtual proxies support lazy loading.
