GAS Developer
Build Google Apps Script solutions for Spreadsheet automation, Gmail, Calendar, and Drive integration.
Instructions
Understand the requirement: What Google service? What automation? What trigger?
Choose the right approach:
Use Case Service Trigger Data processing Spreadsheet onEdit / time-driven Email automation Gmail time-driven Calendar sync Calendar time-driven / onChange File management Drive time-driven Form processing Forms onFormSubmit Web dashboard HTML Service doGet Write clean GAS code following best practices below.
Test thoroughly — use Logger.log() for debugging.
Document the setup — triggers, permissions, configuration.
Templates
Spreadsheet — Read/Write
function processSheet() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('Data');
const data = sheet.getDataRange().getValues();
// Skip header row
for (let i = 1; i < data.length; i++) {
const [name, email, status] = data[i];
if (status === '') {
// Process row
sheet.getRange(i + 1, 3).setValue('Processed');
}
}
}
Gmail — Send with Template
function sendTemplateEmail(to, subject, templateName) {
const template = HtmlService.createTemplateFromFile(templateName);
const htmlBody = template.evaluate().getContent();
GmailApp.sendEmail(to, subject, '', {
htmlBody: htmlBody,
name: 'Your Name'
});
}
External API Call
function callExternalApi(endpoint, apiKey) {
const options = {
method: 'get',
headers: { 'Authorization': 'Bearer ' + apiKey },
muteHttpExceptions: true
};
const response = UrlFetchApp.fetch(endpoint, options);
const code = response.getResponseCode();
if (code !== 200) {
Logger.log('API error: ' + code + ' ' + response.getContentText());
return null;
}
return JSON.parse(response.getContentText());
}
Time-Driven Trigger Setup
function createDailyTrigger() {
// Delete existing triggers for this function
const triggers = ScriptApp.getProjectTriggers();
triggers.forEach(t => {
if (t.getHandlerFunction() === 'dailyTask') {
ScriptApp.deleteTrigger(t);
}
});
// Create new daily trigger at 9 AM
ScriptApp.newTrigger('dailyTask')
.timeBased()
.atHour(9)
.everyDays(1)
.create();
}
Error Handling Pattern
function safeExecute(fn, context) {
try {
return fn();
} catch (error) {
Logger.log('Error in ' + context + ': ' + error.message);
// Optional: send error notification
GmailApp.sendEmail('admin@example.com',
'GAS Error: ' + context, error.stack);
return null;
}
}
Best Practices
- Batch operations: Use
getValues()/setValues()instead of cell-by-cell - Quota awareness: GAS has daily quotas (email: 100/day free, URL fetch: 20,000/day)
- Cache: Use
CacheServicefor expensive API calls - Properties: Store config in
PropertiesService(not hardcoded) - Lock: Use
LockServiceto prevent concurrent trigger execution
Security
- Never hardcode API keys — use
PropertiesService.getScriptProperties() - Validate user input — especially in web apps (doGet/doPost)
- Limit sharing — deploy as "Execute as me, accessible to specific users"
- Review permissions — GAS requests broad OAuth scopes; explain to client
Delivery Checklist
□ Code tested with sample data
□ Triggers documented and set up
□ Error handling in place
□ Setup instructions included
□ API keys stored in Properties (not code)
□ Permissions explained to client
Requirements
- Google account with Apps Script access
- No local dependencies — runs entirely in Google Cloud