Last updated: Apr 29, 2026

Proxy

The Proxy pattern intercepts and controls interactions with a target object, allowing to perform some additional logic before or after the request reaches the target. This is useful, for example, for input validation, access control, lazy initialization, logging, and caching.

Example in TypeScript

For Proxy in JavaScript, there is a built-in Proxy constructor that pairs with the Reflect API:

const user = {
  name: "Lea",
  age: 32,
};

const handler: ProxyHandler<typeof user> = {
  get(target, property) {
    console.log(`Accessing "${property}"`);
    return Reflect.get(target, property);
  },

  set(target, property, value) {
    if (property === "age" && (typeof value !== "number" || value < 18)) {
      throw new TypeError("Age must be a number greater than 18");
    }
    return Reflect.set(target, prop, value);
  },
};

const proxiedUser = new Proxy(user, handler);

proxiedUser.name; // logs: Accessing "name"
proxiedUser.age = 33; // logs: Setting "age" to 33
proxiedUser.age = 17; // throws TypeError

Use Cases

  • Caching Proxy — stores and returns results of expensive operations on repeated calls.
  • Logging Proxy — records every property access or method call for debugging.
  • Validation Proxy — enforces constraints on values before they reach the target.

Trade-Offs

Executing handlers on every object interaction can lead to performance issues.

  • Middleware — applies a similar interception concept to request/response pipelines (e.g., Express middleware), but operates on a sequential chain of handlers rather than wrapping a single object.
  • Decorator — also wraps objects to extend behavior, but focuses on adding responsibilities rather than controlling access.