Hướng dẫn

Apps Script Từ A Đến Z - Bài 1: Giới Thiệu và Các Khái Niệm Cơ Bản

Tuân HoangTuân Hoang
16 phút đọc
Apps Script Từ A Đến Z - Bài 1: Giới Thiệu và Các Khái Niệm Cơ Bản

Apps Script Từ A Đến Z - Bài 1: Giới Thiệu và Các Khái Niệm Cơ Bản

Chào mừng bạn đến với series Apps Script Từ A Đến Z — loạt bài hướng dẫn toàn diện nhất về Google Apps Script bằng tiếng Việt. Đây là bài mở đầu, nơi chúng ta sẽ xây dựng nền tảng vững chắc trước khi đi vào các kỹ thuật nâng cao hơn trong các bài tiếp theo.

Series này dành cho ai?
  • Người dùng Google Sheets muốn tự động hóa công việc
  • Developer biết JavaScript muốn mở rộng sang G Suite
  • Người đã quen VBA Excel muốn chuyển sang Google Workspace
  • Analyst muốn tạo dashboard và report tự động

Apps Script Là Gì?

Google Apps Script (viết tắt: GAS) là nền tảng scripting dựa trên JavaScript V8, được tích hợp sẵn trong hệ sinh thái Google Workspace. Nó cho phép bạn tự động hóa, mở rộng và kết nối các sản phẩm Google (Sheets, Docs, Drive, Gmail, Calendar, Forms...) với nhau và với dịch vụ bên ngoài qua API.

Ra mắt năm 2009 dưới tên "Google Apps Script" (trước khi Google Workspace đổi tên từ G Suite), GAS đã trải qua nhiều cột mốc quan trọng:

  • 2009: Ra mắt lần đầu, tích hợp vào Google Spreadsheets
  • 2011: Mở rộng sang Google Sites, Gmail add-ons
  • 2015: Hỗ trợ Google Forms và Calendar
  • 2017: Clasp CLI ra đời, cho phép phát triển local
  • 2020: Nâng cấp runtime lên V8 (ES6+ syntax support)
  • 2023-2027: Tích hợp Gemini AI, Advanced Services, Workspace Add-ons mới

Apps Script vs VBA Excel: Khi Nào Dùng Cái Nào?

Tiêu chí Google Apps Script VBA Excel
Ngôn ngữ JavaScript (ES6+) Visual Basic for Applications
Chạy trên Cloud (Google servers) Local máy tính
Collaboration Real-time, nhiều người Khó chia sẻ
API Integration UrlFetchApp, dễ dàng WinHttp, phức tạp hơn
Scheduled tasks Triggers tích hợp sẵn Cần Task Scheduler Windows
Performance Chậm hơn với file lớn Nhanh hơn với tính toán nặng
Cost Miễn phí (trong quota) Cần license Microsoft Office
Cross-platform Windows, Mac, Linux, Mobile Chỉ Windows

Kết luận: Nếu tổ chức bạn dùng Google Workspace, Apps Script là lựa chọn rõ ràng. Nếu vẫn dùng Excel On-premise, VBA vẫn có chỗ đứng. Ngày nay với xu hướng cloud, GAS đang dần thay thế VBA trong nhiều doanh nghiệp Việt Nam.

Cài Đặt IDE: Hai Cách Viết Apps Script

Cách 1: Online Editor (Apps Script IDE)

Cách đơn giản nhất để bắt đầu:

  1. Mở Google Sheets (hoặc Docs, Drive)
  2. Vào menu Extensions → Apps Script
  3. IDE mở ra tại script.google.com
  4. Code, Save, Run ngay trong trình duyệt

Online IDE hỗ trợ:

  • Syntax highlighting và autocomplete
  • Execution log real-time
  • Debugger với breakpoints
  • Version history
  • Deployment manager

Cách 2: Clasp CLI (Local Development)

Clasp (Command Line Apps Script Projects) cho phép phát triển Apps Script trên máy local với editor yêu thích (VS Code):

# Cài đặt clasp
npm install -g @google/clasp

# Đăng nhập Google account
clasp login

# Clone script có sẵn từ Google
clasp clone SCRIPT_ID

# Hoặc tạo project mới
clasp create --title "My Project" --type sheets

# Push code lên Google
clasp push

# Pull code từ Google về local
clasp pull

# Mở IDE online
clasp open

Ưu điểm của Clasp: TypeScript support, Git version control, IDE features đầy đủ (ESLint, Prettier), team collaboration qua GitHub.

Cấu Trúc Project Apps Script

File chính: Code.gs

Mỗi project có ít nhất một file .gs (Google Script). Khi tạo mới, file mặc định là Code.gs:

// Code.gs - File script chính
function myFunction() {
  // Code của bạn ở đây
  Logger.log('Hello, Apps Script!');
}

function onOpen() {
  // Chạy khi file mở
  SpreadsheetApp.getUi().createMenu('My Menu')
    .addItem('Run Task', 'myFunction')
    .addToUi();
}

File cấu hình: appsscript.json

File manifest quan trọng, kiểm soát permissions và cấu hình project:

{
  "timeZone": "Asia/Ho_Chi_Minh",
  "dependencies": {
    "libraries": []
  },
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "oauthScopes": [
    "https://www.googleapis.com/auth/spreadsheets",
    "https://www.googleapis.com/auth/drive",
    "https://www.googleapis.com/auth/gmail.send"
  ]
}

Libraries

Apps Script hỗ trợ libraries — tái sử dụng code giữa các project:

// Sử dụng library đã được add vào project
// (thêm qua Libraries + trong IDE)
const result = MyLibrary.myFunction(param1, param2);

// Tạo library của riêng bạn:
// 1. Tạo standalone script
// 2. Deploy as Library
// 3. Copy Script ID
// 4. Add vào project khác

Container-Bound vs Standalone Scripts

Container-Bound Script

Script được gắn với một file Google cụ thể (Sheets, Docs, Forms, Slides):

  • Tạo từ: Extensions → Apps Script trong file đó
  • Script chạy trong context của file cha
  • Tự động có quyền truy cập file cha
  • Không thể chia sẻ độc lập
  • Xóa file cha → script bị xóa
// Container-bound: truy cập Spreadsheet cha
function containerBoundExample() {
  // getActiveSpreadsheet() chỉ hoạt động trong container-bound
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getActiveSheet();
  const value = sheet.getRange('A1').getValue();
  Logger.log('Value in A1: ' + value);
}

Standalone Script

Script độc lập, không gắn với file nào:

  • Tạo từ: script.google.com → New Project
  • Linh hoạt hơn, có thể dùng như microservice
  • Phù hợp cho automation không cần UI
  • Có thể deploy làm Web App hoặc API
// Standalone: phải chỉ định file ID
function standaloneExample() {
  const SHEET_ID = '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms';
  const ss = SpreadsheetApp.openById(SHEET_ID);
  const sheet = ss.getSheetByName('Data');
  Logger.log('Rows: ' + sheet.getLastRow());
}

Authorization và OAuth2 Scopes

Apps Script sử dụng OAuth2 để xin quyền truy cập. Khi chạy script lần đầu, người dùng thấy popup xin phép:

Các Scope Phổ Biến

Scope Quyền truy cập
spreadsheets Đọc/ghi tất cả Spreadsheets
spreadsheets.readonly Chỉ đọc Spreadsheets
drive Truy cập toàn bộ Drive
gmail.send Gửi email qua Gmail
calendar Đọc/ghi Google Calendar
script.external_request Gọi API bên ngoài (UrlFetchApp)

Best practice: Luôn dùng scope hẹp nhất có thể. Thay vì spreadsheets (full access), dùng spreadsheets.currentonly nếu chỉ cần truy cập file hiện tại.

Logging: Logger.log vs console.log

function loggingExamples() {
  // Logger.log: hiển thị trong Execution Log (View → Logs)
  Logger.log('Simple message');
  Logger.log('Value: %s, Count: %d', 'hello', 42);
  Logger.log(JSON.stringify({ key: 'value' }));

  // console.log: hiển thị trong Stackdriver Logging
  // (View → Stackdriver Logging) - lưu lâu hơn
  console.log('This goes to Cloud Logging');
  console.warn('Warning message');
  console.error('Error occurred');

  // Xem log:
  // Logger.log → View → Logs (chỉ lưu trong session)
  // console.log → View → Stackdriver Logging (lưu 30 ngày)
  
  // Retrieve logs programmatically
  const logs = Logger.getLog();
  SpreadsheetApp.getActiveSheet().getRange('A1').setValue(logs);
}

Debugging với Breakpoints

Apps Script IDE hỗ trợ debugging trực quan:

  1. Click vào số dòng bên trái để đặt breakpoint (chấm đỏ xuất hiện)
  2. Nhấn nút Debug (biểu tượng bug) thay vì Run
  3. Execution dừng tại breakpoint
  4. Dùng panel bên phải xem giá trị variables
  5. Dùng Step Over (F10), Step Into (F11), Resume (F8)
function debugExample() {
  const data = [1, 2, 3, 4, 5];
  let sum = 0;
  
  for (let i = 0; i < data.length; i++) {
    sum += data[i]; // Đặt breakpoint ở đây để xem sum thay đổi
  }
  
  Logger.log('Total sum: ' + sum);
  return sum;
}

Runtime Limits: Quan Trọng Cần Nhớ

Giới hạn quan trọng nhất:
  • 6 phút — thời gian tối đa một script chạy liên tục
  • 30 giây — thời gian tối đa một custom function trong Sheets
  • 20MB — dung lượng data có thể đọc/ghi một lần
  • 50MB — response size từ UrlFetchApp
Giới hạn Consumer (Free) Workspace
Script runtime/ngày 90 phút 6 giờ
Trigger tối đa 20 20
UrlFetch calls/ngày 20,000 100,000
Email/ngày 100 1,500
Properties store 500KB 500KB

Best Practices Tránh Vượt Limit

// BAD: Đọc/ghi từng cell một (chậm, tốn quota)
function slowApproach() {
  const sheet = SpreadsheetApp.getActiveSheet();
  for (let i = 1; i <= 1000; i++) {
    const value = sheet.getRange(i, 1).getValue(); // 1000 API calls!
    sheet.getRange(i, 2).setValue(value * 2);      // 1000 API calls!
  }
}

// GOOD: Batch read/write (nhanh, tiết kiệm quota)
function fastApproach() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const data = sheet.getRange('A1:A1000').getValues(); // 1 API call
  const results = data.map(row => [row[0] * 2]);
  sheet.getRange('B1:B1000').setValues(results); // 1 API call
}

// GOOD: Checkpoint pattern cho script chạy lâu
function longRunningTask() {
  const props = PropertiesService.getScriptProperties();
  let startRow = parseInt(props.getProperty('lastRow') || '1');
  const sheet = SpreadsheetApp.getActiveSheet();
  const totalRows = sheet.getLastRow();
  
  const startTime = new Date().getTime();
  const TIME_LIMIT = 5 * 60 * 1000; // 5 phút, còn 1 phút buffer
  
  while (startRow <= totalRows) {
    // Xử lý batch 100 rows
    processRows(sheet, startRow, Math.min(startRow + 99, totalRows));
    startRow += 100;
    
    if (new Date().getTime() - startTime > TIME_LIMIT) {
      // Lưu checkpoint và thoát
      props.setProperty('lastRow', startRow.toString());
      Logger.log('Checkpoint saved at row: ' + startRow);
      return; // Trigger sẽ chạy lại từ checkpoint
    }
  }
  
  // Hoàn thành, xóa checkpoint
  props.deleteProperty('lastRow');
  Logger.log('Task completed!');
}

Hello World: Từng Loại Script

Hello World trong Spreadsheet

function helloSpreadsheet() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getActiveSheet();
  sheet.getRange('A1').setValue('Hello, Apps Script! 🎉');
  SpreadsheetApp.getUi().alert('Đã ghi vào A1!');
}

Hello World Web App

// Deploy → New deployment → Web App
function doGet(e) {
  const name = e.parameter.name || 'World';
  return ContentService
    .createTextOutput(JSON.stringify({ message: 'Hello, ' + name + '!' }))
    .setMimeType(ContentService.MimeType.JSON);
}

// Gọi API: https://script.google.com/macros/s/YOUR_ID/exec?name=SheetStore

Hello World Custom Function

/**
 * Chào hỏi với tên truyền vào
 * @param {string} name Tên người dùng
 * @return {string} Lời chào
 * @customfunction
 */
function XIN_CHAO(name) {
  return 'Xin chào, ' + name + '! Từ Apps Script 👋';
}
// Dùng trong sheet: =XIN_CHAO("Nguyễn Văn A")

Tổng Quan Series Apps Script Từ A-Z

Đây là roadmap các bài học sắp tới trong series:

Bài Chủ đề Nội dung chính
Bài 1 Giới thiệu (bài này) Khái niệm, IDE, structure, limits
Bài 2 SpreadsheetApp Đọc ghi dữ liệu, Range operations
Bài 3 Triggers Tự động hóa theo sự kiện và thời gian
Bài 4 GmailApp Gửi email, đọc inbox, tạo draft
Bài 5 DriveApp Quản lý file, folder trên Drive
Bài 6 UrlFetchApp Gọi REST API, webhook, Zalo/Telegram
Bài 7 HTML Service Tạo sidebar, dialog, web app UI
Bài 8+ Advanced Topics PropertiesService, CacheService, LockService...

Câu Hỏi Thường Gặp

Q: Apps Script có thể chạy khi tôi không online không?
A: Có! Triggers chạy trên Google servers, không cần máy tính bạn online. Ví dụ: trigger backup lúc 2h sáng vẫn chạy dù bạn đang ngủ.
Q: Apps Script có bị Google tính phí không?
A: Miễn phí cho tất cả Google Account. Workspace users có quota cao hơn. Chỉ tính phí nếu dùng Advanced Services tốn tiền (BigQuery, Maps API...).
Q: Tôi biết Python/PHP, học Apps Script có khó không?
A: Rất dễ nếu biết JavaScript. Nếu biết Python/PHP, mất khoảng 1-2 tuần để quen syntax JS và GAS API. Logic tư duy lập trình giống nhau.
Q: Script của tôi có bị người khác nhìn thấy không?
A: Script trong file Sheets chỉ thấy được khi có quyền edit file. Standalone script mặc định chỉ bạn thấy. Web App có thể public nhưng code vẫn private.
Bài tiếp theo: Bài 2 — SpreadsheetApp: Đọc, Ghi và Xử Lý Dữ Liệu
Chúng ta sẽ deep dive vào SpreadsheetApp — trái tim của mọi Apps Script làm việc với Google Sheets. Tất cả Range operations, batch processing, và ví dụ thực tế.

Chia sẻ bài viết:

Tuân Hoang

Tuân Hoang

Đội ngũ SheetStore

Bạn thấy bài viết hữu ích?

Đăng ký nhận thông báo khi có bài viết mới.

Nhận thông báo khi có bài viết mới. Không spam, hứa luôn! 😊

Bình luận (0)

Vui lòng đăng nhập để tham gia thảo luận