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.

Three adults explain personal and shared settings, history size, and locked updates
AI-generated conceptual illustration. Three adults explain personal and shared settings, history size, and locked updates

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.

The original GAS-console settings dialog opened with sample string values. DEMO-ACCOUNT is illustrative and key/secret fields are empty. This screenshot does not execute or verify PropertiesService writes, authorization, or locking.
The original GAS-console settings dialog opened with sample string values. DEMO-ACCOUNT is illustrative and key/secret fields are empty. This screenshot does not execute or verify PropertiesService writes, authorization, or locking.

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

댓글