GA4から取得したデータをmicroCMSに送信して、人気記事ランキングを実装する
投稿日:
更新日:
前提条件
- Next.js v14.0.2
- App Router
- Route Handlers
- Google Analytics 4
- microCMS
- microcms-js-sdk v2.7.0
目的
Next.jsで構築し、ヘッドレスCMSにmicroCMSを採用しているサイトに人気記事を表示します。
その際に、GA4のデータを使用してmicroCMSの記事データを定期的に更新し、そのデータを元に実装します。
Google Cloud Platformで鍵を取得する
サービスアカウントを作成する
- Google Cloud Platform(以下GCP)にログインし、「API とサービス」を開きます。
- 検索フォームにAnalyticsと入力するとGoogle Analytics Data APIがあるのでクリックします。
- 以下の画面で「有効にする」を押下します。
- メニューから[IAMと管理] > [サービスアカウント]を開き、「サービスアカウントを作成」を押下します。
- 任意のサービスアカウント名を入力してから「完了」を押下します。
鍵を取得する
- サービスアカウントの[操作]から[鍵を管理]を押下します。
- 鍵のページで[鍵を追加] > [新しい鍵をアップロード]を押下し、キーのタイプは[JSON]を選択し[作成]を押下します。
- [作成]を押下するとJSONファイルがダウンロードされます。秘密鍵は紛失すると再度作成できません。
また、流出しないように厳重に管理する必要があります。
サービスアカウントにGA4の権限を付与する
- Google Analyticsを開き、左下の[管理]を押下します。
- [アカウントのアクセス管理]を開きます。
- 青丸の+ボタンを押下後、[ユーザーを追加]を押下します。
- 先ほど作成したサービスアカウントのメールアドレスを入力し、「新規ユーザーにメールで通知する」のチェックを外します。「標準の役割」は「閲覧者」にチェックを入れて「追加」を押下します。
GA4から取得したデータをmicroCMSに送信するAPIを作成する
- Google Analytics Data APIを使用してGoogle Analyticsにアクセスするために、Google Analytics Data: Node.js Clientをインストールします。ターミナル
npm install @google-analytics/data
- GCPから取得したJSONファイルの中身から
private_key
とclient_email
をメモしておきます。credential.json{ "type": "service_account", "project_id": "xxxxxxxxxx", "private_key_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "private_key": "-----BEGIN PRIVATE KEY-----\nxxxxxxxxxx\n-----END PRIVATE KEY-----\n", "client_email": "xxxxxxxxxx@xxxxxxxxxx.iam.gserviceaccount.com", "client_id": "xxxxxxxxxx", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/xxxxxxxxxx%40xxxxxxxxxx.iam.gserviceaccount.com", "universe_domain": "googleapis.com" }
- GA4の[管理] > [プロパティの詳細]の右上にあるプロパティIDをメモしておきます。
- 環境変数に以下のように設定します。.env.local
GOOGLE_ANALYTICS_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nxxxxxxxxxx\n-----END PRIVATE KEY-----\n" GOOGLE_ANALYTICS_CLIENT_EMAIL=xxxxxxxxxx@xxxxxxxxxx.iam.gserviceaccount.com GOOGLE_ANALYTICS_PROPERTY_ID=xxxxxxxxx
- src/libs/ga/getPageViews.ts を作成し、以下のように記述します。getPageViews.ts
import { BetaAnalyticsDataClient } from '@google-analytics/data' const analyticsDataClient = new BetaAnalyticsDataClient({ credentials: { client_email: process.env.GOOGLE_ANALYTICS_CLIENT_EMAIL, private_key: process.env.GOOGLE_ANALYTICS_PRIVATE_KEY?.replace(/\\n/g, '\n'), }, }) export const getPageViews = async ( startDate: '7daysAgo' | '14daysAgo' | '30daysAgo', limit: number = 10000, ) => { const [response] = await analyticsDataClient.runReport({ property: `properties/${process.env.GOOGLE_ANALYTICS_PROPERTY_ID}`, dateRanges: [ { startDate, endDate: 'today', }, ], dimensions: [ { name: 'pagePath', }, ], metrics: [ { name: 'screenPageViews', }, ], dimensionFilter: { filter: { fieldName: 'pagePath', stringFilter: { matchType: 'BEGINS_WITH', value: '/posts/', }, }, }, limit, }) return response.rows?.map((row) => { const path = row.dimensionValues !== null && row.dimensionValues !== undefined ? row.dimensionValues[0].value : null const postId = path ? path.split('/').pop() : null // pathからpostIdを抽出 return { path, postId, readCount: row.metricValues !== null && row.metricValues !== undefined ? Number(row.metricValues[0]?.value) : 0, } }) }
- src/libs/microcms.ts に以下のように記述し、記事のデータを更新できるようにupdatePostDataを作成します。microcms.ts
import { createClient } from "microcms-js-sdk"; if (!process.env.MICROCMS_SERVICE_DOMAIN) { throw new Error("MICROCMS_SERVICE_DOMAIN is required"); } if (!process.env.MICROCMS_API_KEY) { throw new Error("MICROCMS_API_KEY is required"); } /** * MicroCMSのクライアントを初期化して生成します。 * サービスドメインとAPIキーは環境変数から取得します。 */ export const client = createClient({ serviceDomain: process.env.MICROCMS_SERVICE_DOMAIN || "", apiKey: process.env.MICROCMS_API_KEY || "", }); export const updatePostData = async (contentId: string, editData: any) => { try { await client.update({ endpoint: "posts", contentId, content: { ...editData, }, }); } catch (error) { console.error(`${contentId}:`, error); } };
- src/app/api/ga/fetchPageViews/route.ts を作成し、microCMSにデータを送信するためのAPIを作成します。route.ts
import { getPageViews } from '@/libs/ga/getPageViews' import { updatePostData } from '@/libs/microcms' import { NextRequest } from 'next/server' export async function GET(request: NextRequest) { try { const pageViewsPosts = await getPageViews('30daysAgo') if (!pageViewsPosts || pageViewsPosts.length === 0) { return new Response('Posts Not Found', { status: 404 }) } const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) for (const post of pageViewsPosts) { if (post.postId) { try { await updatePostData(post.postId, { screenPageViews: post.readCount || 0, }) console.log( `${post.postId} の screenPageViews を ${post.readCount || 0} に更新しました。`, ) } catch (error) { console.error(`${post.postId} の更新に失敗しました。`) } await delay(500) } } return new Response('Success', { status: 200 }) } catch (error: any) { return new Response(error?.message, { status: 400 }) } }
microCMSのPATCHリクエストは1秒間に5回までしか送信できないため、delay関数を作成してリクエストの間隔を調整しています。VercelのCron JobsではGETリクエストしか送信できなかったため、GETリクエストにしています。 - PATCHリクエストを送信できるように、microCMSのAPIキーのデフォルト権限からPATCHにチェックを入れてください。
Next.jsで作成したAPIはGETリクエストですが、microCMSのAPIはPATCHリクエストを送信しています。
ページに出力する
今回はコンポーネントとして使いまわせるようにしたいので、Client Componentsとして非同期でデータを取得します。
- src/libs/microcms.ts に以下の記述を追加します。microcms.ts
/** * MicroCMSからすべての投稿を取得します。 * * @param queries - MicroCMSのクエリオプション * @returns - 投稿のリスト */ export const getAllPosts = async (queries?: MicroCMSQueries) => { const listData = await client.getList<any>({ customRequestInit: { next: { revalidate: 60, }, }, endpoint: 'posts', queries, }) return listData }
- src/app/components/PopularPosts.tsx を作成し、以下のように記述します。PopularPosts.tsx
import { getAllPosts } from "@/libs/microcms"; import Link from "next/link"; export const PopularPostsList = async () => { const { contents } = await getAllPosts({ orders: "-screenPageViews", limit: 3, }); return ( <ol> {contents.map((post) => ( <li key={post.id}> <Link href={`/posts/${post.id}`}>{post.title}</Link> </li> ))} </ol> ); };
- src/app/page.tsx でPopularPostsコンポーネントを呼び出します。この時、Suspenseでラップしてください。page.tsx
import { PopularPostsWidget } from '@/components/PopularPostsWidget' import { Suspense } from 'react' export const = () => { return ( <div> <Suspense fallback={<p>Loading...</p>}> <PopularPosts /> </Suspense> </div> ) }
これでページに人気記事のランキングを出せるようになりました。