更新頻度が低い小規模なWebサイトにはWordPressだとオーバースペックに…また、クライアントによってはWordPress管理画面での操作がハードルになってしまう…なんてことがあったので、お知らせ機能など軽い更新作業だけを使い慣れたGoolgeスプレッドシートで完結させる構成を試してみました。
プロジェクト構成
- バックエンド:Google Apps Script (GAS) をWeb App化し、スプレッドシートの内容をJSONで返すAPIにする
- フロントエンド:Next.js(App Router)
- 本番環境:Node.jsが動かない共有サーバーを想定
上記のような共有サーバーだと想定し、Next.jsは静的エクスポート一択。
ディレクトリ構成
gsheets-nextjs-site/
├── clasp/ # GASプロジェクト
│ └── src/
└── nextjs/ # フロントエンドGAS側:スプレッドシートをJSON APIにする
以下のようなスプレッドシートを準備しておきます。
date | title | bodyヘッダー行(1行目)の値をそのままJSONのキーとしたいので、ヘッダーは英語表記で統一。
実際の運用を考えると、公開ステータス(pulished)などあった方が良いと思いますが、今回はひとまずの表示を目指します。
claspのセットアップ
claspはCommand Line Apps Script Projectsの略で、Googleが公式で提供しているCLIツールです。GASプロジェクトをローカルで作成、編集、デプロイを行うことができます。PCに1回インストールすればよいグローバルツールです。
# claspが初めての環境で1回インストールします
npm install -g @google/clasp
clasp login
# 以下でバージョンが表示されればインストール済みです
clasp -vプロジェクトを新規作成します
cd clasp
clasp create --type webapp --title my-gsheets-api --rootDir ./srcスプレッドシートをJSONで返す
clasp/src/main.js(これがAPI本体です)
function doGet(e) {
const sheet = SpreadsheetApp.openById('シートID').getSheetByName('news');
const values = sheet.getDataRange().getValues();
const [header, ...rows] = values;
const data = rows.map((row) =>
Object.fromEntries(header.map((key, i) => [key, row[i]]))
);
return ContentService
.createTextOutput(JSON.stringify(data))
.setMimeType(ContentService.MimeType.JSON);
}シートIDはGoogleスプレッドシートのURLから取得できます。ここではシート名を「news」にしています。
以下でGoogle側に反映
clasp pushデプロイと権限承認
clasp deploy --description "v1"
clasp deploymentsインデックス「v1」でデプロイできました。
発行されたWeb AppのURLに初めてアクセスすると、「このアプリがあなたのデータへのアクセス許可を求めています」と表示されるので、許可します。以降はJSONが返るようになります。
フロントエンド側:Next.js
npx create-next-app@latest nextjs --typescript --app --eslintnext.config.tsで静的エクスポートを有効にしておきます。
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
output: 'export',
images: {
unoptimized: true,
},
trailingSlash: true,
};
export default nextConfig;GASのURLを.env.localに。Apps Scriptの[デプロイを管理]から取得できます。
GAS_API_URL=https://script.google.com/macros/s/xxxxx/execお知らせ一覧ページを作成します。app/news/page.tsx を用意。
type NewsItem = {
title: string;
date: string;
body: string;
};
async function getNews(): Promise<NewsItem[]> {
const res = await fetch(process.env.GAS_API_URL!);
return res.json();
}
export default async function NewsPage() {
const news = await getNews();
return (
<ul>
{news.map((item, i) => (
<li key={i}>{item.date} {item.title}</li>
))}
</ul>
);
}ローカルで確認してみます。
cd nextjs
npm run devhttp://localhost:3000/news にアクセスして、スプレッドシートのお知らせが表示されれば成功です。
おまけ
小規模なWebサイトの場合、Node.jsが動かない共有サーバーを利用することが想定されます。
なので、Next.jsは静的エクスポートをデプロイする必要があり、output: ‘export’を有効にしましたが、スプレッドシートを更新しただけではサイトの表示は変わらず、再ビルド・再デプロイが必要です。
つまり、このままだと、スプレッドシートを更新するたびに手動で npm run build をしてアップロードする必要があり、非現実的です。
そこで、スプレッドシートに更新を反映するトリガーを用意してはどうかと考えてみました。
スプレッドシートからトリガーを送り、ビルド・デプロイを自動化する
以下のようなイメージです。
スプレッドシートの「反映する」メニュー
↓
GASがGitHub Actionsに「実行して」とAPI経由で通知(repository_dispatch)
↓
GitHub Actions上でNext.jsをビルド
↓
サーバーへSSH経由(rsync)でアップロードGASに「反映する」メニューを追加し、それがトリガーとなり、GitHub Actionsがビルドとデプロイを担います。
こうすれば、クライアントはスプレッドシートを編集して、メニューから反映するを押すだけでお知らせを公開できるようになります。
