Build Your First Google Apps Script Web App

📘 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 point
  • HtmlService — serving HTML from Apps Script
  • google.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:

  1. Click ➕ → HTML
  2. 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

  1. User clicks Get Joke
  2. Browser runs loadJoke()
  3. google.script.run.getJoke() calls Apps Script
  4. Apps Script:
    • Calls the external API
    • Returns clean JSON
  5. Browser updates the UI

This pattern is the backbone of every Apps Script web app.


🚀 Step 4 — Deploy the Web App

  1. Click Deploy → New deployment
  2. Type: Web app
  3. Execute as: Me
  4. Who has access: Anyone
  5. Click Deploy
  6. 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.