先週Nuxt3が正式リリースされました。
RCから色々と使っていたのですがAPIにリクエストを飛ばすuseFetchがデフォルトでキャッシュしなくなった(デフォルトでキャッシュしている問題を解決するためのinitialCacheが消えた)りと便利になりました。
そこでNuxt3では結局useFetchどう使えばいいの?というコードをまとめてみました。
- バージョン情報
- Nuxt: 3.0.0
Fatchコード
# 通信先のAPIは後述するカウントを返すJSONを受け取る { value: 0 }
const getCounter = async (): Promise<number> => {
const URL = `/api/counter`
const METHOD = 'GET'
const { data, pending, refresh, error } = await useFetch(URL, {
key: `${uuid.v4()}`, // POINT1
method: METHOD,
// credentials: 'include', //POINT3
// headers: HEADERS, //POINT4
// params: params, // POINT5
async onResponse({ request, response, options }) {
console.log('onResponse log1', request, response.status)
for (const header of response.headers.entries()) {
console.log('onResponse HEADER', header)
}
}, // POINT2
})
console.debug(URL, data.value, error)
return data.value?.value ?? 0
}
- POINT部分を説明していきます
POINT1 keyについて
このkeyはオプショナルな設定属性なのですが、これがないと同じリクエストを並列・非同期に実行すると取得される結果が同じになる。という問題がおきます。
※
同時に同じリクエスト投げるケースがない人は書かなくてOKです。
自分の場合、コンポーネント化した中でAPIをcallしており、
呼び出し側の状態によって同じリクエストパラメタになるケースがあったため、この設定をしています。
keyについてはドキュメントを覗いてみると
https://nuxt.com/docs/api/composables/use-fetch#usefetch
リクエスト間でデータの取得が重複しないようにするための一位なキー。
指定がない場合、静的コードの場所(クラスや行数)に基づいてキーが生成される。
とあります。
実験するため先程のfetchの通信先APIを作ります。
nuxt3にはserverフォルダを掘るとlocalなapiが作れるので以下のようなカウントアップするapiを用意
# server/api/counter.get.ts
class CountState {
value = 0
getCounter() {
return this.value
}
setCounter(value: number) {
this.value = value
}
addCounter() {
this.value++
}
}
const counter = new CountState()
export default defineEventHandler((event) => {
counter.addCounter()
return {
value: counter.getCounter(),
}
})
これを以下のように呼び出す
<template>
<div>
<div>
<p>counter1: {{ countView1 }}</p>
<p>counter2: {{ countView2 }}</p>
<button @click="callApi">CountUP</button>
</div>
</div>
</template>
<script lang="ts" setup>
const callApi = async () => {
//ここで非同期に同時実行する
const results = await Promise.all([getCounter(), getCounter()])
countView1.value = results[0]
console.log('count1', countView1.value)
countView2.value = results[1]
console.log('count2', countView2.value)
}
const countView1 = ref(0)
const countView2 = ref(0)
const getCounter = async (): Promise<number> => {
const URL = `/api/counter`
const METHOD = 'GET'
const { data, pending, refresh, error } = await useFetch(URL, {
// key: `${uuid.v4()}`, KEYを使ってないケース
method: METHOD,
async onResponse({ request, response, options }) {
console.log('[fetch response]', request, response.status)
for (const header of response.headers.entries()) {
console.log('HEADER', header)
}
},
})
console.debug(URL, data.value, error)
return data.value?.value ?? 0
}
onMounted(async () => {
callApi()
})
</script>
結果
これはドキュメントの説明通り同じURLを呼び出したためです。
この状態にならない方法が以下3つになります
- keyを一位なコードで指定する(さきほどのコードのkey属性のコメントを外す
- 並列ではなく直列で呼ぶ
- パラメタが異なる(GET/POSTでもパラメタが違えば大丈夫)
# awaitして連続で呼ぶ
const callApi = async () => {
countView1.value = await getCounter()
console.log('count1', countView1.value)
countView2.value = await getCounter()
console.log('count2', countView2.value)
}
# パラメタを変える
const getCounter = async (value: number): Promise<number> => {
const URL = `/api/counter`
const METHOD = 'GET'
const { data, pending, refresh, error } = await useFetch(URL, {
method: METHOD,
params: {
id: value,
},
async onResponse({ request, response, options }) {
console.log('[fetch response]', request, response.status)
for (const header of response.headers.entries()) {
console.log('HEADER', header)
}
},
})
console.debug(URL, data.value, error)
return data.value?.value ?? 0
}
## 呼び出し側
const results = await Promise.all([getCounter(1), getCounter(2)])
まったく同じURLに2回リクエストすることがないよね?と思いますがAPIをcallしているコンポーネントを作り、それを複数同時に呼び出す用に設定すると困るケースがある。
結果がキャッシュされて、2回のリクエスト1回分にしているのかと思いきや普通にリクエストは2回飛んでいました。
しかし片方の結果のみになる。(最初に書いたRC時代にあったinitialCacheは1回のリクエストしか飛ばさず同じ結果が取れてた)
というわけで、keyを指定しましょう。というPOINTになります。
POINT2 onResponse
onResponseではレスポンスのステータスコードやヘッダーが取得できます
デフォルトのレスポンスはuseFetchのdata, pending, refresh, execute, errorでは取れない?みたいなので、onResponseでレスポンスの中身を取得しました。
POINT3 credentials
認証のセッションクッキーを利用する場合に利用。
自分が試したのはLaravelのSanctum
POINT4 Header
ヘッダー情報を指定したい場合に利用
POINT5 パラメタ
- GETの場合はparams, POST等の場合はbodyというキー名でobjectで渡せばOK
というわけでNuxt3のFetchの使い方の覚書になります。
POINT1でハマったのでちょっと多めです