Gatsby で Google Analytics Reporting API を使って人気の投稿一覧を作成する方法
今回は静的サイトジェネレーターの Gatsby を使ったプロジェクトにおいて Google Analytics Reporting API v4 を使って投稿一覧を作成する方法を紹介します。
前提
今回は 2020 年時点で最新の Reporting API v4 を使います。 今回はこれが使える環境がすでに整っている前提です。 Reporting API v4 を使うのに必要な作業は公式のドキュメントや多くのブログ記事で説明されているので、その説明は割愛して「 Gatsby ✕ Reporting API v4 」の部分にフォーカスして説明します。
この記事で取り扱う API は Reporting API v4 だけですが、ここでご紹介する
- Gatsby ノードタイプを定義する
- 外部の API からデータを取得して Gatsby ノードとして保存する
- GraphQL を介して取得したデータをページに表示する
という流れは、いわば「 Gatsby ✕ ウェブ API 」における共通パターンです。 Reporting API v4 を使わない場合でも、「 Gatsby ✕ ウェブ API 」に興味のある方にはある程度ご参考になるのではないかと思います。
ちなみに、 Google Analytics のデータという「結果」だけ得られればそれで十分という場合は、 Reporting API v3 を使って Google Analytics のデータを取得するプラグインがいくつか提供されているのでそれらを利用することをおすすめします:
gatsby-source-google-analytics-reporting-api
| GatsbyJSgatsby-plugin-google-analytics-reporter
| GatsbyJS
尚、動作確認に使った Gatsby のバージョンは 2.23.11 です。
用語
この記事では以下の用語を使います。
- GCP: Google Cloud Platform のこと
全体の流れ
全体の流れは以下のとおりです。
- 必要なライブラリをインストールする
- 人気の投稿に対応する node type を作成する
- Reporting API を使って人気の投稿のデータを取得する
- 取得した人気の投稿データをページに表示する
- 完成
具体的な作業
1. 必要なライブラリをインストールする
必要なライブラリをインストールします。
googleapis
( GitHub 上はgoogleapis/google-api-nodejs-client
)google-auth-library
npm i --save googleapis google-auth-library
googleapis
は API へのアクセスを行うためのメインのライブラリで、 google-auth-library
は認証部分を担うライブラリです。
2. 人気の投稿に対応する node type を作成する
gatsby-node.js
で createSchemaCustomization()
を定義して、人気の投稿のスキーマを定義します。
スキーマの定義には 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`],
})
)
}
}
今回は id
・ path
・ title
・ count
という 4 つのフィールドを持つタイプを定義しています。
それぞれの意味合いは次のとおりです。
id
: ユニークな IDpath
: ページパスtitle
: ページタイトルcount
: 閲覧数
他のフィールドが必要な場合は追加します。
3. Reporting API を使って人気の投稿のデータを取得する
スキーマが定義できたら、 gatsby-node.js
の sourceNodes()
を定義して、実際に 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_CREDS
と GCP_VIEW_ID
をセットすることを忘れないようにしてください。
その際の注意点として、 GCP の認証情報等の private 情報を格納した環境変数がクライアントサイドに漏れないよう注意が必要です。
ファイル .env.*
で定義された環境変数や、名前が GATSBY_
から始まる環境変数は自動的にクライアントサイドにも渡されるので、 private 情報に対してこれらを使用しないようにしましょう。
Gatsby 環境変数の扱いは正しく理解して使うようにしましょう。
参考: Client-side JavaScript | Environment Variables | GatsbyJS
ということで、 Gatsby で Google Analytics Reporting API v4 を使って人気の投稿一覧を作成する方法のご紹介でした。
参考
Gatsby 関連
createSchemaCustomization
| Gatsby Node APIs | GatsbyJSsourceNodes
| Gatsby Node APIs | GatsbyJS- Querying Data in Components with the useStaticQuery Hook | GatsbyJS
Google Analytics Reporting API v4 関連
- Method: reports.batchGet | Analytics Reporting API v4
- google-api-javascript-client/start.md at master · google/google-api-javascript-client · GitHub
- Node.js (JavaScript)で Google Analytics Reporting API v4 を使う | mintsu's blog
GCP 公式のドキュメントでも時期によっては古くなっているものがあったりするので注意が必要です。