
🔰データベースプロフェッショナル養成工房💫第8講 TypeScriptから登録処理を呼び出そう――画面と保存部品の接続
【プロローグ】
ミカ「入力検証に成功しました。これで顧客を保存できますね」
教授「まだ顧客データはブラウザーの中にいる」
ミカ「登録ボタンも押しました」
教授「受付で申込書を確認しただけだ。金庫番には届いていない」
ミカ「ブラウザーからNode.jsへ渡す通路が必要なのですね」
教授「今回はその通路と、登録処理の入口を作る」
今回は、ブラウザー側のTypeScriptからNode.js側の登録処理を呼び出します。画面で検証したCustomerInputをJSONに変換し、POST /api/customersへ送信します。
Node.js側ではデータを受け取り、もう一度入力を検証して、登録処理部品を呼び出します。
今回はPostgreSQLへの保存直前までを作ります。INSERTによる本当の保存は第9講で実装します。
【今回の到達点】
今回完成させる流れは次のとおりです。
1.利用者が顧客登録画面へ入力する
2.ブラウザー側TypeScriptが入力を検証する
3.fetchでNode.jsへJSONを送る
4.ExpressがPOST /api/customersを受け取る
5.Node.js側でも入力を検証する
6.registerCustomer関数を呼び出す
7.処理結果をJSONでブラウザーへ返す
8.画面へ成功又はエラーを表示する
PostgreSQLへのINSERTはまだ行わないため、画面には次のメッセージを表示します。
Node.jsの登録処理まで到達しました。
PostgreSQLへの保存はまだ行っていません。【今回追加する部品】
新しく作る主なファイルは次のとおりです。
・src/validation/customer-validation.ts――共通入力検証部品
・src/server/customer-registration.ts――登録処理部品
・src/server/server.ts――画面と登録処理をつなぐAPI
・tsconfig.server.json――Node.js側TypeScriptの確認設定
既存の次のファイルも修正します。
・src/browser/customer-form.ts――fetchによる送信を追加
・tsconfig.browser.json――共通検証部品を変換対象へ追加
・public/styles.css――送信中のボタン表示を追加
【完成後のフォルダー構成】
今回の作業後は、次の構成になります。
customer-management
├─ database
│ ├─ docs
│ └─ sql
├─ public
│ ├─ js
│ │ ├─ browser
│ │ │ └─ customer-form.js
│ │ ├─ types
│ │ │ └─ customer.js
│ │ └─ validation
│ │ └─ customer-validation.js
│ ├─ index.html
│ └─ styles.css
├─ src
│ ├─ browser
│ │ └─ customer-form.ts
│ ├─ server
│ │ ├─ customer-registration.ts
│ │ └─ server.ts
│ ├─ types
│ │ └─ customer.ts
│ ├─ validation
│ │ └─ customer-validation.ts
│ └─ main.ts
├─ dist
├─ package.json
├─ package-lock.json
├─ tsconfig.json
├─ tsconfig.browser.json
└─ tsconfig.server.jsonpublic/jsの中はTypeScriptから自動生成されます。手動で編集するのはsrc内のTypeScriptです。
【画面とNode.jsを同じサーバーから動かす】
第7講では、画面表示用としてhttp-serverを使用しました。
今回はExpressが次の二つを担当します。
・publicフォルダーのHTML、CSS、JavaScriptをブラウザーへ渡す
・POST /api/customersで顧客データを受け取る
画面とAPIを同じhttp://127.0.0.1:3000から動かすため、今回はCORSの追加設定は必要ありません。
【Node.jsのバージョンを確認する】
Visual Studio Codeでターミナルを開き、次のコマンドを実行します。
node --versionたとえば、次のように表示されます。
v22.18.0Express 5はNode.js 18以上を必要とします。Express公式:5.x API Reference
Node.js 18以上であれば、そのまま進めます。
【必要なパッケージをインストールする】
ターミナルの現在位置がcustomer-managementであることを確認し、次のコマンドを実行します。
npm install express続いて、開発用パッケージをインストールします。
npm install --save-dev @types/express @types/node tsx各パッケージの役割は次のとおりです。
・express――Web画面とAPIを提供する
・@types/express――Expressの型情報
・@types/node――Node.jsの型情報
・tsx――開発中にTypeScriptをNode.jsで直接実行する
インストール後、package.jsonとpackage-lock.jsonが更新されます。これは正常です。
【入力検証を共通部品へ移す】
第7講では、入力検証関数をcustomer-form.tsの中へ書きました。
しかし、Node.js側でも同じ規則を使います。同じ検証コードを二か所へコピーすると、一方だけ修正される危険があります。
そこで、画面側とNode.js側の両方から使える共通部品へ移します。
【validationフォルダーを作る】
Visual Studio Codeで次の操作を行います。
1.エクスプローラーでsrcを右クリックします。
2.「新しいフォルダー」を選択します。
3.フォルダー名をvalidationと入力します。
4.作成したvalidationを右クリックします。
5.「新しいファイル」を選択します。
6.ファイル名をcustomer-validation.tsと入力します。
作成場所は次のとおりです。
customer-management/src/validation/customer-validation.ts【共通入力検証部品を書く】
customer-validation.tsへ、次のコードを入力します。
import type {
CustomerInput,
} from "../types/customer.js";
export type CustomerField =
keyof CustomerInput;
export type ValidationError = {
field: CustomerField;
message: string;
};
export function toNullableString(
value: string,
): string | null {
const trimmedValue = value.trim();
return trimmedValue === ""
? null
: trimmedValue;
}
export function parseCustomerInput(
value: unknown,
): CustomerInput | null {
if (
typeof value !== "object" ||
value === null
) {
return null;
}
const data =
value as Record<string, unknown>;
const customerCode =
data["customerCode"];
const customerName =
data["customerName"];
const phone = data["phone"];
const email = data["email"];
if (
typeof customerCode !== "string" ||
typeof customerName !== "string"
) {
return null;
}
if (
phone !== undefined &&
phone !== null &&
typeof phone !== "string"
) {
return null;
}
if (
email !== undefined &&
email !== null &&
typeof email !== "string"
) {
return null;
}
return {
customerCode:
customerCode.trim(),
customerName:
customerName.trim(),
phone:
typeof phone === "string"
? toNullableString(phone)
: null,
email:
typeof email === "string"
? toNullableString(email)
: null,
};
}
export function validateCustomerInput(
input: CustomerInput,
): ValidationError[] {
const errors: ValidationError[] = [];
if (input.customerCode === "") {
errors.push({
field: "customerCode",
message:
"顧客コードを入力してください。",
});
} else if (
input.customerCode.length > 20
) {
errors.push({
field: "customerCode",
message:
"顧客コードは20文字以内で" +
"入力してください。",
});
}
if (input.customerName === "") {
errors.push({
field: "customerName",
message:
"顧客名を入力してください。",
});
} else if (
input.customerName.length > 100
) {
errors.push({
field: "customerName",
message:
"顧客名は100文字以内で" +
"入力してください。",
});
}
if (
input.phone !== null &&
input.phone.length > 30
) {
errors.push({
field: "phone",
message:
"電話番号は30文字以内で" +
"入力してください。",
});
}
if (input.email !== null) {
if (input.email.length > 254) {
errors.push({
field: "email",
message:
"メールアドレスは254文字以内で" +
"入力してください。",
});
} else if (
!isValidEmail(input.email)
) {
errors.push({
field: "email",
message:
"メールアドレスの形式を" +
"確認してください。",
});
}
}
return errors;
}
function isValidEmail(
email: string,
): boolean {
const emailPattern =
/^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailPattern.test(email);
}入力後、Ctrl + Sで保存します。
【parseCustomerInputが必要な理由】
ブラウザーから届くJSONを、最初から正しいCustomerInputだと信用してはいけません。
たとえば、外部から次のようなデータが送られる可能性があります。
{
"customerCode": 123,
"customerName": true
}parseCustomerInputは、受け取った値をいったんunknownとして扱い、次の内容を確認します。
・全体がオブジェクトか
・顧客コードが文字列か
・顧客名が文字列か
・電話番号が文字列、null又は未指定か
・メールアドレスが文字列、null又は未指定か
データの形が正しくなければnullを返します。
【tsconfig.browser.jsonを修正する】
tsconfig.browser.jsonを開き、includeへ検証フォルダーを追加します。
修正前は次の状態です。
"include": [
"src/browser/**/*.ts",
"src/types/**/*.ts"
]次のように修正します。
"include": [
"src/browser/**/*.ts",
"src/types/**/*.ts",
"src/validation/**/*.ts"
]これで、共通入力検証部品もブラウザー用JavaScriptへ変換されます。
【serverフォルダーを作る】
次に、Node.js側の部品を作ります。
1.エクスプローラーでsrcを右クリックします。
2.「新しいフォルダー」を選択します。
3.フォルダー名をserverと入力します。
4.作成したserverを右クリックします。
5.「新しいファイル」を選択します。
6.ファイル名をcustomer-registration.tsと入力します。\
【登録処理部品を作る】
src/server/customer-registration.tsへ、次のコードを入力します。
import type {
CustomerInput,
} from "../types/customer.js";
import {
validateCustomerInput,
type ValidationError,
} from "../validation/customer-validation.js";
export type RegistrationResult =
| {
success: true;
saved: false;
message: string;
}
| {
success: false;
saved: false;
message: string;
errors: ValidationError[];
};
export async function registerCustomer(
input: CustomerInput,
): Promise<RegistrationResult> {
const errors =
validateCustomerInput(input);
if (errors.length > 0) {
return {
success: false,
saved: false,
message:
"入力内容を確認してください。",
errors,
};
}
return {
success: true,
saved: false,
message:
"Node.jsの登録処理まで到達しました。" +
"PostgreSQLへの保存は" +
"まだ行っていません。",
};
}入力後、Ctrl + Sで保存します。
【登録処理部品の役割】
registerCustomerは、画面やExpressの詳しい仕組みを知りません。
受け取るものはCustomerInputです。
input: CustomerInput返すものは、登録処理の結果です。
Promise<RegistrationResult>今回は入力を再検証し、成功結果を返すところまでです。
次回、この関数の成功部分からPostgreSQLの保存部品を呼び出します。
最初から画面イベントの中へSQLを書かないことが重要です。
【server.tsを作る】
src/serverを右クリックして、もう一つファイルを作ります。
server.ts作成場所は次のとおりです。
customer-management/src/server/server.ts【Expressサーバーを書く】
server.tsへ、次のコードを入力します。
import { join } from "node:path";
import express, {
type Request,
type Response,
} from "express";
import {
parseCustomerInput,
} from "../validation/customer-validation.js";
import {
registerCustomer,
} from "./customer-registration.js";
const app = express();
const port = 3000;
const host = "127.0.0.1";
const publicDirectory =
join(process.cwd(), "public");
app.use(express.json());
app.use(
express.static(publicDirectory),
);
app.post(
"/api/customers",
async (
request: Request,
response: Response,
) => {
console.log(
"POST /api/customersを受信しました。",
);
const customerInput =
parseCustomerInput(request.body);
if (customerInput === null) {
response.status(400).json({
success: false,
saved: false,
message:
"入力データの形が正しくありません。",
});
return;
}
const result =
await registerCustomer(customerInput);
if (!result.success) {
response
.status(400)
.json(result);
return;
}
response
.status(200)
.json(result);
},
);
app.listen(
port,
host,
() => {
console.log(
`顧客管理サーバーを起動しました。`,
);
console.log(
`http://${host}:${port}`,
);
},
);入力後、Ctrl + Sで保存します。
【Expressが担当する二つの仕事】
【画面をブラウザーへ渡す】
次の処理が、publicフォルダーを公開します。
app.use(
express.static(publicDirectory),
);Expressのexpress.staticを使うと、HTML、CSS、JavaScriptなどの静的ファイルを配信できます。Express公式:Serving static files
【顧客登録APIを用意する】
次の部分が、顧客登録APIです。
app.post(
"/api/customers",
async (
request: Request,
response: Response,
) => {Expressのルートは、HTTPメソッド、パス、処理関数を組み合わせて定義します。Express公式:Basic routing
今回は次の組合せです。
HTTPメソッド:POST
パス:/api/customers
処理:顧客データを受け取って登録処理を呼び出す【JSONを受け取る】
次の処理が、送信されたJSONを読み取れるようにします。
app.use(express.json());この指定がないと、request.bodyから送信データを取得できません。
【tsconfig.server.jsonを作る】
プロジェクトの一番上に、Node.js側の型を確認する設定ファイルを作ります。
1.customer-managementを右クリックします。
2.「新しいファイル」を選択します。
3.ファイル名をtsconfig.server.jsonと入力します。
4.次の内容を入力します。
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"types": [
"node"
],
"esModuleInterop": true,
"verbatimModuleSyntax": false,
"declaration": false,
"declarationMap": false,
"noEmit": true
},
"include": [
"src/server/**/*.ts",
"src/types/**/*.ts",
"src/validation/**/*.ts"
]
}入力後、Ctrl + Sで保存します。
今回はNode.js側をtsxで実行するため、noEmitをtrueにしています。
"noEmit": trueこの設定ではJavaScriptファイルを作らず、型の誤りだけを確認します。
【customer-form.tsを置き換える】
src/browser/customer-form.tsを開きます。
第7講で入力した内容をすべて選択し、次のコードへ置き換えます。
import type {
CustomerInput,
} from "../types/customer.js";
import {
toNullableString,
validateCustomerInput,
type ValidationError,
} from "../validation/customer-validation.js";
type RegistrationResponse = {
success: boolean;
saved: boolean;
message: string;
errors?: ValidationError[];
};
const form =
document.querySelector<HTMLFormElement>(
"#customer-form",
);
const submitButton =
document.querySelector<HTMLButtonElement>(
"#register-button",
);
const messageElement =
document.querySelector<HTMLParagraphElement>(
"#form-message",
);
const customerCodeInput =
document.querySelector<HTMLInputElement>(
"#customer-code",
);
const customerNameInput =
document.querySelector<HTMLInputElement>(
"#customer-name",
);
const phoneInput =
document.querySelector<HTMLInputElement>(
"#phone",
);
const emailInput =
document.querySelector<HTMLInputElement>(
"#email",
);
if (
!form ||
!submitButton ||
!messageElement ||
!customerCodeInput ||
!customerNameInput ||
!phoneInput ||
!emailInput
) {
throw new Error(
"顧客登録画面の部品を取得できませんでした。",
);
}
const fieldElements:
Record<
keyof CustomerInput,
HTMLInputElement
> = {
customerCode: customerCodeInput,
customerName: customerNameInput,
phone: phoneInput,
email: emailInput,
};
form.addEventListener(
"submit",
async (event) => {
event.preventDefault();
clearValidationDisplay();
const customerInput =
readCustomerInput();
const errors =
validateCustomerInput(customerInput);
if (errors.length > 0) {
showValidationErrors(errors);
return;
}
setSubmitting(true);
try {
const result =
await sendCustomerInput(
customerInput,
);
if (!result.success) {
if (
result.errors &&
result.errors.length > 0
) {
showValidationErrors(
result.errors,
);
} else {
showMessage(
result.message,
"error",
);
}
return;
}
showMessage(
result.message,
"success",
);
} catch (error) {
const errorMessage =
error instanceof Error
? error.message
: "不明なエラーです。";
showMessage(
"Node.jsの登録処理を" +
"呼び出せませんでした。" +
errorMessage,
"error",
);
} finally {
setSubmitting(false);
}
},
);
form.addEventListener(
"reset",
() => {
window.requestAnimationFrame(
() => clearValidationDisplay(),
);
},
);
for (
const inputElement
of Object.values(fieldElements)
) {
inputElement.addEventListener(
"input",
() => clearValidationDisplay(),
);
}
function readCustomerInput():
CustomerInput {
return {
customerCode:
customerCodeInput.value.trim(),
customerName:
customerNameInput.value.trim(),
phone:
toNullableString(phoneInput.value),
email:
toNullableString(emailInput.value),
};
}
async function sendCustomerInput(
input: CustomerInput,
): Promise<RegistrationResponse> {
const response = await fetch(
"/api/customers",
{
method: "POST",
headers: {
"Content-Type":
"application/json",
},
body: JSON.stringify(input),
},
);
const result =
(await response.json())
as RegistrationResponse;
if (!response.ok) {
return {
...result,
success: false,
};
}
return result;
}
function setSubmitting(
isSubmitting: boolean,
): void {
submitButton.disabled =
isSubmitting;
submitButton.textContent =
isSubmitting
? "送信中..."
: "顧客を登録する";
}
function clearValidationDisplay():
void {
messageElement.textContent = "";
messageElement.classList.remove(
"is-error",
"is-success",
);
for (
const inputElement
of Object.values(fieldElements)
) {
inputElement.classList.remove(
"is-invalid",
);
inputElement.removeAttribute(
"aria-invalid",
);
}
}
function showValidationErrors(
errors: ValidationError[],
): void {
messageElement.textContent =
errors
.map(
(error) => `・${error.message}`,
)
.join("\n");
messageElement.classList.add(
"is-error",
);
for (const error of errors) {
const inputElement =
fieldElements[error.field];
inputElement.classList.add(
"is-invalid",
);
inputElement.setAttribute(
"aria-invalid",
"true",
);
}
const firstError = errors[0];
if (firstError) {
fieldElements[
firstError.field
].focus();
}
}
function showMessage(
message: string,
state: "error" | "success",
): void {
messageElement.textContent =
message;
messageElement.classList.add(
state === "error"
? "is-error"
: "is-success",
);
}入力後、Ctrl + Sで保存します。
【fetchでNode.jsへ送信する】
次の部分が、ブラウザーからNode.jsへ顧客情報を送っています。
const response = await fetch(
"/api/customers",
{
method: "POST",
headers: {
"Content-Type":
"application/json",
},
body: JSON.stringify(input),
},
);それぞれの意味は次のとおりです。
・/api/customers――送信先
・POST――新しいデータを渡す方法
・Content-Type: application/json――送信内容がJSONであることを伝える
・JSON.stringify(input)――TypeScriptのオブジェクトをJSON文字列へ変換する
fetchはHTTPリクエストを送り、結果をPromiseとして返します。HTTPエラーでも必ずしも例外にはならないため、response.okも確認します。MDN:Using the Fetch API
【画面側とNode.js側で二度検証する】
処理の流れは次のようになります。
1.画面側のvalidateCustomerInputで検証する
2.問題がなければNode.jsへ送る
3.Node.js側のparseCustomerInputでデータの形を確認する
4.registerCustomerで再び内容を検証する
5.問題がなければ成功結果を返す
画面側の検証は利用者へ早く知らせるためにあります。Node.js側の検証は、受信したデータを信用しないためにあります。
同じ検証関数を共通部品にしたため、画面とNode.jsで規則が食い違いにくくなります。
【送信中はボタンを無効にする】
次の処理により、送信中に登録ボタンを何度も押せないようにしています。
function setSubmitting(
isSubmitting: boolean,
): void {
submitButton.disabled =
isSubmitting;
submitButton.textContent =
isSubmitting
? "送信中..."
: "顧客を登録する";
}送信開始時は次のように呼び出します。
setSubmitting(true);処理が終わると、finallyで元に戻します。
setSubmitting(false);【styles.cssへ送信中の表示を追加する】
public/styles.cssを開き、.primary-button:hoverの後へ次のCSSを追加します。
.primary-button:disabled {
cursor: wait;
opacity: 0.65;
transform: none;
}入力後、Ctrl + Sで保存します。
【第7講のhttp-serverを停止する】
第7講のhttp-serverが動いている場合は、そのターミナルをクリックしてCtrl + Cを押します。
3000番ポートでhttp-serverとExpressを同時に起動することはできません。
本講以降は、Expressが画面とAPIの両方を提供します。
【Node.js側の型を確認する】
新しいターミナルを開き、次のコマンドを実行します。
npx tsc -p tsconfig.server.jsonエラーが表示されず、入力待ちへ戻れば成功です。
noEmitを指定しているため、JavaScriptファイルは作成されません。ここでは型の誤りだけを確認しています。
【ターミナル1でブラウザー用TypeScriptを監視する】
一つ目のターミナルで、次のコマンドを実行します。
npx tsc -p tsconfig.browser.json --watch次の表示を確認します。
Found 0 errors. Watching for file changes.このターミナルは動かしたままにします。
【ターミナル2でExpressを起動する】
ターミナル右上の+を押し、二つ目のターミナルを開きます。
次のコマンドを実行します。
npx tsx watch src/server/server.tstsx watchは、TypeScriptファイルを実行し、修正時にNode.jsの処理を再起動します。tsx公式:GitHub
次の表示が出れば成功です。
顧客管理サーバーを起動しました。
http://127.0.0.1:3000ブラウザーで次のURLを開きます。
http://127.0.0.1:3000【画面から登録処理を呼び出す】
次の架空データを入力します。
顧客コード:CUST-0002
顧客名:みどり工房
電話番号:06-0000-0000
メールアドレス:info@midori.example「顧客を登録する」を押します。
ボタンが一時的に次の表示へ変わります。
送信中...処理に成功すると、画面へ次のメッセージが表示されます。
Node.jsの登録処理まで到達しました。
PostgreSQLへの保存はまだ行っていません。Expressを動かしているターミナルには、次の表示が追加されます。
POST /api/customersを受信しました。これで、ブラウザーからNode.jsまで顧客データが届いたことを確認できます。
【ブラウザーの開発者ツールで確認する】
ブラウザーでF12キーを押し、開発者ツールを開きます。
1.「ネットワーク」又は「Network」を選択します。
2.顧客登録画面で登録ボタンを押します。
3.一覧に表示されたcustomersを選択します。
4.「Payload」又は「ペイロード」を開きます。
送信内容として、次のようなJSONを確認できます。
{
"customerCode": "CUST-0002",
"customerName": "みどり工房",
"phone": "06-0000-0000",
"email": "info@midori.example"
}「Response」又は「応答」には、次のような結果が表示されます。
{
"success": true,
"saved": false,
"message": "Node.jsの登録処理まで到達しました。PostgreSQLへの保存はまだ行っていません。"
}特に確認するのは、次の部分です。
"saved": falseAPIへの送信は成功していますが、PostgreSQLへの保存はまだ行っていないことを表しています。
【なぜ画面からPostgreSQLへ直接つながないのか】
ブラウザーからPostgreSQLへ直接接続すると、次の情報をブラウザーへ置かなければなりません。
・データベースの接続先
・データベース名
・利用者名
・パスワード
これらは利用者から見える場所へ置いてはいけません。
今回の構成では、ブラウザーはNode.jsのAPIだけを呼び出します。Node.jsが入力検証、登録処理、データベース操作を担当します。
【画面からSQLを見えなくする】
画面側のTypeScriptが知っているのは、次の送信先だけです。
POST /api/customers画面側は、次のことを知りません。
・PostgreSQLの接続方法
・customersテーブルの物理的な保存場所
・実行するINSERT文
・データベースの利用者名とパスワード
画面を変更してもSQLへ影響せず、SQLを変更しても画面へ影響しにくい構成を目指します。
【まだ保存済みとは表示しない】
今回はAPIから成功結果が返っても、画面に「顧客を登録しました」と表示してはいけません。
PostgreSQLへの保存が完了していないからです。
今回のメッセージは、次の表現にしています。
Node.jsの登録処理まで到達しました。
PostgreSQLへの保存はまだ行っていません。実際に完了していない処理を、画面だけ成功したように見せないことが重要です。
【演習】
次の架空データを画面から送信してください。
顧客コード:CUST-0801
顧客名:星空サービス
電話番号:空欄
メールアドレス:contact@hoshizora.example次の三点を確認してください。
1.画面に「Node.jsの登録処理まで到達しました」と表示される。
2.開発者ツールのNetworkにPOST /api/customersが表示される。
3.DBeaverで確認してもCUST-0801はまだ保存されていない。
【演習の解答】
DBeaverでcustomer_managementへ接続し、SQLエディターで次のSQLを実行します。
SELECT
customer_id,
customer_code,
customer_name,
phone,
email,
created_at
FROM public.customers
WHERE customer_code = 'CUST-0801';結果が0件であれば正常です。
画面からNode.jsの登録処理までは到達していますが、INSERTをまだ実行していないため、PostgreSQLには保存されていません。
今回の処理結果は次のとおりです。
ブラウザーでの入力 成功
画面側の入力検証 成功
Node.jsへのJSON送信 成功
Node.js側の入力検証 成功
登録処理関数の呼出し 成功
PostgreSQLへの保存 未実装【うまく動かない場合】
【EADDRINUSEと表示される】
3000番ポートを別のプログラムが使用しています。
第7講のhttp-serverが動いている場合は、該当するターミナルでCtrl + Cを押します。
【Cannot find package expressと表示される】
プロジェクト直下で次のコマンドを実行します。
npm install express【Cannot find name processと表示される】
Node.jsの型情報を確認します。
npm install --save-dev @types/nodeその後、tsconfig.server.jsonに次の指定があるか確認します。
"types": [
"node"
]【画面は開くが登録ボタンでエラーになる】
Expressを動かしているターミナルを確認します。
次のコマンドが実行中である必要があります。
npx tsx watch src/server/server.ts【404 Not Foundと表示される】
ブラウザーの送信先とExpressのパスが一致しているか確認します。
fetch("/api/customers", ...)app.post("/api/customers", ...)両方が同じ/api/customersでなければなりません。
【Failed to fetchと表示される】
次の点を確認します。
・Expressが起動している
・ブラウザーをhttp://127.0.0.1:3000で開いている
・file:///から開いていない
・ターミナルにエラーが出ていない
【修正したコードが反映されない】
次の順序で確認します。
1.TypeScriptファイルをCtrl + Sで保存する。
2.ブラウザー用ターミナルにFound 0 errorsと表示されることを確認する。
3.Express側が再起動したことを確認する。
4.ブラウザーでCtrl + F5を押す。
【開発を終了する】
作業を終了するときは、二つのターミナルを停止します。
1.ブラウザー用TypeScriptを監視しているターミナルを選択する。
2.Ctrl + Cを押す。
3.Expressを実行しているターミナルを選択する。
4.Ctrl + Cを押す。\
【今回の完成判定】
次の状態になれば、第8講は完了です。
・Expressをインストールできた
・共通入力検証部品を作成できた
・ブラウザー側とNode.js側で同じ検証関数を使用できた
・POST /api/customersを作成できた
・ブラウザーからJSONを送信できた
・Node.js側でJSONを受信できた
・登録処理関数を呼び出せた
・APIの結果を画面へ表示できた
・送信中に登録ボタンを無効化できた
・開発者ツールで送信内容と応答を確認できた
・PostgreSQLにはまだ保存されていないことを確認できた
・画面へデータベースの接続情報を書かずに済んだ
【次回予告】
ミカ「顧客データがNode.jsの登録処理まで届きました」
教授「受付から金庫室の前までは来た」
ミカ「次はいよいよPostgreSQLへ保存します」
教授「そこで初めて『登録しました』と言える」
次回は、**第9講「顧客情報をPostgreSQLへ保存しよう――INSERT処理」**です。pgを使ってPostgreSQLへ接続し、パラメーター化SQLで顧客情報を安全に保存します。