GAS PropertiesService: Scope, Validation, and Recovery for Small State
TECH INSIGHT / EXPLAINED
A few dozen settings do not require operating a database. PropertiesService is convenient for small string-based state. Its storage contract still needs to define execution identity, input types, concurrent changes, and size limits.
Local execution of the actual project
These captures show the existing project UI or functions executed locally with non-private sample inputs. Each caption states the scope of the execution.
Choose scope with execution identity
Script Properties are shared within the script; User Properties belong to its current executing user. Document Properties require an appropriate document context and are not an always-available store for standalone web apps. A web app executing as its deployer does not automatically create a separate User Properties scope for every visitor. Installable triggers also run as their creator.
Associate account identifiers with authenticated service users and use separate access functions for shared and personal settings. Keeping approval state in Script Properties does not implement authorization. Validate callers and writable keys at the storage API. When moving functions into a separate Apps Script library, check its resource-sharing rules too.
Check for missing values before numeric conversion
As strings, "9" > "10" is true, and Boolean("false") is also true. Number alone turns null or an empty string into zero. This example accepts only decimal ratios from 0.2% to 2%. That is an input-validation example, not a recommended strategy range.
function parseGap(raw) {
if (typeof raw !== 'string' || !/^\d+(\.\d+)?$/.test(raw.trim())) {
throw new Error('A decimal ratio is required');
}
const value = Number(raw.trim());
if (!Number.isFinite(value) || value < 0.002 || value > 0.02) {
throw new Error('Allowed range: 0.002 to 0.02');
}
return value;
}
console.assert(parseGap('0.002') === 0.002);
console.assert(parseGap('0.02') === 0.02);
Reject unknown ON/OFF values too. For JSON, validate arrays, fields, and schemaVersion after parsing. Surface corrupted state for recovery instead of silently replacing it with defaults and continuing trading.
Constrain partial updates and read responses
setProperties(params, false) preserves existing keys omitted from the update. Passing params through unchanged can still overwrite operator-only fields. Build a new object from an explicit writable-key list and validated values. Do not return every property to the UI or echo secrets in save responses.
Separating input names from storage keys reduces migrations when screen labels or ordering change. Names such as settings.v1 and state.lastSuccessAt express responsibilities, but naming does not enforce access control.
Serialized size matters more than 90 entries
The official September 2026 limits are 9KB per value and 500KB per property store, not one combined 500KB allowance for all project data. Even a 90-day array can exceed the per-value limit if its entries grow. Measure serialized UTF-8 bytes before writing and keep a margin below the limit.
A date key needs a clear account and timezone meaning. Replacing same-day snapshots and removing older entries creates a bounded history list. Export long-term history or per-fill audit records elsewhere and document retention and deletion.
Lock the full read-modify-write sequence
Every path updating the same history or configuration version must follow the same LockService convention. A lock failure is not a successful save. Batching fields into setProperties does not establish database transaction guarantees. Keep a state version and last known-good value for recovery from partial failures.
Reading once and reusing values within an execution reduces service calls, but do not assume batching bypasses quotas. CacheService should hold only reconstructible data. Hashing email addresses does not guarantee anonymity, and full property dumps do not belong in logs. As storage, querying, and concurrency requirements grow, a storage interface makes a database migration easier to plan.
A beginner UI that separates saved settings from execution state
References
Content and examples reviewed: 2026-09-29
댓글
댓글 쓰기