12
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Nuxt3 Fetchの使い方

Last updated at Posted at 2022-11-22

先週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>

結果

image.png

これはドキュメントの説明通り同じ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)])

image.png

まったく同じURLに2回リクエストすることがないよね?と思いますがAPIをcallしているコンポーネントを作り、それを複数同時に呼び出す用に設定すると困るケースがある。

結果がキャッシュされて、2回のリクエスト1回分にしているのかと思いきや普通にリクエストは2回飛んでいました。
しかし片方の結果のみになる。(最初に書いたRC時代にあったinitialCacheは1回のリクエストしか飛ばさず同じ結果が取れてた)

image.png

というわけで、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でハマったのでちょっと多めです

12
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?