はじめに
Frontend を React で作る場合、Vite で作って動かすのはデファクトかな、と思います。そして Backend を ASP.NET Core で作成する場合、Visual Studioには専用のテンプレートがあるので、基本的にはそれを使えば良いでしょう。
チュートリアル: Visual Studio で React を使用して ASP.NET Core アプリを作成する
Windows環境 で かつ Visual Studio ユーザーであればこのやり方で全く問題はないのですが、開発環境は Mac/Linux なんだよね、とか、それ以外にも別の理由で使いたくない場合もあるでしょう。この記事では React(Vite) と ASP.NET Core の連携を.NET Aspire(ver: 9.5.0)を使って構築してみましょう。
環境
Visual Studio Code + C# DevKit (Visual Studio, Riderでも可)
Node.js: 22.16.0
.NET: 9.0.304
.NET Aspire(Aspire CLI): 9.5.0
本記事では開発環境が Win/Mac/Linux 等の OS 依存を排除するためにできる限り CLI を用いて、コードエディタには VSCode を使用しています。Visual Studio や Rider をご利用の方は完全に下記手順の通りである必要はありません。
また CLI としてはもう一つ .NET Aspire CLI も使用しています。
場所を作る
このプロジェクト用の場所を作成し、移動します。
mkdir ReactViteAspNetCore
cd ReactViteAspNetCore
Backend を構築する
まずは ASP.NET Core で WebAPI を作成しましょう。dotnet CLI で作る場合、次の手順となります。
- WebAPI の.NET プロジェクトを作成する
- ソリューションファイルを作成して WebAPI の .NETプロジェクトをソリューションに追加する
1. ASP.NET Core を WebAPI で作成する
特に難しいことはないですね。--option(-o) オプションを使用すると、カレントディレクトリに --option(-o) で指定した名前のディレクトリを作成し、その中にプロジェクトが作成されます。さらに --name(-n) でプロジェクトを名を指定することもできますが、省略すると --option(-o) と同じ名前になります。
dotnet new webapi -o ReactViteAspNetCore.Backend
2. ソリューションファイルを作って WebAPI プロジェクトを追加する
ソリューションファイルを作成し、WebAPI プロジェクトを追加します。2025/9現在、ソリューションファイルは sln と slnx(プレビュー)の2種類があります。まだ GA アナウンスはありませんが、今後は間違いなく slnx になるでしょう。今回は最初から slnx 形式でソリューションファイルを作るために --format(-f)オプションで形式を指定します。
dotnet new sln -n ReactViteAspNetCore -f slnx
dotnet sln add ReactViteAspNetCore.Backend
この時点で、次のようなフォルダ構成になっています。
ReactViteAspNetCore/
├– ReactViteAspNetCore.Backend/
| ReactViteAspNetCore.slnx
実行確認する
dotnet run --project ReactViteAspNetCore.Backend
表示された URL (ex: http://localhost:5039) をコピーし、/weatherforecast
をくっつけてブラウザで見てみましょう。(ex: http://localhost:5039/weatherforecast)
ブラウザによって見え方は異なると思いますが、MS Edgeだとこんな感じです。jsonで天気予報データが取得できます。
Frontend を構築する
ViteでReact+TypeScriptを Frontend として構築しましょう。
npm create vite@latest ReactViteAspNetCore.Frontend -- --template react-ts
Interactive にヒアリングが始まります。
- Packageの名前に
reactviteaspnetcore-frontend
と入力 - Use rolldown-vite (Experimental) は No
- Install with npm and start now?は Yes
既定で http://localhost:5173 で稼働するのでアクセスして問題がないことを確認します。
q
を入力してEnterするとVite開発サーバーは停止します。
この時点でフォルダ構成は次の様になっています。
ReactViteAspNetCore/
├– ReactViteAspNetCore.Backend/
├– ReactViteAspNetCore.Frontend/
| ReactViteAspNetCore.slnx
.NET Aspireプロジェクトを作成する
.NET Aspire のテンプレートの存在を確認します。
dotnet new list aspire
もし入っていなかったらインストールが必要です。
dotnet new install Aspire.ProjectTemplates
.NET Aspire の AppHost プロジェクトだけを作成し、ソリューションに追加します。
dotnet new aspire-apphost -o ReactViteAspNetCore.AppHost
dotnet sln add ReactViteAspNetCore.AppHost
この時点でのフォルダ構成は次のとおりです。
ReactViteAspNetCore/
├– ReactViteAspNetCore.AppHost
├– ReactViteAspNetCore.Backend/
├– ReactViteAspNetCore.Frontend/
| ReactViteAspNetCore.slnx
.NET Aspire 管理下にする
それでは作成した Frontend, Backend を.NET Aspireで管理しましょう。
WebAPI(Backend)を .NET Aspire 管理下にする
まず .NET Aspireの本体である AppHost プロジェクトから Backend をプロジェクト参照します。まずは AppHost ディレクトリへ移動し、その後参照設定します。
cd ReactViteAspNetCore.AppHost
dotnet add reference ../ReactViteAspNetCore.Backend
では AppHost プロジェクトのAppHost.cs
を修正していきます。VSCode, Visual Studio, Riderなどお好きなコードエディタで AppHost プロジェクト(フォルダ)を開きます。Visual Stuido の場合は slnx ファイルを開きましょう。
AppHost.cs
を次の様に修正します。
var builder = DistributedApplication.CreateBuilder(args);
+ var backend = builder.AddProject<Projects.ReactViteAspNetCore_Backend>("backend");
builder.Build().Run();
現在 AppHost ディレクトリにいることを確認し、実行します。
dotnet run
.NET Aspire のダッシュボードが自動的に立ち上がるはずです。
Backendの行に表示されているリンク(この例では https://localhost:7008 )をクリックして、ブラウザを立ち上げます。エラーになるので、/weatherforecast
へアクセスして天気予報 json データが取得できることを確認します。
React(Vite)を .NET Aspire 管理下にする
React(Vite)を .NET Aspire で扱うためのメソッドはプリミティブなものからラップして便利にしたものまであるのですが、特別な要件があるのでなければラップした便利なメソッドを使うことができます。
というわけで .NET Aspire Viteアプリケーションを扱うためのライブラリを導入します。.NET Aspire にはとても便利は CLI があります。まず導入済みか確認します。
aspire --version
もしエラーの場合はインストールが必要です。次のページを参考にインストールしてください。
Vite 用のライブラリを探します。ライブラリ名を覚えておく必要はないのですが、Viteという名前では公開されていなくて Node.js 用の拡張機能としての提供であることは覚えておく必要があります。
では .NET Aspire 用のライブラリを検索しましょう。次のコマンドで node
用のライブラリを検索してくれます。(aspire search コマンドではありません)
aspire add node
すると、次の様に二つの候補から選ぶようになりますので、communitytoolkit-nodejs-extensions
を選択します。
追加する統合を選択します:
nodejs (Aspire.Hosting.NodeJs)
> communitytoolkit-nodejs-extensions (CommunityToolkit.Aspire.Hosting.NodeJS.Extensions)
次の様にバージョンの選択を聞かれるかもしれません。今回は9.8.0
を選択しました。
CommunityToolkit.Aspire.Hosting.NodeJS.Extensions のバージョンを選択します:
> 9.8.0 (nuget.org)
stable
無事に追加されると .csproj
ファイルに次の様にアイテムが追加されます。
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Aspire.AppHost.Sdk" Version="9.5.0" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>b7f9d90c-13a9-47d7-9d17-2d989b4a3ea9</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.5.0" />
+ <PackageReference Include="CommunityToolkit.Aspire.Hosting.NodeJS.Extensions" Version="9.8.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ReactViteAspNetCore.Backend\ReactViteAspNetCore.Backend.csproj" />
</ItemGroup>
</Project>
準備ができたので次の様に実装します。
var builder = DistributedApplication.CreateBuilder(args);
var backend = builder.AddProject<Projects.ReactViteAspNetCore_Backend>("backend");
+ var frontend = builder.AddViteApp("frontend", "../ReactViteAspNetCore.Frontend");
builder.Build().Run();
AppHostを実行してダッシュボードを開きます。
dotnet run
frotend、という名前はAddViteApp
の第一引数にセットしたfrontend
です。URLの列のリンクをクリックして React(Vite)のデフォページが表示されることを確認します。
frontend から backend の WebAPI を叩く
frontend と backend は Port 番号が違うので、何も構成しないで frontend から backend の WebAPI を呼んでも CORS エラーになることは自明です。ですが、まずは CORS エラーを起こすところまで構築します。
では Frontend フォルダをお好きな コードエディタ で開いて次の様に実装を追加します。
- import { useState } from 'react'
+ import { useEffect, useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
function App() {
const [count, setCount] = useState(0)
+ useEffect(() => {
+ const fetchData = async () => {
+
+ try {
+ const response = await fetch('https://localhost:7008/weatherforecast')
+ const data = await response.json()
+ console.log(data)
+ } catch (error) {
+ console.error('Error fetching data:', error)
+ }
+ }
+ fetchData()
+ }, [])
・・・
アクセス先として指定した https://localhost:7008 は ASP.NET Coreを今回作成した時に同時に作成された launchSettings.json
ファイルの中の profile が https の箇所に記載された applicationUrl
の URL です。.NET Aspireはこの URL でアクセスできるように backend を毎回立ち上げるため、この Port 番号は固定です。この Port番号は ASP.NET Core を作成するたびに変わりますのでご自身のものに変更してください。
dotnet run
で実行してダッシュボード実行後に frontend を開いて開発者ツールの Console を見てみましょう。
CORS エラーが出ていますので、想定通りです。
CORS エラーを解消する
Vite には Proxy 機能があるので開発時にはそれを使用することで CORS エラーを解消するのが簡単です。vite.config.ts
を修正します。
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
+ server: {
+ proxy: {
+ '/weatherforecast': {
+ target: 'https://localhost:7008',
+ changeOrigin: true,
+ secure: false,
+ }
+ }
+ },
})
この設定だと今後 WebAPI を追加するたびに この Proxy 設定も追加しなければならないため、すごく嫌です。嫌なんですが今は後回しにします。
Proxy を効かせるために、アクセスするURLをルートからの相対パスに変更します。
function App() {
const [count, setCount] = useState(0)
useEffect(() => {
const fetchData = async () => {
try {
- const response = await fetch('https://localhost:7008/weatherforecast')
+ const response = await fetch('/weatherforecast')
const data = await response.json()
console.log(data)
} catch (error) {
console.error('Error fetching data:', error)
}
}
fetchData()
}, [])
再度 dotnet run
からの実行で frontend の Console を確認します。
問題なくデータを取得できました。
Backend の固定 URL を動的にする
vite.config.ts
に設定した Backend の URL (この例だと https://localhost:7008 )は固定なので、このままでもローカルで実行している分には問題ありません。もし複数メンバーで開発するとしても、Portは固定されています。
でもこれでは本番環境にデプロイする時には困ります。サービス間(リソース間)の参照は環境変数で受け取るのが一般的ですのでその形式に修正しましょう。
AppHost.cs
を開いて次の様に修正します。
var builder = DistributedApplication.CreateBuilder(args);
var backend = builder.AddProject<Projects.ReactViteAspNetCore_Backend>("backend");
- var frontend = builder.AddViteApp("frontend", "../ReactViteAspNetCore.Frontend");
+ var frontend = builder.AddViteApp("frontend", "../ReactViteAspNetCore.Frontend")
+ .WithReference(backend)
+ .WaitFor(backend)
+ .WithNpmPackageInstallation();
builder.Build().Run();
重要なのはWithReference
メソッドです。この設定でBackendへの接続である URL が環境変数として渡されます。WaitFor
はbackendの起動を待ち、WithNpmPackageInstallation
は起動時に npm install
を実行します。
そして、vite.config.ts
を開いて次の様に修正します。
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
server: {
proxy: {
'/weatherforecast': {
- target: 'https://localhost:7008',
+ target: process.env.services__backend__https__0 || process.env.services__backend__http__0,
changeOrigin: true,
secure: false,
}
}
},
})
dotnet run
を実行するとダッシュボードが次の様に変化します。npm install
が実行されていることがわかります。
frontend の行の URL にアクセスしてから開発者ツールの Console を開くと、無事にデータが取得できていることがわかります。
これで OK 、と言いたいところですが実はこのままだと npm run build
が失敗するため、本番デプロイ時にうまくいきません。これはvite.config.ts
に追加した実装の中のprocess
の型が見つからないのことが原因です。Frontendディレクトリで次のコマンドを実行し、型に追加しておきます。
npm i --save-dev @types/node
これでnpm run build
は成功するようになります。
vite.config.ts の Proxy 設定をもっと賢くする
今の Proxy 設定では、Backendにアクセス用の URL が増える度にvite.config.ts
にその URL を設定しなければなりません。それではあまりに不便です。次の様にします。
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
server: {
proxy: {
'/weatherforecast': {
target: process.env.services__backend__https__0 || process.env.services__backend__http__0,
changeOrigin: true,
secure: false,
+ rewrite: (path) => path.replace(/^\/api/, ''),
}
}
},
})
この設定で、api/
が URL の先頭に入っている場合、それを削除してアクセスする様になります。なので、アクセスする実装も修正します。
function App() {
const [count, setCount] = useState(0)
useEffect(() => {
const fetchData = async () => {
try {
- const response = await fetch('/weatherforecast')
+ const response = await fetch('/api/weatherforecast')
const data = await response.json()
console.log(data)
} catch (error) {
console.error('Error fetching data:', error)
}
}
fetchData()
}, [])
・・・
このapi/
を削除する設定は Vite 公式ページの proxy サンプルに記載があります。
(おまけ) Visual Studio Code で Frontend をデバッグ
.NET Aspire を起動する際にデバッグ実行をすれば Backend でブレイクポイントを活用したデバッグが可能です。デバッグ実行は Visual Studio Code + C# DevKit、Visual Studio、Riderなどのコードエディタを使用すれば可能です。
一方、React(Vite) のデバッグ実行はブラウザのプロセスにアタッチしなければなりません。その場合はブラウザを立ち上げるときにアタッチを可能とするための引数を渡さなければいけないので手動で行うとかなり面倒なのですが、VSCodeには
- デバッグモードでブラウザを立ち上げる
- プロセスにアタッチする
これを一度にやってくれる便利な機能があります。まず.NET Aspireプロジェクトを実行してダッシュボードを開いたら、Frontend の URL をクリップボードにコピーします。次にコマンドパレットを開き、Debug: Open Link
を選択します。
URL を入力するダイアログが画面上部に表示されます。Frontend の URL をクリップボードにコピーしておくと、自動的に URL が入力されていると思います。Enter 押下するとブラウザが(デバッグモードで)開きます。既にアタッチされているのでブレイクポイントを置けば、停止します。
この方法でとりあえずのデバッグ実行はできますが、.NET Aspire が毎回 React(Vite) の Port を動的に決定しているために毎回 URL をコピーして貼り付けなければなりません。これは面倒です。なので React(Vite) の Port を固定しましょう。
Port 固定をする場合、React(Vite) を.NET Aspire管理下へおくときに使用したAddViteApp
メソッドは使用できません。Port固定のオプションがないからです。仕方がないのでAddNpmApp
メソッドを使用します。AddViteApp
メソッドはAddNpmApp
メソッドのラッパーなので、細かい設定が可能です。
var builder = DistributedApplication.CreateBuilder(args);
var backend = builder.AddProject<Projects.ReactViteAspNetCore_Backend>("backend");
- var frontend = builder.AddViteApp("frontend", "../ReactViteAspNetCore.Frontend")
- .WithReference(backend)
- .WaitFor(backend)
- .WithNpmPackageInstallation();
// Port を固定で公開する
+ var frontend = builder.AddNpmApp(
+ name: "frontend",
+ workingDirectory: "../ReactViteAspNetCore.Frontend",
+ scriptName: "dev")
+ .WithHttpEndpoint(port: 5173, isProxied: false) // 固定Port
+ .WithReference(backend)
+ .WaitFor(backend)
+ .WithNpmPackageInstallation();
builder.Build().Run();
.NET Aspire管理下においたアプリケーションは、既定で前段に Proxy が挟まるようになります。そのため、今回の例で言うと動的に決定されている Port は React(Vite)ではなく、Proxy の Port です。この設定では isProxied: false
と設定することで Proxy を前段に挟まないように設定することで Port を固定にしています。
AddViteApp
のメソッドを修正して固定Portできるような PR を誰かが出してくれるのを待つか、自分で PR するまではこのやり方で逃げることになります。
これで Port が固定になったのでアタッチ設定も固定でセットアップできます。先ほどのDebug: Open Link
を使用してデバッグモードのブラウザ実行+アタッチを開始すると、設定をlaunch.json
に保存するか?と聞いてきます。
「はい」を選べば、次のような内容でlaunch.json
が作成されます。
{
"configurations": [
{
"type": "msedge",
"name": "http://localhost:5173/",
"request": "launch",
"url": "http://localhost:5173/"
}
]
}
name
は好きな様に変更できます。これで、VSCode で F5 押下ですぐにデバッグ実行が開始できます。(もちろん先に .NET Aspireプロジェクトを起動することをお忘れなく!)
(おまけ)ログ・監視の設定
ログ・監視のセットアップが簡単にできるのも .NET Aspire の良い点です。この設定は必須ではありませんが本番環境でも Open Telemetry を活用するのであればぜひセットアップしましょう。ダッシュボードでログやメトリクスを確認できるようにもなります。
### ServiceDefaults プロジェクト作成
dotnet new aspire-servicedefaults -o ReactViteAspNetCore.ServiceDefaults
### ソリューションへプロジェクトを追加
dotnet sln add ReactViteAspNetCore.ServiceDefaults
フォルダ構成はこのようになっています。
ReactViteAspNetCore/
├– ReactViteAspNetCore.AppHost
├– ReactViteAspNetCore.Backend/
├– ReactViteAspNetCore.Frontend/
├– ReactViteAspNetCore.ServiceDefaults/
| ReactViteAspNetCore.slnx
Backend の ASP.NET Core プロジェクトから サービスデフォルトプロジェクトへの参照を追加します。
cd ReactViteAspNetCore.Backend
dotnet add reference ../ReactViteAspNetCore.ServiceDefaults
Backend である ReactViteAspNetCore.Backend
の Program.cs
に1行追加します。
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();
+ builder.AddServiceDefaults();
var app = builder.Build();
・・・
たったこれだけで、構造化ログ、トレース、メトリックが表示される様になります。
最後に
React + Vite + ASP.NET Core を開発時に .NET Aspire で ローカル環境を整える方法はこんな感じでしょう。Database などのその他依存サービスも.NET Aspire管理してあげるとより開発はやりやすくなりますが、最低限はここでご紹介したセットアップになると思います。
気になるのは本番環境へのデプロイでしょう。このままではデプロイしても動作しませんので手を加える必要がありますが、本番環境の構成によってデプロイ用の設定内容は変わってきます。例えば React(Vite) と ASP.NET Core をそれぞれコンテナ化するのか、あるいは1つのコンテナでまとめるのか。1つにまとめるとしてもコンテナは使用しないのか、などなど。
長くなりましたので、本番環境の構成に応じたセットアップは別記事にてご紹介します。