本文へスキップ
メニュー
テーマ

GA4から取得したデータをmicroCMSに送信して、人気記事ランキングを実装する

目次

前提条件

  • Next.js v14.0.2
    • App Router
    • Route Handlers
  • Google Analytics 4
  • microCMS
    • microcms-js-sdk v2.7.0
  • Vercel(Cron Jobsで定期実行する)
補足

記事のコードは、上のバージョンで動かしたときのものです。新しいバージョンでは書き方が変わっている場合があるので、使っているバージョンのドキュメントもあわせて確認してください。

目的

Next.jsで構築し、ヘッドレスCMSにmicroCMSを採用しているサイトに人気記事を表示します。
その際に、GA4のデータを使用してmicroCMSの記事データを定期的に更新し、そのデータを元に実装します。

Google Cloudで鍵を取得する

サービスアカウントを作成する

  1. Google Cloudコンソール⁠(新しいタブで開く)にログインし、「API とサービス」を開きます。
  2. 検索フォームにAnalyticsと入力するとGoogle Analytics Data APIがあるのでクリックします。
    Google Cloudの検索ボックスに「Analytics」と入力した候補一覧。MARKETPLACEの最下部に「Google Analytics Data API」がある
    一番下にある「Google Analytics Data API」を選択する

    Google Analytics Data API | Google for Developers⁠(新しいタブで開く)

  3. 以下の画面で「有効にする」を押します。

    「有効にする」を押す

    「有効にする」を押す
  4. メニューから[IAMと管理] > [サービスアカウント]を開き、「サービスアカウントを作成」を押します。
    Google Cloudのナビゲーションメニュー。「IAMと管理」のサブメニューに「サービスアカウント」がある
    左のメニューの[IAMと管理]から[サービスアカウント]を開く

    サービスアカウントを作成する

    サービスアカウントを作成する
  5. 任意のサービスアカウント名を入力してから「完了」を押します。
    サービスアカウントの作成画面。サービスアカウント名「Google Analytics」とID「google-analytics」が入力されている
    サービスアカウント名を入力する

鍵を取得する

  1. サービスアカウントの[操作]から[鍵を管理]を押します。

    操作メニューから「鍵を管理」を開く

    操作メニューから「鍵を管理」を開く
  2. 鍵のページで[鍵を追加] > [新しい鍵を作成]を押します。
    「鍵を追加」のプルダウン。「新しい鍵を作成」と「既存の鍵をアップロード」の2項目がある
    [鍵を追加]>[新しい鍵を作成]を選ぶ
  3. キーのタイプは[JSON]を選択し、[作成]を押すとJSONファイルがダウンロードされます。
    秘密鍵の作成ダイアログ。キーのタイプでJSONが選択されている
    キーのタイプにJSONを選んで「作成」を押す

    ダウンロードした鍵のファイルは、あとから再ダウンロードできません。なくした場合は、新しい鍵を作り直します。
    また、この鍵があれば誰でもサービスアカウントとしてGA4のデータを読めてしまうので、流出しないように厳重に管理する必要があります。

注意

組織(Google Workspaceなど)に属するGoogle Cloudでは、組織のポリシー(「サービス アカウント キー作成を無効にする」)で鍵の作成が禁止されていることがあります。2024年5月3日以降に作られた組織では、このポリシーが最初から有効です。鍵を作れないときは、組織の管理者に相談してください。

サービスアカウントにGA4の権限を付与する

  1. Google Analyticsを開き、左下の[管理]を押します。
  2. [アカウントのアクセス管理]を開きます。
    Google Analyticsの管理画面の左メニュー。「アカウントのアクセス管理」が赤枠で強調されている
    [アカウントのアクセス管理]を開く
  3. 青丸の+ボタンを押したあと、[ユーザーを追加]を押します。

    [ユーザーを追加]を選ぶ

    [ユーザーを追加]を選ぶ
  4. 先ほど作成したサービスアカウントのメールアドレスを入力し、「新規ユーザーにメールで通知する」のチェックを外します。「標準の役割」は「閲覧者」にチェックを入れて「追加」を押します。

    サービスアカウントのメールアドレスを入力し、役割を「閲覧者」にする

    サービスアカウントのメールアドレスを入力し、役割を「閲覧者」にする

GA4から取得したデータをmicroCMSに送信するAPIを作成する

  1. microCMSの記事のAPI(ここではposts)のスキーマに、表示回数を入れる数字フィールドを追加します。フィールドIDはscreenPageViewsにします。
  2. Google Analytics Data APIを使用してGoogle Analyticsにアクセスするために、Google Analytics Data: Node.js Client⁠(新しいタブで開く)をインストールします。
    ターミナルbash · 1行
    npm install @google-analytics/data

    @google-analytics/data - npm⁠(新しいタブで開く)

  3. Google Cloudから取得したJSONファイルの中身からprivate_keyとclient_emailをメモしておきます。
    credential.jsonJSON · 13行
    {  "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"}
  4. GA4の[管理] > [プロパティの詳細]の右上にあるプロパティIDをメモしておきます。

    プロパティIDを控える

    プロパティIDを控える
  5. 環境変数に以下のように設定します。Vercelで動かす場合は、Vercelのプロジェクトの環境変数にも同じ値を設定します。
    .env.local.env · 3行
    GOOGLE_ANALYTICS_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nxxxxxxxxxx\n-----END PRIVATE KEY-----\n"GOOGLE_ANALYTICS_CLIENT_EMAIL=xxxxxxxxxx@xxxxxxxxxx.iam.gserviceaccount.comGOOGLE_ANALYTICS_PROPERTY_ID=xxxxxxxxx
  6. src/libs/ga/getPageViews.ts を作成し、以下のように記述します。
    getPageViews.tsTypeScript · 60行
    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,    }  })}
  7. src/libs/microcms.ts に以下のように記述し、記事のデータを更新できるようにupdatePostDataを作成します。
    microcms.tsTypeScript · 29行
    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 || "",});// 失敗したときは呼び出し側(route.ts)で記録するので、ここではエラーをそのまま投げるexport const updatePostData = async (contentId: string, editData: Record<string, unknown>) => {  await client.update({    endpoint: "posts",    contentId,    content: {      ...editData,    },  });};
  8. src/app/api/ga/fetchPageViews/route.ts を作成し、microCMSにデータを送信するためのAPIを作成します。
    route.tsTypeScript · 41行
    import { getPageViews } from '@/libs/ga/getPageViews'import { updatePostData } from '@/libs/microcms'import { NextRequest } from 'next/server'export async function GET(request: NextRequest) {  // Vercel Cron Jobsからの呼び出しだけを受け付ける  const cronSecret = process.env.CRON_SECRET  if (!cronSecret || request.headers.get('authorization') !== `Bearer ${cronSecret}`) {    return new Response('Unauthorized', { status: 401 })  }  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} の更新に失敗しました。`, error)        }        await delay(500)      }    }    return new Response('Success', { status: 200 })  } catch (error: any) {    return new Response(error?.message, { status: 400 })  }}

    microCMSのWRITE API(POST・PUT・PATCH・DELETE)は、1サービスあたり1秒間に5回までしか送信できないため、delay関数を作成してリクエストの間隔を調整しています。

    制限事項/注意事項|microCMSドキュメント⁠(新しいタブで開く)

    VercelのCron JobsはGETリクエストしか送信できないため、GETリクエストにしています。誰でもこのURLを開けば更新が走ってしまうので、Cron Jobsが送ってくるAuthorizationヘッダーを確かめています(設定は次の章で行います)。

  9. PATCHリクエストを送信できるように、microCMSのAPIキーの権限で、postsのPATCHにチェックを入れてください。

    デフォルト権限でPATCHにもチェックを入れる

    デフォルト権限でPATCHにもチェックを入れる

    Next.jsで作成したAPIはGETリクエストですが、そこからmicroCMSのAPIにはPATCHリクエストを送信しています。

注意

APIでコンテンツを更新すると、microCMSの更新日時(updatedAt・revisedAt)も変わり、Webhookを設定していれば記事ごとに送られます。サイトに記事の更新日を表示している場合、この仕組みを動かすと、表示回数が変わった記事がすべて「更新された」ように見えてしまいます。更新日は、別に用意した日付のフィールドから表示するのがおすすめです。

Vercel Cron Jobsで定期的に実行する

  1. Vercelのプロジェクトの環境変数にCRON_SECRETを追加します。値は16文字以上のランダムな文字列にします。Vercelは、Cron Jobsを実行するときに、この値をAuthorization: Bearer <CRON_SECRET>として送ってきます。
  2. プロジェクトのルートに vercel.json を作成し、実行するパスとスケジュールを書きます。スケジュールはUTCで指定するので、以下は毎日午前3時(日本時間)に実行する設定です。
    vercel.jsonJSON · 8行
    {  "crons": [    {      "path": "/api/ga/fetchPageViews",      "schedule": "0 18 * * *"    }  ]}
  3. デプロイすると、Vercelのプロジェクトの[Settings] > [Cron Jobs]に登録されます。実行の結果は、Vercelのログで確かめられます。
補足

Hobbyプランでは、Cron Jobsは1日1回までです。また、実行される時刻は、指定した時刻から最大59分遅れることがあります。

Managing Cron Jobs | Vercel⁠(新しいタブで開く)

ページに出力する

今回はコンポーネントとして使いまわせるようにしたいので、非同期のServer Componentとしてデータを取得し、Suspenseで読み込み中の表示を出します。

  1. src/libs/microcms.ts に以下の記述を追加します。MicroCMSQueriesは、ファイルの先頭のimportに追加してください。
    microcms.tsTypeScript · 21行
    import { createClient, type MicroCMSQueries } from "microcms-js-sdk";/** * 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}
  2. src/components/PopularPosts.tsx を作成し、以下のように記述します。
    PopularPosts.tsxTSX · 19行
    import { getAllPosts } from "@/libs/microcms";import Link from "next/link";export const PopularPosts = 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>  );};
  3. src/app/page.tsx でPopularPostsコンポーネントを呼び出します。このとき、Suspenseでラップしてください。
    page.tsxTSX · 12行
    import { PopularPosts } from '@/components/PopularPosts'import { Suspense } from 'react'export default function Home() {  return (    <div>      <Suspense fallback={<p>Loading...</p>}>        <PopularPosts />      </Suspense>    </div>  )}

    これでページに人気記事のランキングを出せるようになりました。

参考・引用