Author: By Raj

Part of our Apps Script Web Apps guides. Need this built for your team? Hire a Google Apps Script developer.

Estimated reading time: 10 minutes

Build a Full CRUD Web App with Apps Script and Google Sheets

A CRUD web app treats Google Sheets as a lightweight database: Create appends rows, Read lists filtered data, Update sets values by primary key, Delete removes or soft-deletes with IsActive column.

Primary keys should be UUIDs generated with Utilities.getUuid(), not fragile row numbers after sorts. Soft delete preserves audit history finance teams expect.

Build UI with HtmlService per /blog/apps-script-html-service-guide and secure with /blog/apps-script-user-authentication.

Sheet schema for CRUD

Columns: id, createdAt, updatedAt, createdBy, fields..., deleted. Protect header row. Use data validation on enums.

Index scans by reading column A into a map id→rowNumber updated each Read if dataset modest.

Create and Update server functions

createRecord(data) appends row with UUID. updateRecord(id, patch) finds row via map and setValues on specific columns.

Use LockService during writes to prevent double-submit from double-click.

Delete strategies

Soft delete sets deleted=true and hides via filter views. Hard delete deleteRow only for GDPR erasure requests with admin role.

Archive sheet receives copy before hard delete.

UI table pagination

Client requests page size 50 with offset. Server returns { rows, total } to keep payloads small.

Sort server-side by reading all IDs if under few thousand rows, beyond that, split tabs by year.

Example code

function updateRecord(id, patch) {
  const lock = LockService.getScriptLock();
  lock.waitLock(10000);
  try {
    const sh = SpreadsheetApp.openById('SHEET_ID').getSheetByName('Data');
    const ids = sh.getRange('A2:A').getValues().flat();
    const idx = ids.indexOf(id);
    if (idx < 0) throw new Error('Not found');
    const row = idx + 2;
    if (patch.name) sh.getRange(row, 2).setValue(patch.name);
    sh.getRange(row, 4).setValue(new Date());
  } finally {
    lock.releaseLock();
  }
}
ApproachBest forTradeoff
Apps Script nativeGoogle Workspace-centric workflows6-min limit, quotas
Zapier / MakeNo-code, many connectorsPer-task cost, vendor lock-in
Python + CloudHeavy data / MLHosting cost, separate auth
Apps Script web app developmentProduction custom logicBuild cost, you own code

FAQ

Is Sheets a real database?

It is fine for internal tools under ~50k rows with low concurrency. Beyond that, consider Cloud SQL with Sheets as reporting export.

Concurrent edit conflicts?

LockService plus updatedAt checks, reject update if client stale updatedAt.

Can I expose REST JSON?

Yes via doGet/doPost returning JSON, same backend as HtmlService CRUD.

Attachment fields?

Store Drive file IDs in cells; upload via DriveApp in server function from base64 client payload.

Examples?

See /blog/apps-script-internal-tool-examples for intake portal patterns.

Need this done for you? I handle this as part of my consulting work, fixed-price quote within 24 hours.

Book a call with Raj →

Get the full Build a Full CRUD Web App with Apps Script and Google Sheets script template

I'll email you a production-ready, commented version you can deploy in 10 minutes.

Need help with this? I handle this as part of my Apps Script Web Apps service.

Internal tools, CRUD apps, HtmlService UIs, and Google SSO.

See how it works →