📘 Lesson 3 — Build Your First Google Apps Script Web App (HTML + APIs)
Blog Post #3 in the Apps Script + APIs + Gemini Series
Up until now, you’ve been running Apps Script functions from the editor or triggering them from Sheets and Docs.
In this lesson, everything changes.
You’ll build a real web app — with buttons, UI, and live API data — using Google Apps Script + HTML.
This is the exact foundation used for:
- AI dashboards
- Internal tools
- Gemini-powered assistants
- Lightweight apps hosted on Google’s infrastructure
🎯 What You’ll Build
You will create:
✅ A deployed Apps Script web app
✅ An HTML interface with a button
✅ Client-side JavaScript
✅ Server-side Apps Script
✅ A live API-powered UI
By the end, you’ll understand how Apps Script becomes a full-stack platform.
🧠 Key Concepts Introduced
doGet(e)— web app entry pointHtmlService— serving HTML from Apps Scriptgoogle.script.run— calling server functions from the browser- Client ↔ Server separation
- Safe API handling for UI apps
📍 Step 1 — Create the Server-Side Apps Script
Open your Apps Script project and add the following code.
/**
* Lesson 3: Web App entry point
*/
function doGet(e) {
return HtmlService.createHtmlOutputFromFile('index')
.setTitle('Joke Web App');
}
🔍 What This Does
doGet()runs when the web app URL is opened- It loads an HTML file named
index.html - This is how Apps Script serves web pages
📍 Step 2 — Add the API Logic (Server-Side)
Now add the function that calls the external API.
/**
* Fetch a joke from an external API
* and return structured data to the client.
*/
function getJoke() {
var url = 'https://official-joke-api.appspot.com/jokes/random';
var options = {
method: 'get',
muteHttpExceptions: true
};
var response = UrlFetchApp.fetch(url, options);
var status = response.getResponseCode();
if (status !== 200) {
return {
ok: false,
error: 'API error: ' + status
};
}
var data = JSON.parse(response.getContentText());
return {
ok: true,
setup: data.setup || 'No setup available',
punchline: data.punchline || 'No punchline available'
};
}
🔍 Why This Pattern Matters
- The client never calls external APIs directly
- The server controls:
- API calls
- Error handling
- Data shape
- This is critical for Gemini integrations later
📍 Step 3 — Create the HTML File
In the Apps Script editor:
- Click ➕ → HTML
- Name the file: index
Paste in the following:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<meta charset="UTF-8">
<title>Joke Web App</title>
<style>
body {
font-family: system-ui, sans-serif;
padding: 2rem;
}
button {
padding: 0.6rem 1.2rem;
font-size: 1rem;
cursor: pointer;
}
.output {
margin-top: 1.5rem;
font-size: 1.1rem;
}
.error {
color: #b00020;
}
</style>
</head>
<body>
<h1>Random Joke Generator</h1>
<button onclick="loadJoke()">Get Joke</button>
<div id="output" class="output"></div>
<script>
function loadJoke() {
var output = document.getElementById('output');
output.textContent = 'Loading...';
google.script.run
.withFailureHandler(function(err) {
output.textContent = 'Script error: ' + err.message;
output.className = 'output error';
})
.withSuccessHandler(function(result) {
if (!result.ok) {
output.textContent = result.error;
output.className = 'output error';
return;
}
output.textContent =
result.setup + ' — ' + result.punchline;
output.className = 'output';
})
.getJoke();
}
</script>
</body>
</html>
🔁 How Client ↔ Server Communication Works
- User clicks Get Joke
- Browser runs
loadJoke() google.script.run.getJoke()calls Apps Script- Apps Script:
- Calls the external API
- Returns clean JSON
- Browser updates the UI
This pattern is the backbone of every Apps Script web app.
🚀 Step 4 — Deploy the Web App
- Click Deploy → New deployment
- Type: Web app
- Execute as: Me
- Who has access: Anyone
- Click Deploy
- Open the provided URL
🎉 You now have a live web app hosted by Google.
🧪 Exercises to Level Up
Exercise 1 — Add a “Save to Sheet” Button
Add a second button that saves the joke.
Server-side:
function saveJoke(setup, punchline) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Jokes') || ss.insertSheet('Jokes');
sheet.appendRow([new Date(), setup, punchline]);
}
Client-side idea:
- Store the last joke
- Send it to
saveJoke()
Exercise 2 — Improve UX
- Disable the button while loading
- Show a spinner or emoji
- Display success feedback
Exercise 3 — Add Input
Replace the joke API with a search-based API and allow user input.
This prepares you for prompt-based AI tools.
💡 Pro Tips for Apps Script Web Apps
Tip 1 — Server Handles All Secrets
Never expose API keys in HTML.
Tip 2 — Keep Return Objects Simple
Always return plain objects { ok, data, error }.
Tip 3 — Reuse This Pattern for Gemini
The Gemini API fits perfectly into this architecture.
🔮 What’s Next?
In Lesson 4, you’ll learn how to properly handle API keys and secrets using:
PropertiesService- Secure configuration
- Environment-style setups
This is mandatory before integrating Gemini.