VSCode+ZooCodeを用いてNextjs/TypeScriptの勉強を始めるの課題4に入る。
Zoo(Architectモード)(openrouter/free)に以下の指示を出した。
「Nextjs / TypeScriptを学習を開始する目的で、次の課題に沿うアプリを考案してください。
データフェッチ(サーバーコンポーネント)課題
Next.js 13+ の App Routerでは、サーバーコンポーネントで fetch ができる。React の useEffect とは完全に別物。
課題: (1) サーバーコンポーネントで OpenWeather API を叩いて天気を表示 (2) クライアントコンポーネントでボタンを押すと天気を更新 (3) API Routes 経由で天気を取得する
クリア条件: (a) async function Page() の中で fetch (b) クライアントコンポーネントは "use client" を付ける
フェーズ分けして少しずつ実装することとし、仕様が決まったら
C:\TOOL\JS の下にNextjsプロジェクト js14serverfetch を作成してください。」
⇒To-Doリストを更新しました
Check if C:\TOOL\JS directory exists, create if necessary
Create Next.js TypeScript project js14serverfetch in C:\TOOL\JS
Set up OpenWeather API key in .env.local
Create API route /app/api/weather/route.ts to fetch weather from OpenWeather
Create server component /app/page.tsx that fetches weather from API route
Create client component /app/components/WeatherRefreshButton.tsx with refresh button
Implement button click to trigger router.refresh() for data re-fetch
Add loading and error states to components
Test all three requirements: server fetch, client button update, API route usage
いつもながら、Zooがすんなりとは Next.jsプロジェクトを作成してくれることはなく、今回はなぜか js11approuterの中身を調べ始めた。
「js11approuterは関係のない別プロジェクトです。読む必要ありません。
指示は「C:\TOOL\JS の下にNextjsプロジェクト js14serverfetch を作成してください」だし、To-Doもそれに適合しています。
なお、OpenWeatherのAPI keyは <ひみつ> です。」
と投げたら、今度は mkdir js14serverfetch しようとする。(なんでやねん)
「Nextjsプロジェクトを作成するコマンドを使うとフォルダが自動的に作成されませんか。mkdirで1階層余分にフォルダを作るようなことはしないでださい。」
でようやく
npx create-next-app@latest js14serverfetch --typescript
してくれた。しかし、なぜか js14serverfetch というファイルができていて、まさかの作業失敗。手作業で上記コマンドを打って Nextjsプロジェクト作成、ついでに
git init
git remote add origin https://github.com/minitony/js14serverfetch
git branch -m master main
git push origin main
して VSCodeでOpen Folder & Trustした。(next-appブランチ)
毎度のことながら、ここまでで Openrouter無料枠50のうち20くらい使ってしまう。
To-Doの最後まで行かないと動かせないので最後まで行ってみた。
生成されたファイルは
.env.local (API keyを指定)
app/pase.tsx (fetches weather from API route)
app/weather/route.ts (fetch weather from OpenWeather)
app/components/WeatherRefreshButton.tsx (refresh button)
npm run devするとエラーはでなかった。天気情報も出なかった。(下図)

天気を取ってくること自体はできそうだ。(下図)

◆ 修正
pate.tsxを添付してコパエモンに聞くと、${process.env.NEXT_PUBLIC_BASE_URL}/api/weather からfetchしようとしていて、NEXT_PUBLIC_BASE_URLが未定義なためエラーになっていることが分かった。Zooに対して
「手作業でnpm run devしました。
環境変数NEXT_PUBLIC_BASE_URLが未定義のため、api/weatherのfetchでエラーになります。デフォルト値を用意してください。」
と投げたら NEXT_PUBLIC_BASE_URLを使わないように(パスを相対パスに)変更してくれた。だが、エラー。
コパエモンに聞いても Geminiに聞いても、サーバコンポーネントでfetchする場合、相対パスは使えないらしい。
こういうときは「Route Handler を直接 import して呼ぶ」のが推奨で
import { GET as getWeatherApi } from '@/app/api/weather/route';
const res = await getWeatherApi()
const weather = await res.json();
と書くものらしい。
が、fetchがクリア条件なので Zooの変更を Discardし、Zooに対して
「サーバコンポーネントのfetchで相対パスは使えないので Zooによる変更を戻しました。
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL ?? 'http://localhost:3000';
のようにデフォルト値 'http://localhost:3000' を持つ変数を定義して fetchにパスを与えてください。」
と投げると、どういうわけか、.env.localに定義していた OPENWEATHER_API_KEYを削除して NEXT_PUBLIC_BASE_URLとして http://localhost:3000 を定義しようとした。
「API_Key消すな」と入れたら
作業放棄してタスク完了と返ってきた。
Zooのさぼり技(完了宣言)に屈するのも癪にさわるけど、延々と解決するまで指示してOpenrouterのリクエスト数を稼がれてしまうのも嫌なので、手作業でコパエモン案を反映した。動くようになった(下図)

コードを見ていくと、
app/components/WeatherRefreshButton.tsx
「天気を更新」ボタンの<button>要素を返すWeatherRefreshButton関数を宣言していて、onClickのイベントハンドラで router.refresh()している。
router.refreshするとサーバコンポーネントのデータだけを再取得する模様。
useRouterには next/routerと next/navigationの二つのバージョンがあって、App Routerでは後者の方を importする。
Openweathermapから天気情報をfetchする Route Handler。
Route HandlerとかResponseオブジェクトとか、消化できなくて頭がパンク。。
api/weatherから天気情報をfetchする getWeather関数を宣言して
Page関数で getWeather関数で取得した天気情報と importしたWeatherRefreshButton関数による「天気を更新」ボタンをレンダリングしている。
課題は「API Routes 経由」で、Zooがコード生成したRoute Handlerとは違うものだけど、API Routesは古い方式なので、Route Handlerで良しとする。
コパエモンも新しい方式に則って課題を作ってくれたら良かったのに。
消化不良ぎみというか、コード生成に登場する部分は氷山の一角という感じで、全然学習できてない気がするけど、次の課題に行く。
◆(参考)ネット検索したこと
・Web Fetch API Response
・router.refresh router.reload
◆(参考) コパエモンに質問
・Zooが次のコマンドを実行したら js14serverfetchというフォルダではなくファイルができてしまいました。なぜですか
npx create-next-app@latest js14serverfetch --typescript
・環境変数データを記すファイルの名前は .env、.env.local どちらでもいいのですか
⇒読込順 .env.local > .env.development > .env.production > .env
・NEXT_PUBLICについて詳しく
⇒ブラウザに公開される環境変数
・NEXT_PUBLIC_BASE_URL が環境変数として定義されていないときに
fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/weather`, {
cache: 'no-store' // Always fresh data, no caching })
はどうなりますか
⇒ fetch("undefined/api/weather")するのでエラーになる (確かに page.tsxのcatch文のエラーテキストだ)
デフォルト値を設ける
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000";
fetch(`${baseUrl}/api/weather`);
・?とか??とかどういう意味ですか。 or演算子とはどう違いますか。正規表現にも ? がありましたが類似点ありますか。
title: product?.name ?? 'Product'
(前回の課題 js13ssrssgで気になってたのに聞き忘れてた)
⇒ ?(オプショナルチェーン)null/undefinedなら評価をそこで止めて undefined
??(Null合体演算子)null/undefinedなら右側を使う
・添付page.tsxのcatch(error)に飛んでしまうのですが、なぜか分かりますか。
なお、ブラウザで http://localhost:3000/api/weatherにアクセスすると問題なく temperature/description/humidity/timestampが取得できます。
(ファイル添付 app/page.tsx)
⇒ App Router の Server Component の fetch は “絶対 URL” を要求する
Route Handler を直接 import して呼ぶ方法が推奨
・添付のroute.tsについて、tryの return のResponseの頭が大文字ですが、fetchの戻り値の responseとは別物ですか。何も importしていませんが、Responseはどういう型ですか。Response.jsonは静的メソッドか何かですか。catchのreturnでは new Responseとしているのは何か必然性はありますか。あと、next/serverからimportする NextResponseとはどのように違いますか。
(app/api/weather/route.tsをファイル添付)
⇒ response(fetch の戻り値)と Response(返却用クラス)は別物
Node.jsのFetch APIとして Request/Response/Headers/fetchなどがグローバルに提供される
・API Routesと Route Handlersは何が違いますか。Zooがコード生成した app/api/weather/route.ts はAPI Routesですか。
⇒ Pages Router 時代 → API Routes、App Router 時代 → Route Handlers
Zoo が生成した app/api/weather/route.ts は Route Handler(App Router の新方式)