TEXTBOOK SECTION / AI LEARNING

記事詳細ページを作成してWebアプリを公開する

AI活用概論の「GASとmicroCMSで作るシンプルなポートフォリオ」より、記事詳細ページを作成してWebアプリを公開するを解説。生成AI、AI活用、DX、業務改善を実践しながら学べるオンライン教材です。

5GASとmicroCMSで作るシンプルなポートフォリオ概論 / AI活用 / ChatGPT / Gemini / Claude / 基礎から学ぶ

OVERVIEW

この節で学べること

概要を表示する
項目内容
教材名AI活用概論
GASとmicroCMSで作るシンプルなポートフォリオ
記事詳細ページを作成してWebアプリを公開する
カテゴリ概論 / AI活用 / ChatGPT / Gemini / Claude / 基礎から学ぶ
学習内容生成AI、AI活用、DX、業務改善を実践しながら理解するための教材です。

TABLE OF CONTENTS

目次

CONTENT

ここから

この節では、記事一覧ページから選択された記事を1件取得し、詳細ページとして表示します。

最後に、完成したポートフォリオをGoogle Apps ScriptのWebアプリとして公開します。


URLクエリパラメータidを取得する

記事一覧ページでは、各記事のリンクにmicroCMSの記事IDを付けています。

<https://script.google.com/macros/s/デプロイID/exec?id=記事ID>

例えば、記事IDがclinic-reception-appの場合は次のURLになります。

<https://script.google.com/macros/s/デプロイID/exec?id=clinic-reception-app>

GASでは、doGet(event)event.parameterからURLの値を取得できます。

function doGet(event) {
  const articleId = String(
    event?.parameter?.id ?? '',
  ).trim();

  if (articleId) {
    return renderDetailPage(articleId);
  }

  return renderIndexPage();
}

event.parameter.idには、URLの?id=以降の値が入ります。

?id=clinic-reception-app
       ↓
event.parameter.id
       ↓
clinic-reception-app

idがある場合は詳細ページを表示し、ない場合は一覧ページを表示します。


renderDetailPage()で詳細ページを生成する

記事詳細ページは、renderDetailPage()で生成します。

function renderDetailPage(articleId) {
  const template =
    HtmlService.createTemplateFromFile('Detail');

  template.article = getArticle(articleId);

  return template
    .evaluate()
    .setTitle(template.article.title)
    .addMetaTag(
      'viewport',
      'width=device-width, initial-scale=1',
    );
}

この関数では、次の処理を行っています。

  1. Detail.htmlをテンプレートとして読み込む
  2. 記事IDを使ってmicroCMSから記事を1件取得する
  3. 取得した記事をtemplate.articleへ代入する
  4. HTMLテンプレートを評価する
  5. 記事タイトルをページタイトルに設定する
  6. スマートフォン表示用のviewportを設定する

microCMSの記事IDで1件取得する

記事詳細は、getArticle(articleId)で取得します。

function getArticle(articleId) {
  if (!/^[a-zA-Z0-9_-]+$/.test(articleId)) {
    throw new Error(
      '記事IDの形式が正しくありません。',
    );
  }

  const config = getMicroCmsConfig();

  const url = [
    `https://${config.serviceId}.microcms.io`,
    `/api/v1/${config.endpoint}/`,
    encodeURIComponent(articleId),
  ].join('');

  const response = fetchMicroCms(
    url,
    config.apiKey,
  );

  return parseJsonResponse(response);
}

記事一覧取得では、エンドポイントまでのURLを使用しました。

<https://サービスID.microcms.io/api/v1/blogs>

記事を1件取得する場合は、末尾に記事IDを追加します。

<https://サービスID.microcms.io/api/v1/blogs/記事ID>

例えば、次の設定の場合を考えます。

サービスID:tomoya-portfolio
エンドポイント:blogs
記事ID:clinic-reception-app

API URLは次のようになります。

<https://tomoya-portfolio.microcms.io/api/v1/blogs/clinic-reception-app>

記事IDを検証する

URLから取得した記事IDは、そのままAPI URLへ使用せず、形式を確認します。

if (!/^[a-zA-Z0-9_-]+$/.test(articleId)) {
  throw new Error(
    '記事IDの形式が正しくありません。',
  );
}

この正規表現では、次の文字だけを許可しています。

  • 半角英字
  • 半角数字
  • ハイフン
  • アンダースコア

さらに、URLへ追加するときはencodeURIComponent()を使用します。

encodeURIComponent(articleId)

外部から受け取った値を検証してから使用することで、想定外のURLが作られることを防ぎます。


Detail.htmlへ記事データを渡す

取得した記事データは、次のコードでHTMLテンプレートへ渡します。

template.article = getArticle(articleId);

microCMSから返される記事データは、次のようなオブジェクトです。

{
  id: 'clinic-reception-app',
  title: 'クリニック受付管理アプリ',
  description: '受付状況を管理するアプリです。',
  thumbnail: {
    url: '<https://images.microcms-assets.io/>...',
    width: 1280,
    height: 720
  },
  content: '<h2>開発の背景</h2><p>...</p>',
  publishedDate: '2026-07-13T00:00:00.000Z'
}

Detail.htmlでは、articleという変数名で各フィールドを使用できます。


タイトルを表示する

記事タイトルは、<?= ?>を使って表示します。

<h1><?= article.title ?></h1>

例えば、article.titleに次の値が入っている場合、

クリニック受付管理アプリ

HTMLには次のように表示されます。

<h1>クリニック受付管理アプリ</h1>

概要を表示する

記事の概要は、次のコードで表示します。

<p class="description">
  <?= article.description ?>
</p>

概要は一覧ページと詳細ページの両方で使用します。

詳細ページでは、本文を読む前に記事の内容を簡潔に伝える役割があります。


公開日を表示する

microCMSのpublishedDateは、formatDate()を使って日本語形式に変換します。

<p class="date">
  <?= formatDate(article.publishedDate) ?>
</p>

変換前の値は次のような形式です。

2026-07-13T00:00:00.000Z

表示結果は次のようになります。

2026年7月13日

formatDate()の処理はCode.gsに記述しています。

function formatDate(value) {
  const date = new Date(value);

  if (Number.isNaN(date.getTime())) {
    return '';
  }

  return Utilities.formatDate(
    date,
    'Asia/Tokyo',
    'yyyy年M月d日',
  );
}

サムネイル画像を表示する

サムネイル画像が登録されている場合だけ、<img>を表示します。

<? if (article.thumbnail && article.thumbnail.url) { ?>
  <img
    class="thumbnail"
    src="<?= article.thumbnail.url ?>?w=1280&h=720&fit=crop"
    alt="<?= article.title ?>"
  >
<? } ?>

画像が登録されているか、次の条件で確認しています。

article.thumbnail && article.thumbnail.url

画像URLには、microCMSの画像変換用パラメータを追加します。

?w=1280&h=720&fit=crop

これにより、画像を16:9の比率で表示します。


リッチエディタの本文を表示する

microCMSのリッチエディタで入力した本文は、HTML形式の文字列として返されます。

例えば、次のようなデータです。

<h2>開発の背景</h2>
<p>受付業務の負担を軽減するために開発しました。</p>

<h2>主な機能</h2>
<ul>
  <li>受付状況の表示</li>
  <li>患者情報の管理</li>
</ul>

この本文をDetail.htmlへ表示するには、次の構文を使います。

<div class="body">
  <?!= article.content ?>
</div>

<?!= article.content ?>の役割

GASのHTMLテンプレートには、主に3種類の構文があります。

構文役割
<? ?>JavaScriptの処理を実行する
<?= ?>値をHTMLへ安全に表示する
<?!= ?>HTMLとしてそのまま出力する

記事タイトルは通常の文字列なので、<?= ?>で表示します。

<h1><?= article.title ?></h1>

一方、リッチエディタ本文には<h2><p>などのHTMLタグが含まれています。

<?!= article.content ?>

<?= article.content ?>を使うと、HTMLタグが文章として表示される場合があります。

<h2>開発の背景</h2><p>受付業務を...</p>

<?!= article.content ?>を使うと、HTMLタグが解釈され、見出しや段落として表示されます。

注意点

<?!= ?>は、HTMLをそのまま出力するため、信頼できない内容には使用しません。

今回の本文は、自分がmicroCMSの管理画面から登録したコンテンツであることを前提としています。


リッチエディタ本文へCSSを適用する

本文内には、見出し、段落、画像、コード、引用などが含まれます。

次のようにCSSを設定します。

.body {
  margin-top: 48px;
}

.body h2 {
  margin-top: 2.2em;
  font-size: 28px;
}

.body h3 {
  margin-top: 2em;
  font-size: 22px;
}

.body p {
  margin: 1.4em 0;
}

.body img {
  max-width: 100%;
  height: auto;
  border-radius: 12px;
}

.body pre {
  overflow-x: auto;
  padding: 18px;
  border-radius: 10px;
  background: #202020;
  color: #ffffff;
}

.body blockquote {
  margin: 28px 0;
  padding-left: 18px;
  border-left: 3px solid #999999;
  color: #666666;
}

microCMSのリッチエディタが生成するHTMLに合わせて、.bodyの内側へスタイルを指定します。


一覧ページへ戻るリンクを作る

詳細ページの最後に、一覧ページへ戻るリンクを設置します。

<a
  class="back-link"
  href="<?= ScriptApp.getService().getUrl() ?>"
>
  ← 記事一覧へ戻る
</a>

ScriptApp.getService().getUrl()は、現在公開しているWebアプリのURLを返します。

idパラメータを付けていないため、doGet(event)では一覧ページが選択されます。

WebアプリURLへアクセス
  ↓
idパラメータなし
  ↓
renderIndexPage()
  ↓
記事一覧ページを表示

Detail.htmlの主要部分

詳細ページの主要なHTMLは次のとおりです。

<main class="container article">
  <p class="date">
    <?= formatDate(article.publishedDate) ?>
  </p>

  <h1><?= article.title ?></h1>

  <p class="description">
    <?= article.description ?>
  </p>

  <? if (article.thumbnail && article.thumbnail.url) { ?>
    <img
      class="thumbnail"
      src="<?= article.thumbnail.url ?>?w=1280&h=720&fit=crop"
      alt="<?= article.title ?>"
    >
  <? } ?>

  <div class="body">
    <?!= article.content ?>
  </div>

  <a
    class="back-link"
    href="<?= ScriptApp.getService().getUrl() ?>"
  >
    ← 記事一覧へ戻る
  </a>
</main>

詳細ページが表示されるまでの流れ

一覧ページから詳細ページが表示される流れは次のとおりです。

一覧ページの記事カードをクリック
  ↓
?id=記事ID付きのURLへ移動
  ↓
doGet(event)
  ↓
event.parameter.idを取得
  ↓
renderDetailPage(articleId)
  ↓
getArticle(articleId)
  ↓
microCMSから記事を1件取得
  ↓
template.articleへ代入
  ↓
Detail.htmlを評価
  ↓
記事詳細ページを表示

エラー時に確認する項目

401エラー

次のようなエラーが表示された場合です。

microCMSへの接続に失敗しました。HTTP 401

401は、認証に失敗したことを表します。

次の項目を確認します。

  • MICROCMS_API_KEYが正しいか
  • APIキーの前後に空白が入っていないか
  • 別のmicroCMSサービスのAPIキーを登録していないか
  • APIキーにGET権限があるか
  • スクリプトプロパティの名前が正しいか

スクリプトプロパティ名は、次の文字列と完全に一致させます。

MICROCMS_API_KEY

404エラー

次のようなエラーが表示された場合です。

microCMSへの接続に失敗しました。HTTP 404

404は、指定したAPIや記事が見つからないことを表します。

記事一覧で404になる場合は、次を確認します。

  • MICROCMS_SERVICE_IDが正しいか

  • MICROCMS_ENDPOINTが正しいか

  • microCMSにblogsAPIが存在するか 記事詳細で404になる場合は、さらに次を確認します。

  • URLの記事IDが正しいか

  • 記事が削除されていないか

  • 記事が公開済みか

  • 一覧取得後に記事の公開を終了していないか API URLを確認すると原因を切り分けやすくなります。

<https://サービスID.microcms.io/api/v1/blogs/記事ID>

フィールド名の不一致

microCMSのフィールドIDとコード内の名前が異なると、値を取得できません。

今回使用しているフィールドIDは次のとおりです。

title
description
thumbnail
content
publishedDate

例えば、microCMS側で公開日を次の名前にしている場合、

publishedAt

コード内のarticle.publishedDateでは取得できません。

<?= formatDate(article.publishedDate) ?>

microCMS側をpublishedDateへ変更するか、コード側を実際のフィールドIDへ合わせます。

<?= formatDate(article.publishedAt) ?>

表示名ではなく、フィールドIDが一致しているかを確認してください。


HTMLファイル名の大文字・小文字

GASのHTMLファイル名と、createTemplateFromFile()へ指定する名前は一致させます。

例えば、ファイル名が次の場合、

Detail.html

コードは次のようにします。

HtmlService.createTemplateFromFile('Detail');

次の組み合わせは一致していません。

ファイル名:detail.html
コード:createTemplateFromFile('Detail')

また、一覧ページでも同じ点に注意します。

現在のコードが次の場合、

HtmlService.createTemplateFromFile('index');

GAS上のファイル名も次である必要があります。

index.html

授業では、ファイル名を次のように統一すると分かりやすくなります。

Index.html
Detail.html

コードも同じ大文字・小文字に揃えます。

HtmlService.createTemplateFromFile('Index');
HtmlService.createTemplateFromFile('Detail');

GAS Webアプリをデプロイする

コードとHTMLが完成したら、Webアプリとして公開します。

Apps Script画面の右上にある「デプロイ」をクリックします。

デプロイ
→ 新しいデプロイ

歯車アイコンから、デプロイの種類として「ウェブアプリ」を選択します。

設定例は次のとおりです。

説明:
ポートフォリオ v1

次のユーザーとして実行:
自分

アクセスできるユーザー:
全員

「デプロイ」をクリックすると、初回はGoogleアカウントの権限確認が表示されます。

権限を許可すると、WebアプリのURLが発行されます。

<https://script.google.com/macros/s/デプロイID/exec>

このURLへアクセスすると、記事一覧ページが表示されます。


「次のユーザーとして実行」の意味

今回のWebアプリでは、次の設定を使用します。

次のユーザーとして実行:自分

閲覧者がアクセスした場合でも、microCMSへのAPI通信はWebアプリをデプロイした所有者の権限で実行されます。

そのため、閲覧者がスクリプトプロパティやAPIキーへ直接アクセスすることはありません。


「アクセスできるユーザー」の設定

一般公開するポートフォリオでは、次の設定を選びます。

アクセスできるユーザー:全員

Google Workspaceの設定やアカウントの種類によっては、表示される選択肢が異なる場合があります。

「全員」または「全員(匿名ユーザーを含む)」に相当する設定を選びます。


コード更新時に再デプロイする

GASでは、コードを保存しただけでは、公開中のWebアプリへ変更が反映されない場合があります。

コードを更新した後は、次の手順で新しいバージョンをデプロイします。

デプロイ
→ デプロイを管理
→ 公開中のデプロイを選択
→ 編集
→ バージョンを「新バージョン」に変更
→ デプロイ

同じデプロイを更新した場合、通常はWebアプリURLを変更せずに新しいコードを反映できます。


テスト用URLと公開用URLの違い

GASでは、テスト用のURLと公開用のURLが異なる場合があります。

テスト用URLは、一般的に次の形式です。

<https://script.google.com/macros/s/デプロイID/dev>

公開用URLは次の形式です。

<https://script.google.com/macros/s/デプロイID/exec>

/devは開発確認用、/execは公開用です。

授業で動作を確認するときは、現在どちらのURLを開いているか確認してください。


microCMSで追加した記事の反映を確認する

GASのコードを変更せず、microCMSへ新しい記事を追加します。

1. microCMSの管理画面を開く
2. blogs APIを選択する
3. 新しい記事を作成する
4. title、description、thumbnail、content、publishedDateを入力する
5. 記事を公開する
6. GASのWebアプリを再読み込みする

今回のコードはページへアクセスするたびにgetArticles()を実行するため、microCMSで公開した記事は一覧ページへ反映されます。

GASのコードを変更していない場合、再デプロイは不要です。

GASコードを変更した
→ 再デプロイが必要

microCMSの記事だけを変更した
→ 通常は再デプロイ不要

記事が反映されない場合

microCMSで追加した記事が表示されない場合は、次を確認します。

  • 記事が公開済みか
  • 公開日時が未来になっていないか
  • publishedDateが入力されているか
  • APIキーにGET権限があるか
  • orders=-publishedDateの対象フィールドが存在するか
  • ブラウザを再読み込みしたか
  • 正しいWebアプリURLを開いているか getArticles()では、次の順番指定を使用しています。
orders=-publishedDate

そのため、publishedDateがmicroCMS側に存在しない場合は、APIエラーになる可能性があります。


完成後のファイル構成

今回の最小構成は、次の3ファイルです。

Code.gs
index.html
Detail.html

ファイル名を統一する場合は、次の構成にします。

Code.gs
Index.html
Detail.html

その場合は、Code.gsも次のように変更します。

function renderIndexPage() {
  const template =
    HtmlService.createTemplateFromFile('Index');

  template.articles = getArticles();

  return template
    .evaluate()
    .setTitle('ポートフォリオ')
    .addMetaTag(
      'viewport',
      'width=device-width, initial-scale=1',
    );
}

完成したWebアプリの構成

記事一覧ページ
  ↓ 記事カードをクリック
?id=記事ID
  ↓
記事詳細ページ
  ↓ 「記事一覧へ戻る」
記事一覧ページ

コンテンツの管理はmicroCMSで行います。

microCMS
  ├─ 記事を作成
  ├─ サムネイルを登録
  ├─ 本文を編集
  └─ 公開
       ↓
GASがContent APIから取得
       ↓
ポートフォリオへ表示

到達目標

この節では、URLのidパラメータからmicroCMSの記事IDを取得し、該当する記事を詳細ページへ表示できることを目標とします。

また、リッチエディタ本文、サムネイル画像、公開日、一覧ページへ戻るリンクを含む詳細ページを完成させ、Google Apps ScriptのWebアプリとして公開できるようになります。

FAQ

よくある質問

記事詳細ページを作成してWebアプリを公開するは医療関係者向けだけの内容ですか。
医療分野の例が含まれる場合もありますが、医療関係者だけに限定した内容ではありません。生成AI、AI活用、DX、業務改善、プロトタイプ開発など、一般的なAI学習の事例として読める内容です。
AI初心者でも読めますか。
はい。AIをこれから学ぶ方、数学が苦手な方、仕事でAIを使いたい方にも読み進めやすいように、教材の章と節の流れに沿って整理しています。
サムネイル画像は必ず表示されますか。
はい。教材にcoverUrlが設定されている場合はその画像を表示し、未設定の場合は代替サムネイル画像を表示します。
AI活用概論のほかの章も読めますか。
はい。教材トップから章立てを確認でき、前後の節へもページ下部のナビゲーションから移動できます。