この記事をおすすめしたい人
- WordPressのREST APIから全記事を取得したい人
- Nuxt.jsで静的出力する際にAPIから動的ルートを追加したい人
- つまりオレ
WordPress Nuxt化の一環です。
今回はWordPressのREST APIから記事を全件取得します。
全件取得なんかいらんやろ。
と思っていたんですが、必要になったのでやり方を検討して、実際に取得するところまでやってみたいと思います。
WordPressの上でな!
※ このブログはまだWordPress製です
記事の全件取得が必要なのはgenerateの時
WordPressのNuxt化として、静的HTML出力を考えています。
この際に、動的ルーティングのページ(記事やカテゴリページ)は無視されるので、自分でルートを追加してやらないといけません。
このままだとページをリロードした時に表示されなかったり、HTMLがないせいでSEO的にもあまりよくなさそうな気がするので、全ページ事前に出力します。
WordPressのREST APIは全件取得できないのか?
一度に取得できる記事数は最大100件までです。
記事取得の際に「per_page」をWordPress内で全件指定の「-1」や大きな値(1万とか)を指定すると次のようなエラーがでます。
{
"code": "rest_invalid_param",
"message": "無効なパラメーター: per_page",
"data": {
"status": 400,
"params": {
"per_page": "per_page は1以上、100以下でなければなりません。"
}
}
}
つまり100件しばりで全件取得を考えないといけません。
全件取得の考え方
1回のAPIリクエストあたり100件しか取得できないので、全記事の取得が終わるまで複数回APIを叩くことになります。
この時に問題になるのが、「どこで終わるのか」です。
終了判定の違いで3つの方法を考えました。
何も考えずにリクエストを続ける方法
WordPressのREST APIから「次の100件」を取得する場合、「per_page」と「page」でコントロールします。 「次の100件」の場合は「per_page=100」「page=2」になります。
「page」の数を増やしていき、該当記事が1件もなかった場合は次のエラーを返します。
{
"code": "rest_post_invalid_page_number",
"message": "リクエストされたページ数は存在するページ数を上回っています。",
"data": {
"status": 400
}
}
このエラーが返った時を終了とする方法です。
ただし、このエラーは上記JSONを正常に受け取れるわけではなく、例外が発生します。
例外処理をしなくてはならないのでちょっと面倒です。
「x-wp-total」から記事数を取得する方法
REST APIを一度リクエストするとレスポンスのヘッダの中に「x-wp-total」という値があります。 これはリクエストしたクエリに該当する「全件」の数です。
詳しくはWordPress公式を見てください。
総記事数が分かるなら、何回リクエストすれば全記事取得できるかが計算できます。
独自APIを使う方法
動的ルーティングを定義するのに必要な最小限の情報(スラッグだけとか)を、全記事分返す独自APIを作ります。
独自APIを実装する手間はありますが、終了判定を気にする必要がなく、APIリクエストも1回で済みます。
独自APIでの実装が一番利便性はいいかなと思いますが、独自APIはなんでもできてしまうし、ここまで独自APIに頼らずにやってきているので、「x-wp-total」を使う方法でコードを考えていきます。
全記事取得のコード
今回はgenerate時のルート追加のために全件取得を行うので、nuxt.config.jsに以下のコードを追加します。
//axiosのインポート
import axios from 'axios'
export default {
generate: {
routes: async () => {
//戻り値用の配列
let list = []
//1回の取得数
const per_page = 100
//APIを取得するURL
const baseUrl = 'https://dev.ore-shika.com'
//リクエスト用のクエリ
const query = baseUrl + '/wp-json/wp/v2/posts' + '?context=embed&per_page=' + per_page + '&page='
//最初の100件を取得
let res = await axios.get(query + '1')
//ヘッダから総記事数を取得
const total = res.headers['x-wp-total']
//何回取得する必要があるかを計算
const limit = Math.ceil(total / per_page)
//page番号をiとしてループ
for ( let i = 1; i <= limit; i++ ) {
//1回目のレスポンスは受け取り済み
if ( i > 1 ) {
res = await axios.get(query + i)
}
//取得した記事を戻り値配列に追加
res.data.forEach(post => {
list.push('/post/' + post.slug + '/')
})
}
return list
},
},
}
同じ用途で使う場合は、「baseUrl」の部分だけ書き換えればコピペで使えると思います。
「query」に「context=embed」とついていますが、これは簡単に言うと返すデータを必要なものにだけ絞って軽量化するサインです。
他の場所で使う場合は調整が必要ですが、要は以下の4点だけ抑えておけば流用できると思います。
- 最初の100件を取得
- ヘッダの「x-wp-total」から総記事数を取得
- 「page」の最大値を計算
- 「page」の数だけループして記事を取得
以上、WordPressからお届けしました!