TypeScriptでExpressのルーティングに型を安全に付ける方法!バックエンド開発初心者向け完全ガイド
生徒
「Node.jsのExpressを使ってWebサーバーの裏側の処理を作っているのですが、TypeScriptでルーティングの処理を書くときに、データの形に型を付けるにはどうすればいいですか?」
先生
「TypeScriptを使うと、画面から送られてくるデータや、画面に返すデータの種類をあらかじめ決めておくことができます。これにより、開発中の入力ミスや想定外のエラーを未然に防ぐことができるようになります。」
生徒
「画面からのデータを受け取るときに、型があると安心ですね!具体的にどう書くのか知りたいです!」
先生
「それでは、初心者の方にも分かりやすいように、基本的な設定から実際のプログラムの書き方まで順番に詳しく見ていきましょう!」
1. ルーティングとバックエンド開発の基本
Webアプリケーションの裏側を作るバックエンド開発において、ルーティングは非常に重要な役割を持っています。ルーティングとは、インターネットを通じて送られてきた要求に対して、どの処理を実行するかを交通整理のように案内する仕組みのことです。
例えば、利用者がパソコンやスマートフォンの画面でボタンを押したとき、その操作に応じて特定の情報が表示されます。このとき、画面側からはインターネットを通じて、特定のURLという住所に向けて処理の依頼が送られます。この依頼を受け取り、適切なデータを用意して画面に送り返す仕組み全体を、バックエンドのプログラムが担当しています。今回は、この裏側の仕組みを効率よく組み立てることができる人気の道具であるExpressという仕組みと、安全にプログラムを書くことができる言語であるTypeScriptを組み合わせて、データの通り道に型というルールを設定する方法を学びます。
2. TypeScriptで型を付ける理由とメリット
プログラミングを始めたばかりの段階では、なぜわざわざ型という制限を設けるのか不思議に思うかもしれません。型とは、プログラムの中で扱うデータの種類を明確に指定するための取扱説明書のようなものです。文字を入れる箱には文字だけを、数値を入れる箱には数値だけを入れるというルールをあらかじめ決めておきます。
型を付けずにプログラムを書くと、文字を計算しようとしてしまったり、存在しないデータを読み込もうとしたりして、画面が急に動かなくなるエラーが発生しやすくなります。TypeScriptを導入すると、プログラムを実際に動かす前の段階で、パソコンが自動的に書き間違いや使い方の誤りを見つけて教えてくれます。これにより、不具合の少ない頑丈なWebシステムを初心者でも安心して作ることができるようになります。特に、画面から送られてくるデータは中身が何であるかが変化しやすいため、型をしっかりと定義しておくことが安全な開発への第一歩となります。
3. 開発の準備と必要な道具の設定
まずは、プログラムを書くための環境を整える必要があります。一般的に、JavaScriptという言語を使ってサーバー側の処理を動かすためにNode.jsという仕組みをパソコンに導入します。今回はその発展形として、TypeScriptを使って動かすための設定を行います。
開発を始める際は、まずプロジェクトの初期化を行い、必要な部品をインストールします。具体的には、Webサーバーの土台となるExpressと、TypeScriptを動作させるための心臓部、そしてExpressの中でTypeScriptの型情報を利用できるようにするための専用の定義集を準備します。これらの道具が揃うことで、プログラムを入力している最中に、パソコンが次に書くべき文字を予測して候補を出してくれる機能が働くようになり、開発が劇的に快適になります。
4. 画面から送られてくる文字データを受け取るルーティング
Webサーバーが動いたら、最も基本となるデータの受け取り方を実装してみましょう。画面側からURLの末尾に特定の文字を付けて送られてくる形式を扱います。これをパスパラメータと呼びます。例えば、利用者の番号を指定して、その人の情報を個別に取得するような場面で使用します。
Expressでは、このURLの一部に含まれる文字を自動的に解析して、プログラムの中で利用できるようにしてくれます。TypeScriptを使ってここに型を適用する場合、URLから抽出される予定の文字がどのような名前で、どのような種類であるかを事前に登録します。ここでは、簡単な利用者情報を取得する具体的なプログラムを見てみましょう。
import express, { Request, Response } from 'express';
const app = express();
interface UserParams {
userId: string;
}
app.get('/users/:userId', (req: Request<UserParams>, res: Response) => {
const id = req.params.userId;
res.send(`指定された利用者の識別番号は ${id} です。`);
});
app.listen(3000, () => {
console.log('サーバーが起動しました');
});
上記のプログラムでは、画面から送られてくるURLの中の特定の文字を安全に受け取るために、専用の形を定義しています。これにより、存在しない名前でデータを読み取ろうとした場合に、開発中の画面でエラーを検知できるようになります。実行した際の結果は以下のようになります。
指定された利用者の識別番号は 123 です。
5. 新しい情報を登録する際の送信データに型を付ける方法
次に、画面の入力フォームなどに文字や数値を入力して、新しくデータをサーバーに送信する場合の処理を作ります。このときに送られてくるデータの塊をリクエストボディと呼びます。このデータは、目に見えない状態で通信の荷物の中に入って送られてきます。
この荷物の中身は、文字や数値が複雑に組み合わさっていることが多いため、型を定義する重要性が最も高まる部分です。今回は、新しく商品情報を登録することを想定して、商品の名前と価格という異なる種類のデータを安全に受け取る仕組みを作ります。Expressでこの荷物を解析するためには、データを解凍するための設定が必要になりますが、それと同時に型を組み合わせることで、荷物の中身を保証します。
import express, { Request, Response } from 'express';
const app = express();
app.use(express.json());
interface ProductBody {
productName: string;
price: number;
}
app.post('/products', (req: Request<{}, {}, ProductBody>, res: Response) => {
const name = req.body.productName;
const itemPrice = req.body.price;
res.send(`商品名:${name}、価格:${itemPrice}円を新しく登録しました。`);
});
app.listen(3000, () => {
console.log('商品登録サーバーが起動しました');
});
この処理では、送信されてくるデータの設計図を用意して、それを要求を意味するオブジェクトに組み込んでいます。これにより、価格という項目に対して間違えて文字データを送信してしまった場合や、商品名という文字を書き間違えた場合に、プログラムがすぐに異常を教えてくれます。動作させたときの結果は以下の通りです。
商品名:リンゴ、価格:150円を新しく登録しました。
6. 検索条件などの細かい絞り込みデータに型を付ける方法
Webサイトで検索を行う際、URLの最後に特定の記号をつけて条件を付け足すことがあります。これをクエリパラメータと呼びます。例えば、特定のキーワードで検索をしたり、表示する件数を制限したりするときに頻繁に使われる手法です。
この絞り込み条件も、サーバー側で正しく解釈して処理を行う必要があります。何でも自由に文字を送ることができる状態にしておくと、想定外の検索条件が指定されたときにシステムが混乱してしまいます。そのため、検索に使用するキーワードや、ページ番号といった条件に対しても、あらかじめ受け付ける型をしっかりと定めておくことで、安全な検索機能を実装することができます。
import express, { Request, Response } from 'express';
const app = express();
interface SearchQuery {
keyword: string;
page: string;
}
app.get('/search', (req: Request<{}, {}, {}, SearchQuery>, res: Response) => {
const searchWord = req.query.keyword;
const pageNumber = req.query.page;
res.send(`検索キーワード:${searchWord} の、${pageNumber}ページ目を表示しています。`);
});
app.listen(3000, () => {
console.log('検索サーバーが起動しました');
});
このように設定することで、検索処理を行う際に必要となる情報が確実に存在しているかどうかを、プログラムが判別しやすくなります。不特定多数の利用者が自由に条件を入力する場所だからこそ、事前の型定義による防衛が効果を発揮します。出力される結果のイメージは以下のようになります。
検索キーワード:書籍 の、2ページ目を表示しています。
7. 画面へ返却する応答データ全体の型安全性を高める仕組み
これまでは、画面から送られてくる要求側のデータに型を付ける方法を見てきましたが、今度はサーバーから画面に対して送り返す応答データの型についても考えてみましょう。画面側に返すデータの形式が急に変わってしまうと、今度は画面を表示する側のプログラムがエラーを起こしてしまいます。
サーバーからどのような形式のデータを返すかを約束しておくことで、バックエンドとフロントエンドの両方の開発がスムーズに進むようになります。TypeScriptでは、応答を担当するオブジェクトに対しても、返却するデータの形状を縛ることができます。これにより、必要な項目を返し忘れるといった初歩的なミスを完全に防ぐことが可能になります。
import express, { Request, Response } from 'express';
const app = express();
interface ApiResponse {
status: string;
message: string;
code: number;
}
app.get('/status', (req: Request, res: Response) => {
const responseData: ApiResponse = {
status: '成功',
message: 'システムは正常に稼働しています',
code: 200
};
res.json(responseData);
});
app.listen(3000, () => {
console.log('状態確認サーバーが起動しました');
});
このように、送信する直前のデータの塊に対して直接型を当てはめることで、構造が正しいかどうかをチェックできます。項目が足りなかったり、値の種類が異なったりした場合は、その場でエラーが発生して修正を促されます。実行した結果は、以下のような形として画面側に送信されます。
{"status":"成功","message":"システムは正常に稼働しています","code":200}