google-apps-script
Use when automating Gmail, Drive or Sheets with Google Apps Script developed locally in TypeScript with clasp — minimal OAuth scopes, time-driven triggers, quotas, idempotent daily jobs, deploying the
SKILL.mdskills/tech/google-apps-script/SKILL.md
GitHubGoogle Apps Script with clasp
Default: TypeScript in a git repository, clasp to push, pure logic in modules tested with Node, Apps Script services touched only in thin adapters, explicit minimal scopes in appsscript.json, triggers created by code.
Setup
npm i -D @google/clasp typescript
npx clasp login # per Google account
npx clasp create --type standalone --title "my-automation"
npx clasp push
- Keep
.clasp.json(script ID) per account/deployment, e.g. one file per account that a deploy script copies into place beforepush. - Recent clasp versions do not transpile TypeScript themselves; compile with
tsc(or a bundler) to plain JS first and push the output.
Scopes
Declare scopes explicitly in appsscript.json (oauthScopes) — e.g. read-only mail access plus permission to send mail — rather than letting the editor infer broad ones. Narrow scopes are enforcement: the script cannot delete what it cannot write.
Triggers and idempotency
- Create time-driven triggers from a
setup()function (ScriptApp.newTrigger("run").timeBased().atHour(7).everyDays(1).create()), deleting existing ones first so reruns do not duplicate. - Make daily jobs idempotent: record "sent for date D, target T" in
PropertiesService, and on rerun send only what is missing. - Provide a one-time
setupAccount()that the account owner runs once in the editor: it grants consent, installs triggers and sends a first result. Google requires the owner's interactive consent; it cannot be automated away.
Quotas
- Services have daily and per-minute quotas (e.g. Gmail reads). Search with narrow queries, fetch only needed fields, and process in pages with
Utilities.sleepbetween batches. - Each execution has a maximum run time; split long work across trigger runs and keep a cursor in properties.
Mail delivered to yourself
When sending a digest from an account to an address that forwards back to the same mailbox, the provider may keep only the sent copy and nothing appears in the inbox. Send to the mailbox you actually read, and include a distinct subject per logical recipient so each digest can be identified and audited.
Testing
- Keep classification, formatting and date logic in pure functions; test with Node's test runner using fixtures shaped like Apps Script objects.
- Add an audit function that lists what was sent today and what is missing, and run it after each deploy.
Verification loop
tscbuilds; unit tests pass.clasp push; runsetup()once; check triggers in the project's trigger list.- Run the job manually; confirm the result arrives where expected and a second run sends nothing new.
Pitfalls
- Trigger owner: triggers run as the user who created them; deploying under another account needs that account's login and consent.
- Time zone: set
timeZoneinappsscript.json; date math otherwise uses the project default. - Silent failures: enable failure notifications for triggers and log to the execution log.
- Global code runs on every invocation; keep top-level statements free of side effects.