使用技術
-
フロントエンド
- Vite
- React:
^18.2.0
- YamadaUI:
^1.4.3
- axios:
^1.7.2
- react-router-dom:
^6.23.1
-
バックエンド(※外部サーバ)
- FastAPI
React Router DOMでページルーティング
以下のような形でルーティングを定義していました。
// ...
const App = () => {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
);
}
// ...
ViteProxy
外部のバックエンドサーバとやりとりするため、Proxyを活用することで、サーバ内アクセスで完結できます。これをvite
側で制御できます。
//...
export default defineConfig({
// ...
server:{
cors: false,
proxy:{
"/api": {
target: "[バックエンドサーバのベースパス]",
// target: "http://127.0.0.1:3000", // Debugging
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
})
上記のように定義することで、[フロントエンドのベースパス]/api
以降のルートは全て[バックエンドのベースパス]/
以降のルートにリダイレクトされます。
例 ([front]
=> [back]
)
-
/api
=>/
-
/api/get_about
=>/get_about
-
/api/c/send_mail
=>/c/send_mail
したがって、axios
などでバックエンドにfetchする場合は、以下のように短いパスでアクセスできます。
await axios({
method: POST,
url: `/api/${/**[バックエンドサーバのエンドポイント]*/}`,
headers: {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
},
data: /** [リクエストjsonデータ] */
})
ローカル環境ならここまでで開発可能です。(バックエンドもCORSを通しておけば)
しかしVercelにデプロイする場合、また違う設定が必要になるようでした
Vercelデプロイ
ViteProxyを有効にしてあげる
そのままでは、ViteProxyは有効にならず、Vercel側で同じような設定をしてあげる必要があります。
{
"rewrites" : [
{
"source": "/api/:path*",
"destination": "[バックエンドサーバのベースパス]/:path*"
}
]
}
これで、ViteProxyが有効になり、同じようにリダイレクトされるようになります。
React Router DOMが機能しない
そのままvite環境としてデプロイすると、/
以外のパス(/about
、/contact
など)が全て404 Not Found
になります。
VercelがReactのサブページを認識できてないようです。
先ほどのrewrites
プロパティにその旨を設定してあげます。これで各ルートがベースパスを認識できるようになり、解消されます。
{
"rewrites" : [
{
"source": "/api/:path*",
"destination": "[バックエンドサーバのベースパス]/:path*"
},
{
"source": "/(.*)",
"destination": "/"
}
]
}
しかしこれにはトラップがありました。この設定によって、ViteProxyで設定した/api
以降のルートは、全てReactRouterDOMのReactページだと認識されてしまうのです。
例えば、vercelデプロイ後の/api/get_about
にアクセスしても、そんなページコンポーネントは存在しないので白紙のページが返ってきます。
解決策
めんどくさいですが、ReactRouterDOMのルーティングとViteProxyのルートを丁寧に書いてあげる必要があります。
ReactRouterDOMの場合はdestination
が/
なので気をつけてください。
{
"rewrites" : [
{
"source": "/api/:path*",
"destination": "[バックエンドサーバのベースパス]/:path*"
},
{
"source": "/about",
"destination": "/"
},
{
"source": "/contact",
"destination": "/"
},
]
}