## 概要 [[useFetch]]を想定するユースケースとは異なるケースで利用した場合にどのような影響があるかの調査。具体的には [[$fetch]] のように単独のリクエストをするために使った場合にどうなるか。 ## 環境 | 対象 | バージョン | | -------------- | ---------- | | [[macOS]] | 15.5 | | [[Mockoon]] | 9.3.0 | | [[Nuxt]] | 3.18.1 | | [[Vue]] | 3.5.18 | | [[Vue Router]] | 4.5.1 | | [[TypeScript]] | 5.9.2 | ## モックサーバーの準備 ### [[Mockoon]]のインストール ```console brew install mockoon --cask ``` ### モックサーバーの設定 POSTボディの `.name` をレスポンスに流し込む簡易的なモックサーバーを構築。 ![[2025-08-09-15-03-21.avif]] > [!code]- mockoon-sandbox.json > ```json > { > "uuid": "fc75a3f2-ca34-4a24-b34b-be6139d03b9f", > "lastMigration": 33, > "name": "Mockoon sandbox", > "endpointPrefix": "", > "latency": 0, > "port": 3333, > "hostname": "", > "folders": [], > "routes": [ > { > "uuid": "edf50ec4-36e2-4b44-9daf-5669567a4c2a", > "type": "http", > "documentation": "", > "method": "post", > "endpoint": "user", > "responses": [ > { > "uuid": "cc676992-d057-47d0-8d77-246f9383f704", > "body": "{\n \"name\": \"{{body 'name'}}\"\n}", > "latency": 0, > "statusCode": 200, > "label": "", > "headers": [], > "bodyType": "INLINE", > "filePath": "", > "databucketID": "", > "sendFileAsBody": false, > "rules": [], > "rulesOperator": "OR", > "disableTemplating": false, > "fallbackTo404": false, > "default": true, > "crudKey": "id", > "callbacks": [] > } > ], > "responseMode": null, > "streamingMode": null, > "streamingInterval": 0 > } > ], > "rootChildren": [ > { > "type": "route", > "uuid": "edf50ec4-36e2-4b44-9daf-5669567a4c2a" > } > ], > "proxyMode": false, > "proxyHost": "", > "proxyRemovePrefix": false, > "tlsOptions": { > "enabled": false, > "type": "CERT", > "pfxPath": "", > "certPath": "", > "keyPath": "", > "caPath": "", > "passphrase": "" > }, > "cors": true, > "headers": [ > { > "key": "Content-Type", > "value": "application/json" > }, > { > "key": "Access-Control-Allow-Origin", > "value": "*" > }, > { > "key": "Access-Control-Allow-Methods", > "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" > }, > { > "key": "Access-Control-Allow-Headers", > "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" > } > ], > "proxyReqHeaders": [ > { > "key": "", > "value": "" > } > ], > "proxyResHeaders": [ > { > "key": "", > "value": "" > } > ], > "data": [], > "callbacks": [] > } > ``` ### 動作確認 ```console $ curl -X POST "localhost:3333/user" --json '{"name": "hoge"}' { "name": "hoge" } ``` ## Nuxt3の環境構築 ### 初期設定 ```bash toki nuxt3 nuxt-sandbox # official modulesは使用しない ``` 起動確認。 ```console cd nuxt-sandbox pnpm dev -o ``` ### useFetchを使ったコードを書く `app.vue` ```ts <script setup lang="ts"> // 実際は別ファイルに定義されている想定 type Response = { name: string }; class Client { async fetchUser(name: string): Promise<Response> { const { data } = await useFetch(`/user`, { method: "POST", headers: { "Content-Type": "application/json", }, baseURL: "http://localhost:3333", body: { name, }, }); return data.value as Response; } } const client = new Client(); // ここからが通常のvueファイルフロー const name = ref(""); const result = ref(""); watch(name, async () => { const user = await client.fetchUser(name.value); result.value = user.name; }); </script> <template> <div> <input type="text" v-model="name" placeholder="Enter your name" /> <h3>{{ result }}</h3> </div> </template> ``` モックサーバーに1秒のdelayを入れ、1秒以内に複数リクエストを飛ばしてみたが事象は再現しなかった。 そもそもvueファイルに定義された場合は、今回のような問題を避けるためにkeyが衝突しない構造になっている気がする。 ## Nuxtプラグイン化する プラグインにしただけでは変化がなかった。 `plugins/bff.ts` ```ts type Response = { name: string }; class Client { async fetchUser(name: string): Promise<Response> { const { data } = await useFetch(`/user`, { method: "POST", headers: { "Content-Type": "application/json", }, baseURL: "http://localhost:3333", body: { name, }, }); return data.value as Response; } } export default defineNuxtPlugin((nuxtApp) => { const bff = new Client(); return { provide: { bff } }; }); ``` `app.vue` ```ts <script setup lang="ts"> const name = ref(""); const result = ref(""); const { $bff } = useNuxtApp(); watch(name, async () => { const user = await $bff.fetchUser(name.value); result.value = user.name; }); </script> <template> <div> <input type="text" v-model="name" placeholder="Enter your name" /> <h3>{{ result }}</h3> </div> </template> ``` ## 並列処理にしてみる 再現しない。 ```ts <script setup lang="ts"> const name = ref(""); const result = ref(""); const { $bff } = useNuxtApp(); async function concurrentFetchUser() { const users = await Promise.all([ $bff.fetchUser(name.value + "1"), $bff.fetchUser(name.value + "2"), $bff.fetchUser(name.value + "3"), ]); result.value = users.map((user) => user.name).join(", "); } watch(name, async () => { await concurrentFetchUser(); }); </script> <template> <div> <input type="text" v-model="name" placeholder="Enter your name" /> <h3>{{ result }}</h3> </div> </template> ``` [[Mockoon]]で[[ランダムにレイテンシーを設定 (Mockoon)|ランダムにレイテンシーを設定]]してみても変わらなかった。 これ以上の調査は難しそう。。