フロントエンドとバックエンドを疎結合にできるJAMstackのアプローチは、利用するJavascriptフレームワークやヘッドレスCMSを柔軟に組み替えることができる利点があります。
しかし、その一方で従来簡単に実装できていた「人気記事一覧」などを実装する場合には工夫が必要です。
例えば、WordPressなどで「人気記事一覧」を簡単に実装できるのは、同一のデータベース上に参照すべき閲覧数が記録されているためです。
しかしJAMstackの場合は、フロントエンドとバックエンドが分離しているため、フロント側へのアクセスを記録する仕組みがありません。
ここで多くの場合実装されるのがGoogle Analyticsです。これによってサイトへの閲覧数をダッシュボードから確認できるようになります。
今回はそのAnalyticsに記録された閲覧数データをGoogle Analytics Data API (GA4 Data API)で取得して、ヘッドレスCMS環境に「人気記事一覧」を実装した手順を紹介します。
ヘッドレスCMS環境と人気記事一覧の難しさ
従来のWordPressベースのサイトであれば、PV数(ページビュー)を元にした人気記事一覧は多くのプラグインで瞬時に実現できます。なぜなら、ページ閲覧数や記事情報が同一環境のデータベースに蓄積されており、動的なクエリでランキングを生成できるからです。
しかし、JAMstack環境は先述した通りフロントエンドとバックエンドが分離しています。それにNext.jsやGatsby.jsはビルド時の記事データを元にサイトを静的生成するので、いずれにせよ「動的に閲覧数を取得して ランキングを構築する」ことはできません。
そこで出番となるのが外部サービスであるGoogle Analyticsです。そのAPIを活用することで静的サイトにおいても人気記事一覧を実装することができます。
- Google Analytics Data APIを使って閲覧数上位の記事を取得する
- 取得した記事を内部的にjsonで保存する
- Next.jsやGatsby.jsの記事取得用クエリにjsonを組み込んで人気記事を判別する
このような仕組みにしておけば、ビルドのたびにjsonが上書きされるので、常に最新の「人気記事一覧」を表示することが可能になります。
Google Analytics Data APIの有効化と基本準備
今回は、GA4向けのGoogle Analytics Data API
を用いて、特定期間の人気ページを取得します。事前に下記の準備を行ってください。
- Google Cloud Platform上でAPI有効化: GA4用のData APIを有効にし、サービスアカウントを作成します。
- 環境変数設定: GA4のプロパティIDやサービスアカウントキーへのパスを、環境変数や設定ファイルで管理します。
これらの設定はGoogle CloudやAnalyticsの公式ドキュメントに従えば問題 なく行えるはずです。また、ローカルやビルドサーバー上で実行する際は、dotenv
などを用いて環境変数を管理すると良いでしょう。
サービスアカウントの利用と注意点
Google Analyticsのデータ取得を行う際には、サービスアカウントを用います。サービスアカウントは人間のユーザーではなく、プログラム的にAPIへアクセスするための専用アカウントです。
- GCP上でサービスアカウントを作成し、JSONキーをダウンロードします。
- このキーは機密情報であり、公開リポジトリに含めないなどセキュリティ対策が必要です。
- GA4プロパティの「管理」タブから、対象のサービスアカウントに対して閲覧権限を付与することで、API経由のデータ取得が可能になります。
データ取得スクリプトの実装例
今回の実装では、上記で用意したGoogleのサービスアカウントとAnalyticsのPropertyIDを用いてデータを取得し、それをjsonで保存するためのスクリプトを記載します。
このスクリプトでjsonファイルさえ出力できれば、あとはNextやGatsbyなど、お使いの環境に合わせて人気記事を表示することは容易なはずです。
以下は、@google-analytics/data
パッケージとdotenv
を用いて、GA4から直近30日間の人気記事トップ10を取得し、JSONファイルとして保存するサンプルスクリプトです。
新しくルートに/scrptsフォルダを作成し、このスクリプトをfetch-popular-posts.jsとして保存しました。
そしてルートフォルダにサービスアカウント作成時にダウンロードした鍵jsonをservice-account.jsonとして保存しています。
同じくルートフォルダに「GA4_PROPERTY_ID=?????????」と記載した.envファイルを配置しています。????????はご自身のPropertyIDの数字に置き換えてください。
これらのファイルはそのまま公開してしまうと問題になるので、.gitignoreで除外対象に加えておきましょう。
そしてCloudFlareなどホスティングサービス側のSecret情報にこれらの情報を記載しておけば、ローカルと同様に動作するはずです。
認証が成功すると、スクリプトは/dataフォルダにpopular-posts.jsonという人気記事リストの情報を出力します。
このスクリプトでは、GA4_PROPERTY_ID
を環境変数で指定しています。また、実行前にnpm install @google-analytics/data dotenv
コマンドで必要なパッケージをインストールしておいてください。
require("dotenv").config()
const { BetaAnalyticsDataClient } = require("@google-analytics/data")
const fs = require("fs")
const path = require("path")
async function fetchPopularPosts() {
let credentials
// GA_CREDENTIALS_JSONが存在すればそちらを使用。なければファイル読み込み。
if (process.env.GA_CREDENTIALS_JSON) {
// 環境変数からパース
credentials = JSON.parse(process.env.GA_CREDENTIALS_JSON)
} else {
// ローカル開発時はservice_account.jsonを使用
credentials = JSON.parse(
fs.readFileSync(
path.resolve(__dirname, "../service_account.json"),
"utf8"
)
)
}
const analyticsDataClient = new BetaAnalyticsDataClient({
credentials: {
client_email: credentials.client_email,
private_key: credentials.private_key,
},
})
const propertyId = process.env.GA4_PROPERTY_ID
if (!propertyId) {
throw new Error("GA4_PROPERTY_ID is not set in environment variables.")
}
const [response] = await analyticsDataClient.runReport({
property: `properties/${propertyId}`,
dateRanges: [{ startDate: "30daysAgo", endDate: "today" }],
dimensions: [{ name: "pagePath" }, { name: "pageTitle" }],
metrics: [{ name: "screenPageViews" }],
orderBys: [{ metric: { metricName: "screenPageViews" }, desc: true }],
limit: 10,
dimensionFilter: {
filter: {
fieldName: "pagePath",
stringFilter: {
matchType: "PARTIAL_REGEXP",
value: "^/blog/",
},
},
},
})
const popularPosts = response.rows.map(row => ({
pagePath: row.dimensionValues[0].value,
pageTitle: row.dimensionValues[1].value,
pageViews: parseInt(row.metricValues[0].value),
}))
const dataDir = path.resolve(__dirname, "../data")
if (!fs.existsSync(dataDir)) {
fs.mkdirSync(dataDir)
}
fs.writeFileSync(
path.join(dataDir, "popular-posts.json"),
JSON.stringify(popularPosts, null, 2)
)
console.log("Fetched popular posts and saved to data/popular-posts.json")
}
fetchPopularPosts().catch(e => {
console.error(e)
process.exit(1)
})
これによりpagePathとpageTitleとpageViewsの項目から成るjsonファイルが出力されます。自分の場合はブログ記事のURLは/blog/[post-slug]という形式にしていたので、dimensionFilterのなかで/blogとして閲覧数の多い記事を取得しています。この指定がないとおそらくホームページ(/)が1位で取得されてしまうはずです。ご自身のパス設定に合わせて変更してください。
あとはページ生成のロジックの中で、このjsonを活用してクエリを構築すれば人気記事リストを作成できます。
個別の調整などが必要な場合は、上記スクリプトをChatGPTなどに読み込ませて、いろいろ実験してみてください。
事前取得した人気記事データをJSONで活用する方法
ここまでで人気記事の情報をまとめたjson作成と表示が出来たと仮定します。
忘れてはいけないのが「ビルドのたびにjsonを新しくすること」です。
今回のスクリプトは「node scripts/fetch-popular-posts.js」で実行することができますが、ホスティングサービス側でビルドするときにも、必ずこのコマンドが実行されるように、package.jsonのなかに新しいコマンドを追記しておきます。例えばGatsby環境では下記のようになります。
"scripts": {
"fetch-data": "node scripts/fetch-popular-posts.js",
"start": "npm run fetch-data && gatsby develop",
}
これによりnpm startをしたとき、ビルドプロセス前にdata/popular-posts.json
が生成されます。その後に、静的サイトジェネレーター側でJSONファイルを参照して記事リストを出力すれば、ビルド時点で最新の人気記 事一覧を組み込むことができます。
ビルド後は純粋な静的ファイルで構成されるため、ランタイムでデータベースやAPIアクセスを必要とせず、パフォーマンスやセキュリティの面でもメリットがあります。
まとめ:ヘッドレスCMSでも人気記事一覧は実現可能
ヘッドレスCMS環境では、従来のWordPress的な「リアルタイムな人気記事表示」を容易に実現することは難しくなります。しかし、Google Analytics Data APIを活用し、ビルド前にアクセスデータを収集・整形することで、静的サイトでも人気記事一覧を組み込むことができます。
この手法は、CMSやビルド環境、フロントエンドフレームワークに依存しない柔軟な実装が可能です。ぜひ、自分の構成に合わせてアレンジしてみてください。