先日、フロントエンド開発に関連する技術系記事をまとめてチェックできる 「TechDeck」 という Web サイトを Next.js + Tailwind CSS + いくつかの API + Vercel という組み合わせで作ってみましたという記事を書きました。
TechDeck では、下記の技術系記事がまとまっている Web サイトから記事データを取得させていただいてるんですけども、それぞれ、Qiita API (Qiita)、Feedly API (Zenn と はてなブックマーク)、DEV API (DEV Community)、という 3つの API を使用して記事を取得しています。
- Zenn から 「Tech」 カテゴリに分類される人気記事
- Qiita から過去 10 日間の人気記事 (20 ストック以上されている記事)
- はてなブックマーク 「テクノロジー」 カテゴリの人気記事
- DEV Community から、フロントエンド開発に関連しそうなタグごとの人気記事
今回は、その中から、Qiita API を使用して、Next.js から 「過去 10 日間の人気記事 (20 ストック以上されている記事)」 を取得する方法について簡単にメモしておこうと思います。
Qiita API
Qiita API (v2) の公式ドキュメントは下記にあります。
今回は記事を取得したいだけなので、API 群の中から、「GET /api/v2/items」 を使用して記事を取得します。
アクセストークン
公式ドキュメント内の 「認証認可」 セクションにあるとおり、単純な GETリクエストのみ使用する場合は、アクセストークンは不要なのですが、認証していない状態では IP アドレスごとに 1 時間に 60 回までというリクエスト制限が入ります。
それで問題ないという場合はそのままでもよいですし、制限が緩い方が開発時とかに安心だなという場合はアクセストークンを取得しましょう。
ちなみに、アクセストークンを使用して認証している状態ではユーザごとに 1 時間に 1000 回までリクエストが可能になります。
アクセストークンの取得方法
アクセストークンは、Qiita にログイン後、「設定」 -> 「アプリケーション」 と進むと、「個人用アクセストークン」 という項目がありますので、「新しくトークンを発行する」 を選択します。
「アクセストークンの説明」 は適当に。「スコープ」 については公式ドキュメントではそれぞれ下記のように説明されていますが、今回のように単に記事データを取得するだけなら、すべてチェックをしない状態で問題ありません。
- read_qiita
Qiita からアクセストークンに紐付いたユーザに関連したデータを読み出す - read_qiita_team
Qiita Team からデータを読み出す - write_qiita
Qiita にデータを書き込む - write_qiita_team
Qiita Team にデータを書き込む
保存するとアクセストークンが画面に表示されますので、必ずコピーしておきましょう。再表示はされませんので忘れるとアクセストークンを再発行することになります。
記事取得のためのリクエスト
まず一番基本的なところですが、下記のようにリクエストを投げることで、記事の一覧が作成日時の降順 (要するに新しいものが上) で帰ってきます。
https://qiita.com/api/v2/items?page=1&per_page=50
page
パラメータに指定した数はページ番号 (1 ~ 100 までの数値が指定可能)、per_page
パラメータに指定した数は、1 ページあたり取得する記事件数 (1 ~ 100 までの数値が指定可能) になりますので、上記の例だと、1 ページあたり 50 件の記事データの、1ページ目を取得していることになります。つまり最新の記事 50 件を取得したということですね。
100 ページまで取得可能で、1 ページあたりの最大記事件数が 100 件なので、最大で 10,000 件までは記事データが取得できるということですが、今回は最新の 50 件だけ使わせて頂きます。
で、このままだと単純に公開された記事が新しい順で入ってきてしまいますので、絞り込みを行います。記事の絞り込みには query
パラメータが使用できますので、これで 「ストックされている数が 20 件以上ある記事」 に限定してみようと思います。そうすることで人気の記事だけが取得できると。
何を持って 「人気記事」 と判断するかは人それぞれだと思います。例えば、もう少し条件を緩めにしたい場合は 「ストック 10 件以上」 などとストック数の閾値を下げればよいと思います。
さらに、全期間で 「ストック数 20 件以上」 とすると、かなり過去の記事も入ってきてしまうため、「過去 10 日間で」 という検索条件も付与します。
これら条件を加えると、具体的には下記のようなクエリになります。
https://qiita.com/api/v2/items?page=1&per_page=50&query=created:>2021-06-25+stocks:>20
created:>2021-06-25
とすることで、2021 年 6 月 25 日以降に公開された記事に限定されます。さらに前述したとおり、stocks:>20
を加えて、「ストック数が 20 件以上ある記事」 に絞り込んでいます (c:>
は実際のリクエストでは URL エンコードします)。
Next.js における具体的な実装例
ということで、ここまで決まったらあとは実際に Next.js から API にリクエストを投げて記事を取得、表示するところまで実装するだけになります。
まず、必要な情報を .env
ファイルにまとめておきましょう。今回は .env.local
ファイルを作成して、下記のように Qiita API のエンドポイントと、取得したアクセストークンを設定しておきます。
// .env.local
QIITA_ENDPOINT_URL=https://qiita.com/api/v2/
QIITA_API_KEY=[アクセストークン]
とりあえず開発環境ではこれで問題ないと思います。
本番公開時、例えば GitHub Actions を使用して Vercel にデプロイしようとしたりすると .env ファイルは使い勝手がよくないので、GitHub Secrets に環境変数を登録するなどひと手間必要かと思いますが、その辺は別途、GitHub Actions を使用した Vercel への定期デプロイに関する記事で触れます。
ということで、次に lib/api.js
を作成します。
// lib/api.js
import { formatISO } from 'date-fns'
const QIITAURL = process.env.QIITA_ENDPOINT_URL
const QIITAAPIKEY = process.env.QIITA_API_KEY
export const getQiitaPosts = async () => {
const nowdate = new Date()
const limitdate = formatISO(nowdate.setDate(nowdate.getDate() - 10), { representation: 'date' })
const key = {
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${QIITAAPIKEY}`
},
}
const res = await fetch(`${QIITAURL}items?page=1&per_page=50&query=created%3A%3E${limitdate}+stocks%3A%3E20`, key)
.catch((err) => {
console.error(err)
})
const json = await res.json()
if (json.message) {
console.error(json.message)
throw new Error('Failed to fetch API')
}
return json
}
日付けデータを扱うのに date-fns
を使用していますので、パッケージをインストールしておきます。
npm install date-fns --save # or with yarn yarn add date-fns
記事を取得する時点の日付けから、10 日前の日付けを作成し、limitdate
に代入しておきます。
// 現在日時を取得して
const nowdate = new Date()
// 10 日前の日時を取得した上で、ISO 8601 形式に変換、yyy-mm-dd を取得
const limitdate = formatISO(nowdate.setDate(nowdate.getDate() - 10), { representation: 'date' })
上で取得した日付けを使用して、Qiita API にリクエストを投げます。
// リクエストヘッダとか認証情報とか
const key = {
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${QIITAAPIKEY}`
},
}
// Qiita API からデータ取得
const res = await fetch(`${QIITAURL}items?page=1&per_page=50&query=created%3A%3E${limitdate}+stocks%3A%3E20`, key)
.catch((err) => {
console.error(err)
})
const json = await res.json()
return json
Qiita API はデータ取得でエラーが発生したい場合、エラーレスポンスとして下記のような JSON を返してくるらしいので、
{ "message": "Not found", "type": "not_found" }
その場合はエラーとして処理を中断するように、一応対策しておきます。
if (json.message) {
console.error(json.message)
throw new Error('Failed to fetch API')
}
あとは、getStaticProps
で記事データを取得し、実際のページに反映すればよいと。
// pages/index.js
import { getQiitaPosts } from '../lib/api'
export const getStaticProps = async () => {
const qiita = await getQiitaPosts()
return {
props: {
qiitapost: qiita,
}
}
}