Skip to content

Commit 3887681

Browse files
committed
フロントエンドとバックエンドの連携とデプロイの節を追加
1 parent cceb0dd commit 3887681

File tree

1 file changed

+244
-0
lines changed

1 file changed

+244
-0
lines changed
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
---
2+
title: フロントエンドとバックエンドの連携とデプロイ
3+
---
4+
5+
## フロントエンドとバックエンドを連携する
6+
7+
Reactによって作成されたブラウザ上で動くアプリケーションと、Node.jsによって作成されたサーバー上で動くアプリケーションを接続する方法について学びましょう。
8+
9+
### 全体構成
10+
11+
ここでは、フロントエンドとバックエンドを分けて開発します。
12+
13+
フロントエンドとバックエンドの開発用サーバーをそれぞれ起動し、フロントエンドからバックエンドにFetch APIを用いてHTTPリクエストを送信します。
14+
15+
ここに図を埋め込む
16+
17+
### バックエンドを作成する
18+
19+
まずは、バックエンドを作成しましょう。
20+
21+
1. 新しいプロジェクト用のディレクトリを作成し、その中に`backend`ディレクトリを作成して開きます。
22+
1. [データーベースの節](/docs/web-servers/database/)と同様に、データベースを作成し、Expressを用いてWebサーバーを作成します。
23+
1. Supabaseで新しいデータベースを作成します。
24+
1. `npm init`コマンドと`npx prisma init`コマンドを実行して、Prismaのセットアップをします。
25+
1. `.env`ファイルを編集し、Prismaがデータベースに接続できるようにします。
26+
1. `schema.prisma`ファイルを編集し、メッセージを保存するためのテーブルとカラムの定義を次のように記述します。
27+
28+
```javascript
29+
// This is your Prisma schema file,
30+
// learn more about it in the docs: https://pris.ly/d/prisma-schema
31+
32+
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
33+
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
34+
35+
generator client {
36+
provider = "prisma-client-js"
37+
output = "../generated/prisma"
38+
}
39+
40+
datasource db {
41+
provider = "postgresql"
42+
url = env("DATABASE_URL")
43+
}
44+
45+
model Message {
46+
id Int @id @default(autoincrement())
47+
content String
48+
}
49+
```
50+
51+
1. `npx prisma db push`コマンドを実行して、テーブルとカラムの定義をデータベースに反映します。
52+
1. メッセージのサンプルデータをデータベースに登録します。
53+
1. `npm install express`コマンドを実行して、Expressのセットアップを行います。
54+
1. `main.mjs`ファイルを作成し、次のように記述します。
55+
56+
```js
57+
import express from "express";
58+
import { PrismaClient } from "./generated/prisma/index.js";
59+
60+
const app = express();
61+
const client = new PrismaClient();
62+
63+
app.use(express.json());
64+
65+
app.get("/messages", async (request, response) => {
66+
response.json(await client.message.findMany());
67+
});
68+
69+
app.post("/send", async (request, response) => {
70+
await client.message.create({
71+
data: { content: request.body.content },
72+
});
73+
response.send();
74+
});
75+
76+
app.listen(3000);
77+
```
78+
79+
1. `node main.mjs`コマンドを実行して、Webサーバーを起動し、`http://localhost:3000/messages`にアクセスして、メッセージの一覧が取得できることを確認します。
80+
81+
1. バックエンドでTypeScriptを使用するためのセットアップを行います。
82+
83+
ここに動画を埋め込む
84+
1. `npm install -D typescript`コマンドを実行してTypeScriptをインストールします。
85+
1. `npx tsc --init`コマンドを実行して、TypeScriptの設定を記述するための`tsconfig.json`ファイルを生成します。
86+
1. 生成された`tsconfig.json`を編集して、`outDir`オプションの値を`./dist`にしてトランスパイル結果が`dist`ディレクトリに入るようにしましょう。また、`declaration`オプションと`declarationMap`オプションを消し、`allowJs`オプションの値を`true`にしてPrismaが生成したJavaScriptファイルをトランスパイル結果に含めるようにしましょう。
87+
1. `npm install -D @types/express`コマンドを実行して、Expressの型定義をインストールします。
88+
1. `main.mjs`ファイルの拡張子を`.mts`に変更し、TypeScriptで記述できるようにします。
89+
1. `package.json``scripts`プロパティに`npm run`コマンドを登録しましょう。`npm run build`コマンドと`npm start`コマンドを次のように登録します。これで、`npm run build`コマンドでTypeScriptファイルをJavaScriptファイルにトランスパイルし、`npm start`コマンドでトランスパイルされたJavaScriptファイルを実行できるようになります。
90+
```json title="package.jsonの抜粋"
91+
{
92+
"scripts": {
93+
"dev": "vite",
94+
"build": "vite build",
95+
"preview": "vite preview"
96+
}
97+
}
98+
```
99+
1. `npm run build`コマンドと`npm start`コマンドを実行して、Webサーバーを起動し、`http://localhost:3000/messages`にアクセスして、メッセージの一覧が取得できることを確認します。
100+
101+
:::tip[`tsx`]
102+
103+
`tsx`は、
104+
105+
`npm install -D tsx`とすることでインストールでき、`tsx main.mts`のようにすることで、TypeScriptファイルを直接実行できます。
106+
107+
:::
108+
109+
1. 今回はフロントエンドとバックエンドのドメインが異なるため、CORS(Cross-Origin Resource Sharing)を設定する必要があります。
110+
111+
ここに動画を埋め込む
112+
1. `npm install cors @types/cors`コマンドを実行して、CORSミドルウェアとその型定義をインストールします。
113+
1. `.env`ファイルに`WEB_ORIGIN=http://localhost:5173`と追記し、環境変数`WEB_ORIGIN`の値をViteの開発用サーバーのURLに設定します。
114+
1. `main.mts`ファイルにCORSに関する設定を追加して、次のようにします。8行目のように`app.use(cors({ origin: process.env.WEB_ORIGIN }));`とすることで、`WEB_ORIGIN`に設定したURLからのアクセスのみを許可するようにします。
115+
116+
```ts showLineNumbers
117+
import express from "express";
118+
import cors from "cors";
119+
import { PrismaClient } from "./generated/prisma/index.js";
120+
121+
const app = express();
122+
const client = new PrismaClient();
123+
124+
app.use(cors({ origin: process.env.WEB_ORIGIN }));
125+
126+
app.use(express.json());
127+
128+
app.get("/messages", async (request, response) => {
129+
response.json(await client.message.findMany());
130+
});
131+
132+
app.post("/send", async (request, response) => {
133+
await client.message.create({
134+
data: { content: request.body.content },
135+
});
136+
response.send();
137+
});
138+
139+
app.listen(3000);
140+
```
141+
142+
:::tip[CORS]
143+
144+
CORS(Cross-Origin Resource Sharing)とは、
145+
146+
ここでは、フロントエンドの開発用サーバーのみからのアクセスのみを許すようにします。バックエンドのURLは開発環境と本番環境で異なるため、環境変数を使用して切り替えられるようにします。
147+
148+
:::
149+
150+
### フロントエンドを作成する
151+
152+
1. プロジェクト用のディレクトリをの中に`frontend`ディレクトリを作成して開きます。
153+
1. `npm create vite@latest`コマンドを実行して、Reactのプロジェクトを作成します。
154+
1. `.env`ファイルを作成し、`VITE_API_ENDPOINT=http://localhost:3000`と記述し、環境変数`VITE_API_ENDPOINT`の値をバックエンドのURLに設定します。
155+
1. `App.tsx`ファイルの中身を次のようにします。データを取得する際には、`useEffect`フックを使用して、コンポーネントの読み込み時に一度だけ実行するようにします。また、`fetch`関数の第一引数は今までは`/messages`のようにしていましたが、ここではバックエンドのURLを指定する必要があるため、`${import.meta.env.VITE_API_ENDPOINT}/messages`のようにしています。
156+
157+
```tsx
158+
import { useEffect, useState } from "react";
159+
160+
// Viteはトランスパイル時にimport.meta.envのプロパティをVITE_から始まる環境変数に置換する
161+
// これを利用して開発環境と本番環境でFetch APIのリクエスト先を切り替えられる
162+
// 参考:https://ja.vite.dev/guide/env-and-mode
163+
const getMessagesApi = `${import.meta.env.VITE_API_ENDPOINT}/messages`;
164+
const postMessageApi = `${import.meta.env.VITE_API_ENDPOINT}/send`;
165+
166+
type Message = { id: number; content: string };
167+
168+
function App() {
169+
const [messages, setMessages] = useState<Message[]>([]);
170+
const [newMessageContent, setNewMessageContent] = useState("");
171+
172+
// コンポーネント読み込み時に処理を実行するにはuseEffectフックを使う
173+
useEffect(() => {
174+
const timerId = setInterval(async () => {
175+
const response = await fetch(getMessagesApi);
176+
setMessages(await response.json());
177+
}, 1000 * 5);
178+
179+
// useEffectフックに指定した関数の戻り値に指定した関数はコンポーネントの破棄時に実行される
180+
return () => {
181+
clearInterval(timerId);
182+
};
183+
}, []);
184+
185+
return (
186+
<>
187+
<ul>
188+
{messages.map((message) => (
189+
<li key={message.id}>{message.content}</li>
190+
))}
191+
</ul>
192+
<input
193+
value={newMessageContent}
194+
onChange={(e) => {
195+
setNewMessageContent(e.target.value);
196+
}}
197+
/>
198+
<button
199+
type="button"
200+
onClick={async () => {
201+
await fetch(postMessageApi, {
202+
method: "POST",
203+
headers: { "Content-Type": "application/json" },
204+
body: JSON.stringify({ content: newMessageContent }),
205+
});
206+
}}
207+
>
208+
送信
209+
</button>
210+
</>
211+
);
212+
}
213+
214+
export default App;
215+
```
216+
217+
:::tip[Viteでの環境変数の利用]
218+
219+
Viteでは、`VITE_`で始まる環境変数を`import.meta.env`から参照
220+
221+
これを利用して、開発環境と本番環境で環境変数を使用してバックエンドのURLを切り替えられるようにします。
222+
223+
:::
224+
225+
1. `npm run dev`コマンドを実行して、Viteの開発用サーバーを起動し、`http://localhost:5173`にアクセスして、正しく動作することを確認します。
226+
227+
## デプロイする
228+
229+
1. バックエンドをデプロイするため、`Web Service`を選びます
230+
{/* ![](./images/web-service.png) */}
231+
1. バックエンドは、次のように設定します
232+
{/* ![バックエンドの設定](./images/backend-configuration.png) */}
233+
1. フロントエンドをデプロイするため、`Static Site`を選びます
234+
{/* ![](./images/static-site.png) */}
235+
1. フロントエンドは、次のように設定します
236+
{/* ![フロントエンドの設定](./images/frontend-configuration.png) */}
237+
1. 環境変数`VITE_API_ENDPOINT`に先ほどデプロイしたバックエンドのURLを設定します
238+
{/* ![](./images/frontend-environment-variable.png) */}
239+
1. バックエンドの環境変数の設定を再度開き、環境変数`DATABASE_URL`を設定し、環境変数`WEB_ORIGIN`に先ほどデプロイしたフロントエンドのURLを設定します
240+
{/* ![](./images/backend-environment-variable.png) */}
241+
242+
{/* ### 全体構成 */}
243+
{/* ### 本番ビルドを確認する */}
244+
{/* ### Renderにデプロイする */}

0 commit comments

Comments
 (0)