Back to Blog
Patterns2026-04-05

JavaScript Proxy API: Practical Patterns for Real-World Use

Master the JavaScript Proxy API with practical patterns including validation, logging, caching, and reactive objects.

What is the Proxy API?

The Proxy API lets you intercept and customize fundamental operations on objects — property access, assignment, function invocation, and more. Unlike simple wrappers, Proxy gives you fine-grained control over how objects behave, opening up patterns that are impossible with plain JavaScript objects.

A Proxy wraps a target object and defines "traps" — handler functions that intercept operations. The beauty is that the proxy is transparent: code using the proxy doesn't know it's not the original object.

Validation Proxy

One of the most practical uses is automatic input validation. Instead of scattering validation logic across your codebase, you can centralize it in a proxy.

``javascript function createValidatedObject(schema) { return new Proxy({}, { set(target, prop, value) { const validator = schema[prop]; if (validator && !validator(value)) { throw new TypeError(\Invalid value for \${String(prop)}: \${value}\); } target[prop] = value; return true; } }); }

const user = createValidatedObject({ age: (v) => typeof v === 'number' && v >= 0 && v <= 150, email: (v) => typeof v === 'string' && v.includes('@'), name: (v) => typeof v === 'string' && v.length > 0 });

user.name = 'Alice'; // OK user.age = 25; // OK user.age = -5; // TypeError: Invalid value for age: -5 `

Observable / Reactive Proxy

You can build a simple reactive system that triggers callbacks whenever data changes — the same idea behind Vue 3's reactivity.

`javascript function reactive(target, onChange) { return new Proxy(target, { set(obj, prop, value) { const oldValue = obj[prop]; obj[prop] = value; if (oldValue !== value) { onChange(prop, value, oldValue); } return true; }, deleteProperty(obj, prop) { const oldValue = obj[prop]; delete obj[prop]; onChange(prop, undefined, oldValue); return true; } }); }

const state = reactive({ count: 0 }, (prop, newVal, oldVal) => { console.log(\\${String(prop)} changed: \${oldVal} → \${newVal}\); });

state.count = 1; // "count changed: 0 → 1" `

Caching and Memoization Proxy

Wrap expensive function calls with a proxy that caches results automatically.

`javascript function memoize(fn) { const cache = new Map(); return new Proxy(fn, { apply(target, thisArg, args) { const key = JSON.stringify(args); if (cache.has(key)) return cache.get(key); const result = Reflect.apply(target, thisArg, args); cache.set(key, result); return result; } }); }

const expensiveCalc = memoize((n) => { console.log('Computing...'); return n * n; });

expensiveCalc(5); // "Computing..." → 25 expensiveCalc(5); // 25 (cached, no log) ``

These patterns show that Proxy is not just an academic API — it solves real problems elegantly and can replace entire libraries when used thoughtfully.