Gatsby で Google Analytics Reporting API を使って人気の投稿一覧を作成する方法

JavaScriptJamstackSSGReactGatsby

今回は静的サイトジェネレーターの Gatsby を使ったプロジェクトにおいて Google Analytics Reporting API v4 を使って投稿一覧を作成する方法を紹介します。

前提

今回は 2020 年時点で最新の Reporting API v4 を使います。 今回はこれが使える環境がすでに整っている前提です。 Reporting API v4 を使うのに必要な作業は公式のドキュメントや多くのブログ記事で説明されているので、その説明は割愛して「 Gatsby ✕ Reporting API v4 」の部分にフォーカスして説明します。

この記事で取り扱う API は Reporting API v4 だけですが、ここでご紹介する

  1. Gatsby ノードタイプを定義する
  2. 外部の API からデータを取得して Gatsby ノードとして保存する
  3. GraphQL を介して取得したデータをページに表示する

という流れは、いわば「 Gatsby ✕ ウェブ API 」における共通パターンです。 Reporting API v4 を使わない場合でも、「 Gatsby ✕ ウェブ API 」に興味のある方にはある程度ご参考になるのではないかと思います。

ちなみに、 Google Analytics のデータという「結果」だけ得られればそれで十分という場合は、 Reporting API v3 を使って Google Analytics のデータを取得するプラグインがいくつか提供されているのでそれらを利用することをおすすめします:

尚、動作確認に使った Gatsby のバージョンは 2.23.11 です。

用語

この記事では以下の用語を使います。

  • GCP: Google Cloud Platform のこと

全体の流れ

全体の流れは以下のとおりです。

  1. 必要なライブラリをインストールする
  2. 人気の投稿に対応する node type を作成する
  3. Reporting API を使って人気の投稿のデータを取得する
  4. 取得した人気の投稿データをページに表示する
  5. 完成

具体的な作業

1. 必要なライブラリをインストールする

必要なライブラリをインストールします。

npm i --save googleapis google-auth-library

googleapis は API へのアクセスを行うためのメインのライブラリで、 google-auth-library は認証部分を担うライブラリです。

2. 人気の投稿に対応する node type を作成する

gatsby-node.jscreateSchemaCustomization() を定義して、人気の投稿のスキーマを定義します。 スキーマの定義には createTypes() を使用します。

gatsby-node.js:

exports.createSchemaCustomization = ({ actions, schema }) => {
  const { createTypes } = actions

  createPupularPage()

  // `IPopularPage` と `PopularPage` を作成する
  function createPupularPage() {
    createTypes(`
      interface IPopularPage @nodeInterface {
        id: ID!
        path: String!
        title: String!
        count: Int!
      }
    `)

    createTypes(
      schema.buildObjectType({
        name: `PopularPage`,
        fields: {
          id: { type: `ID!` },
          path: { type: `String!` },
          title: { type: `String!` },
          count: { type: `Int!` },
        },
        interfaces: [`Node`, `IPopularPage`],
      })
    )
  }
}

今回は idpathtitlecount という 4 つのフィールドを持つタイプを定義しています。 それぞれの意味合いは次のとおりです。

  • id: ユニークな ID
  • path: ページパス
  • title: ページタイトル
  • count: 閲覧数

他のフィールドが必要な場合は追加します。

3. Reporting API を使って人気の投稿のデータを取得する

スキーマが定義できたら、 gatsby-node.jssourceNodes() を定義して、実際に Google Analytics の Reporting API にアクセスして人気の投稿データを取得します。 取得したデータは createNode() を使って node として保存します。

gatsby-node.js:

exports.sourceNodes = async ({ actions, createNodeId, createContentDigest, reporter }) => {
  const { createNode } = actions

  await addPopularPageNodes()

  // `PopularPage` のノードを追加する
  async function addPopularPageNodes() {
    // Reporting API の利用に必要なデータを取ってくる
    const CREDS = process.env['GCP_CREDS']
    const VIEW_ID = process.env['GCP_VIEW_ID']

    if (!CREDS || !VIEW_ID) {
      reporter.panic(`GCP credentials missing.`)
      return
    }

    const client = auth.fromJSON(JSON.parse(CREDS))
    client.scopes = [`https://www.googleapis.com/auth/analytics.readonly`]

    const analyticsreporting = google.analyticsreporting({
      version: 'v4',
      auth: client,
    })

    // 実際に API にリクエストをかけてデータを取得する
    // ここでは以下の条件でデータを取得しています
    // 期間: 30 日前から当日まで
    // ディメンション: ページパスとページタイトル
    // 指標: セッション数
    // 絞り込み: ページパスが `/content/` から始まるものに限定
    // 並び順: セッション数の降順
    // データ取得数: 20 件
    const res = await analyticsreporting.reports.batchGet({
      requestBody: {
        reportRequests: [
          {
            viewId: VIEW_ID,
            dateRanges: [
              {
                startDate: '30daysAgo',
                endDate: 'today',
              }
            ],
            dimensions: [
              {
                name: 'ga:pagePath'
              },
              {
                name: 'ga:pageTitle'
              }
            ],
            metrics: [
              {
                expression: 'ga:sessions',
              }
            ],
            filtersExpression: `ga:pagePath=~^/content/`,
            orderBys: {
              fieldName: 'ga:sessions',
              sortOrder: 'DESCENDING',
            },
            pageSize: 20
          }
        ]
      }
    })

    // データが取得できなければ終了
    if (res.statusText !== 'OK') {
      reporter.panic(`Reporting API response status is not OK.`)
      return
    }

    // レスポンスからデータを抽出して node として保存する
    const [report] = res.data.reports
    const dimensions = report.columnHeader.dimensions
    const rows = report.data.rows
    for (let row of rows) {
      let valueMap = mapFromArray(dimensions, row.dimensions)

      let data = {
        path: valueMap['ga:pagePath'],
        title: valueMap['ga:pageTitle'],
        count: parseInt(row.metrics[0].values[0], 10),
      }

      let nodeMeta = {
        id: createNodeId(`PopularPage-${data.path}`),
        parent: null,
        children: [],
        internal: {
          type: `PopularPage`,
          contentDigest: createContentDigest(data),
        },
      }

      let node = Object.assign({}, data, nodeMeta)
      createNode(node)
    }
  }
}

やっていることは複雑ではありませんが、コードが縦に長いので少しだけ補足します。

ここでは指標にセッション数を使用しています。 必要に応じて PV (ページビュー)やユーザー数に変更してください。

認証方法には Application Default Credentials ―― Service Account Credentials の JSON ファイルを使用しています。 認証情報は次のところで環境変数から渡しています。

    const CREDS = process.env['GCP_CREDS']
    const VIEW_ID = process.env['GCP_VIEW_ID']

GCP_CREDS は Service Account Credentials の JSON ファイルの中身で、 GCP_VIEW_ID は対象となる Google Analytics の view id です。 私は開発環境では次のような感じで渡しています。

GCP_CREDS=$(cat gcp-credentials.json) GCP_VIEW_ID="XXX" npm run develop

Service Account Credentials の JSON ファイルは GCP のアカウントがあれば次のページから生成できます。

参考: Application Default Credentials | GitHub - googleapis/google-auth-library-nodejs

コードの中で使われている mapFromArray() は 2 つの Array を組み合わせて Object を作るためのヘルパー関数です。 次のように定義したものですがここでは重要ではありません。

/**
 * 2 つの Array から片方をキー、もう片方を値にした Object を作成する
 */
const mapFromArray = (a1, a2) => {
  let valueMap = {}
  for (let i = 0, length = Math.min(a1.length, a2.length); i < length; i++) {
    let v1 = a1[i], v2 = a2[i]
    valueMap[v1] = v2
  }

  return valueMap
}

title は Google Analytics に認識されているページタイトルです。 ページタイトルの末尾にサイト名のサフィックスがくっついていたりすると、そのままでは使いづらいかもしれません。 その場合はサフィックスを除去する等の前処理を追加します。

これで GCP 側の設定や認証情報が間違っていなければ、 gatsby develop を実行すると投稿データが GraphQL で取得できることが確認できるはずです。 次のような GraphQL クエリを試せば全件確認できます。

query MyQuery {
  allPopularPage(sort: {fields: count, order: DESC}) {
    edges {
      node {
        count
        path
        title
        id
      }
    }
  }
}

出力例:

{
  "data": {
    "allPopularPage": {
      "edges": [
        {
          "node": {
            "count": 5000,
            "path": "/content/a/",
            "title": "title A",
            "id": "04b41138-e627-589b-9ad9-b02b175f234f"
          }
        },
        {
          "node": {
            "count": 4000,
            "path": "/content/b/",
            "title": "title B",
            "id": "9265c3c7-54c0-5c76-9b91-f0385961ad3f"
          }
        },
        {
          "node": {
            "count": 3000,
            "path": "/content/c/",
            "title": "title C",
            "id": "5d1ef857-950c-530e-9868-94f10853aa93"
          }
        },
        {
          "node": {
            "count": 2000,
            "path": "/content/d/",
            "title": "title D",
            "id": "5382f2e4-81ab-527d-a50d-a9288af738c0"
          }
        },
        {
          "node": {
            "count": 1000,
            "path": "/content/e/",
            "title": "title E",
            "id": "99c03347-1343-5327-aa2f-2cfc03e4582c"
          }
        },
      ]
    }
  }
}

4. 取得した人気の投稿データをページに表示する

他の Gatsby node と同じように GraphQL で取得できるようになったので、後はこれを利用したいところで取得して描画すれば OK です。

例えばページではないコンポーネントで利用するなら次のような感じになります。

src/components/popular-posts-widget.js:

import React from "react"
import { Link, useStaticQuery, graphql } from "gatsby"

const PopularPosts = () => {
  const data = useStaticQuery(graphql`
    PopularPageQuery {
      allPopularPage(sort: {fields: count, order: DESC}, limit: 5) {
        nodes {
          id
          path
          title
          count
        }
      }
    }
  `)

  const posts = data.allPopularPage.nodes

  if (posts.length < 1) {
    return ``
  }

  return <ol>
    {posts.map((post) => (
      <li key={post.id} >
        <a as={Link} to={post.path}>
          {post.title}
        </a>
      </li>
    ))}
  </ol>
}

export default PopularPosts

5. 完成

以上で完成です。 あとは認証方法や指標や件数を必要に応じて調整するとよいでしょう。

production 用のビルド環境でも環境変数 GCP_CREDSGCP_VIEW_ID をセットすることを忘れないようにしてください。 その際の注意点として、 GCP の認証情報等の private 情報を格納した環境変数がクライアントサイドに漏れないよう注意が必要です。 ファイル .env.* で定義された環境変数や、名前が GATSBY_ から始まる環境変数は自動的にクライアントサイドにも渡されるので、 private 情報に対してこれらを使用しないようにしましょう。 Gatsby 環境変数の扱いは正しく理解して使うようにしましょう。

参考: Client-side JavaScript | Environment Variables | GatsbyJS

ということで、 Gatsby で Google Analytics Reporting API v4 を使って人気の投稿一覧を作成する方法のご紹介でした。

参考

Gatsby 関連

Google Analytics Reporting API v4 関連

GCP 公式のドキュメントでも時期によっては古くなっているものがあったりするので注意が必要です。


アバター
後藤隼人 ( ごとうはやと )

Python や PHP を使ってソフトウェア開発やウェブ制作をしています。詳しくはこちら