How the Proxy pattern controls access to an object, with two examples from Odoo's JavaScript: the localization object and reactive records in the mail store.
If you work with JavaScript code in Odoo, you may come across Proxy. Proxy is a structural design pattern that provides a substitute for another object. A proxy controls access to the original object and can run extra logic before or after a request reaches it. Think of a security guard at a building entrance, who checks credentials before letting anyone in.
Overview of the Pattern
Sometimes accessing an object needs extra logic: delaying initialization, restricting access, caching results or handling other concerns. Changing the original object directly is not always a good idea, because it complicates the code and breaks the single responsibility principle.
The Proxy pattern solves this with a proxy object that has the same interface as the original. Clients work with the proxy without knowing it isn’t the real object, and the proxy manages access to the original, adding logic before or after it delegates the request.
How to Implement It in JavaScript
JavaScript has a built-in Proxy object for this pattern. It takes two arguments:
- Target: the original object.
- Handler: an object that defines the custom behavior, through traps such as
getandset.
Example 1: Access Control in localization
In the web module, localization
is a proxy object that holds the user’s localization settings:
export const localization = new Proxy(
{},
{
get: (target, p) => {
// "then" can be called implicitly if the object is returned in an
// `async` function, so we need to allow it.
if (p in target || p === "then") {
return Reflect.get(target, p);
}
throw new Error(
`could not access localization parameter "${p}": parameters are not ready yet. Maybe add 'localization' to your dependencies?`
);
},
}
);
Its properties are filled in later by the localization service :
Object.assign(localization, {
dateFormat,
timeFormat,
dateTimeFormat: `${dateFormat} ${timeFormat}`,
decimalPoint: userLocalization.decimal_point,
direction: userLocalization.direction,
grouping: JSON.parse(userLocalization.grouping),
multiLang: result.multi_lang,
thousandsSep: userLocalization.thousands_sep,
weekStart: userLocalization.week_start,
});
When code reads a property such as localization.dateFormat, the proxy intercepts the access. If the property has not been set yet, it throws an error instead of silently returning undefined, so bugs caused by uninitialized data surface early. The built-in Reflect API makes it simple to forward the access to the target object.
Example 2: Reactivity and Lazy Computation in mail
In the mail module, records in the store
are wrapped in proxies. The get trap handles lazy computation:
get(record, name, recordFullProxy) {
// ...
if (Model._.fieldsCompute.get(name) && !Model._.fieldsEager.get(name)) {
record._.fieldsComputeInNeed.set(name, true);
if (record._.fieldsComputeOnNeed.get(name)) {
record._.compute(record, name);
}
}
// ...
return Reflect.get(record, name, recordFullProxy);
},
When a computed field is not eager, the trap marks it as needed and computes its value only when it is read, which avoids unnecessary work.
The set trap is where it gets interesting:
set(record, name, val, receiver) {
// ...
return store.MAKE_UPDATE(function recordSet() {
// ...
store._.updateFields(record, { [name]: val });
// ...
return true;
});
},
Instead of assigning the value directly, it runs the update inside store.MAKE_UPDATE
, which batches several changes into a single update cycle so the UI can react to data changes efficiently.
Pros and Cons
Pros:
- Extra functionality: validation, security checks or caching can be added without changing the original code.
- Lazy initialization: expensive objects or values are created only when they are needed.
- Separation of concerns: add-on behavior stays separate from core business logic.
Cons:
- More complexity: the extra layer can make debugging harder.
- Performance overhead: every intercepted operation adds a small cost.
Conclusion
The Proxy pattern gives Odoo’s JavaScript code a way to control and extend how objects behave without modifying the objects themselves. Whether for lazy computation, validation or guarding data that is not ready yet, as with localization, proxies help keep the code robust and maintainable.
References
- Patterns.dev, Proxy Pattern .