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.