Gatsby でも人気のページや記事などを表示させたいとします。ですが、Gatsby はそもそもいわゆる DB を持っていないので、アクセス数などもかんたんには記録できません。 やるとしたら外部に何らかのページビューリソース的なものが必要です。
安直に考えれば Google Analytics の値を API 経由で引っ張ってくることで、なんとなくの人気記事を把握できそうです。 人気記事を知るために、まずは各ページの Page View 取得できると良さそうです。
今回は、gatsby-source-google-analytics-reporting-api
というプラグインが存在しているので 試しに使用してみます。
前提として、以下の準備ができている必要があります。
- Google Analytics が既に対象のサイトに設置してあり計測している状態である
- Google APIs が利用可能な状態である
- 「サービス アカウント」が登録されている
- 認証に必要な鍵情報が正しく生成されている
- Google Analytics 側で自動的に作成された「サービス アカウント」と同一のアドレスでユーザーが登録されている
事前準備が一番面倒なので、もとから Google APIs をつかっていないと、色々調べることが多くて大変です 🥺
インストール
npm install gatsby-source-google-analytics-reporting-api --save
プラグインを経由して API を利用するため、email
, key
, viewId
, startDate
の値をそれぞれ入力します。
development 環境では .env.development
に定義した値を環境変数経由で渡します。
production 環境(ここでは Netlify を想定)では、Netlify の管理画面から、CLIENT_EMAIL
, PRIVATE_KEY
の値を指定しておきます。
(Settings > Build & deploy > Environment > Environment variables)
// 開発環境時に .env.* が読み込めるように `dotenv` を使用
const activeEnv =
process.env.GATSBY_ACTIVE_ENV || process.env.NODE_ENV || 'development';
require('dotenv').config({
path: `.env.${activeEnv}`,
});
module.exports = {
plugins: [
{
resolve: `gatsby-source-google-analytics-reporting-api`,
options: {
// 「サービス アカウント」のメールアドレス
email: process.env.CLIENT_EMAIL,
// 「サービス アカウント」生成時にダウンロードできる json データに含まれる
// `private_key` から生成される公開鍵を秘密鍵にがっちゃんこしてまとめ、扱いやすいよう base64 で
// あらかじめエンコードしたものを環境変数として渡して、ビルド時にデコードする。
key: Buffer.from(process.env.PRIVATE_KEY, 'base64').toString(),
// Google Analytics のページから取得
viewId: `xxxxxxxxx`,
// 使い始めの日を指定
startDate: `2020-02-17`,
},
},
],
};
認証に関する情報はもちろんプロジェクトでコード管理せず .env.* は gitignore しておき、ローカル端末からのみ読み込めるようにしておきます。
ちなみに startDate の部分を一週間前に指定しておくと、ビルド毎にはなりますが、週間人気ランキングみたいなものも 作ることができていいですね。
const moment = require('moment');
// (省略)
module.exports = {
plugins: [
{
resolve: `gatsby-source-google-analytics-reporting-api`,
options: {
email: process.env.CLIENT_EMAIL,
key: Buffer.from(process.env.PRIVATE_KEY, 'base64').toString(),
viewId: `xxxxxxxxx`,
startDate: moment()
.add(-7, 'days')
.format('YYYY-MM-DD'),
},
},
],
};
# dotenv environment variable files
.env*
CLIENT_EMAIL=xxx-user@xxx-1234567.iam.gserviceaccount.com
PRIVATE_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...
GraphQL での値取得
うまくいくと後は値を利用するだけです。ためしに GraphiQL で値を引っ張ってみます。
id
には、ページのパスであるところの、記事の slug
を渡します。
query MyQuery {
pageViews(id: { eq: "/2020/02/14/gatsby-published-unpublished/" }) {
totalCount
}
}
レスポンスはこのようになりました。
{
"data": {
"pageViews": {
"totalCount": 15
}
}
}
いい感じですね。
ページで表示してみる
記事詳細ページでビルド時の投稿数を表示するとこんな感じ。ただし、 pageViews は記事公開直後など、 page view に関するデータを Google Analytics 側で持っていない場合があリ、pageViews の値は null になります。
このままだと null から totalCount の値を撮ろうとして怒られるので、 Optional chaining を使うなり、 pageViews の値を事前に判定するなりして処理を条件分岐すると良さそうですね。
interface BlogPostProps {
data: {
pageViews?: {
totalCount: number;
};
markdownRemark: MarkdownRemark;
};
}
const BlogPost: React.FC<BlogPostProps> = ({
data,
}: BlogPostProps) => {
const { markdownRemark: post, pageViews } = data;
return (
<Layout>
<h1>{post.frontmatter.title}</h1>
<div className="content">>{post.html}</div>
<div className="count">count: {{pageViews ? pageViews.totalCount : 'unknown.'}}</div>
</Layout>
);
};
export const pageQuery = graphql`
query BlogPostBySlug($slug: String!) {
pageViews(id: { eq: $slug }) {
totalCount
}
markdownRemark(fields: { slug: { eq: $slug } }) {
title
html
}
}
`;
最終的にこんな感じになりました。また、ビルドのタイミングでのみ API にリクエストするので、リアルタイム性は損なわれるものの API を都度消費しないのもいいですね。
(すくない・・・)
この値を使って人気記事のランキングも作れそうですね。つづく。