TypeScriptでNode.jsとExpressのリクエスト・レスポンスに型定義する方法を徹底解説!初心者向けバックエンド開発ガイド
生徒
「TypeScriptを使って、ウェブサーバー側で受け取るデータや返すデータに型をつける方法が知りたいです。Node.jsやExpressというものを使うと聞いたのですが、どうすれば安全にデータを扱えますか?」
先生
「TypeScriptでは、リクエストとレスポンスのそれぞれに型定義を行うことで、どのようなデータが送られてきて、どのようなデータを返すのかを明確に制限することができます。これによって、開発中のバグを大幅に減らすことができますよ。」
生徒
「なるほど!具体的な型定義の書き方や、実際のプログラムでの設定方法を詳しく教えてほしいです!」
先生
「それでは、初心者の方でも分かりやすいように、基本的な手順から具体的なプログラムの書き方まで順番に見ていきましょう!」
1. バックエンド開発とExpressの基本知識
ウェブサイトやスマートフォンのアプリを動かすとき、画面に見えない裏側でデータを処理するシステムが必要です。これをバックエンド開発、またはサーバーサイド開発と呼びます。パソコンをあまり触ったことがない方に向けて例えると、レストランの注文システムのようなものです。お客さんが注文する画面がフロントエンド、厨房で料理を作って提供する仕組みがバックエンドです。
この厨房の役割を効率よく作るための道具がNode.js(ノードジェーエス)とExpress(エクスプレス)です。Node.jsはパソコン上でJavaScript(ジャバスクリプト)というプログラミング言語を動かすための土台であり、Expressはその上でウェブサーバーを簡単に構築するための骨組みです。これらを活用することで、世界中のユーザーからの要求を処理するシステムを迅速に構築できるようになります。
2. リクエストとレスポンスとは何か
インターネットの通信は、基本的に二つのやり取りで成り立っています。それがリクエスト(要求)とレスポンス(応答)です。ユーザーがウェブブラウザでページを開いたり、ボタンを押したりしてサーバーに情報を求める行動がリクエストです。一方で、サーバーがその要求を受け取り、画面に表示するデータや処理結果をユーザーに送り返す行動がレスポンスです。
例えば、あなたがインターネットの検索窓にキーワードを入力して検索ボタンを押したとします。このとき、入力されたキーワードを含んだ「検索結果をください」というメッセージがリクエストとしてサーバーに届きます。サーバーはそのキーワードを元にデータベースから情報を探し出し、「これが検索結果のデータです」と送り返します。この送り返されるデータ一式がレスポンスです。この往復のやり取りを正しく管理することが、バックエンド開発の最も重要な仕事です。
3. TypeScriptで型を定義するメリット
JavaScriptは柔軟で書きやすい反面、どのようなデータでも受け入れてしまうため、打ち間違いや勘違いによるエラーが本番環境で発生しやすいという弱点があります。そこで登場するのがTypeScript(タイプスクリプト)です。TypeScriptはJavaScriptに型(データの手順書やルールのこと)を追加した言語です。文字列なのか、数字なのか、それとも複数の情報が集まった複雑なデータなのかをあらかじめ厳しく指定します。
リクエストとレスポンスに型を定義する最大のメリットは、プログラムを書いている最中にパソコンが「そのデータは形が違いますよ」とリアルタイムで教えてくれる点にあります。例えば、本来は数字を受け取るはずの場所に文字が送られてきた場合、プログラムを実行する前に赤線でエラーを表示してくれます。これにより、開発者は勘違いによる不具合を未然に防ぐことができ、安全で壊れにくいシステムを作ることができます。また、どのようなデータが届くのかがソースコードを見るだけで一目でわかるため、他の人と一緒に開発を進める際にも非常に役立ちます。
4. Expressの標準的な型定義の基本
Expressを使ってサーバーを構築する際、TypeScriptではあらかじめ用意されている専用の型を利用します。それがRequest(リクエスト型)とResponse(レスポンス型)です。これらはExpressのパッケージから取り込んで使用します。型を割り当てることで、それぞれの変数の中にどのような関数やデータが含まれているかをパソコンが認識できるようになります。
まずは、最もシンプルにサーバーへアクセスがあった際、お決まりのメッセージを返すプログラムの書き方を見てみましょう。以下のコードでは、関数の引数であるreqにリクエスト型を、resにレスポンス型を指定しています。これにより、Expressのルールに則った正しい通信処理が保証されます。
import express, { Request, Response } from 'express';
const app = express();
app.get('/hello', (req: Request, res: Response) => {
res.send("こんにちは!サーバーからの応答です。");
});
このプログラムを実行し、ウェブブラウザから該当のURLにアクセスすると、画面に以下のような結果が出力されます。
こんにちは!サーバーからの応答です。
5. 送られてくるデータ(リクエスト)に独自の型をつける方法
実際の開発では、ユーザーから様々なデータが送られてきます。例えば、ユーザーの新規登録時には「名前」や「年齢」といった情報が送られてきます。このように送られてくるデータの塊をリクエストボディ(Body)と呼びます。このリクエストボディに対して、独自の型を定義して制限をかける方法を解説します。
型を定義するときはinterface(インターフェース)という仕組みを使います。これは、データの設計図を作るようなものです。今回は、名前と年齢を持つユーザー情報の設計図を作り、それをExpressの型に組み込みます。ExpressのRequest型は、内部にいくつかの細かい型を設定できる仕組みになっているため、自分で作った設計図を当てはめることができます。
import express, { Request, Response } from 'express';
const app = express();
app.use(express.json());
interface UserRegisterBody {
userName: string;
userAge: number;
}
app.post('/register', (req: Request<{}, {}, UserRegisterBody>, res: Response) => {
const { userName, userAge } = req.body;
res.send(`登録完了しました:${userName}さん(${userAge}歳)`);
});
上記のコードでは、Request<{}, {}, UserRegisterBody>という特殊な書き方をしています。これは、三番目の位置に指定した型がリクエストボディの型として適用されるというExpressの決まり事です。これにより、req.bodyの中には必ず文字列の名前と数字の年齢が入っているとパソコンが理解してくれます。この処理を実行した結果の出力は以下のようになります。
登録完了しました:山田太郎さん(25歳)
6. 返すデータ(レスポンス)に独自の型をつける方法
リクエストだけでなく、サーバーから送り出すデータ(レスポンス)にも型を定義することが重要です。特に、最近のシステム開発では**JSON(ジェイソン)**という形式でデータを返却することが一般的です。JSONとは、データを鍵と値のペアで表現したテキスト形式のデータで、異なるシステム間でのデータのやり取りに広く使われています。レスポンスに型をつけておけば、想定外の形式でデータを返してしまうミスを防げます。
レスポンスの型を制限する場合も、先ほどと同様にinterfaceで返却データの設計図を作成します。そして、ExpressのResponse型の括弧の中にその設計図を指定します。今回は、処理が成功したかどうかの状態と、メッセージを含んだデータを返す処理を作ってみましょう。
import express, { Request, Response } from 'express';
const app = express();
interface CustomResponseData {
success: boolean;
message: string;
}
app.get('/status', (req: Request, res: Response<CustomResponseData>) => {
res.json({
success: true,
message: "システムは正常に稼働しています。"
});
});
このプログラムによって、res.jsonの中に指定するデータは、必ずsuccessという真偽値(正しいか間違いかを表す値)と、messageという文字列を含んでいなければならないというルールが強制されます。もし全く関係のないデータを返そうとすると、TypeScriptがエラーを発生させて開発者に教えてくれます。この処理の出力結果は以下のようになります。
{
"success": true,
"message": "システムは正常に稼働しています。"
}
7. URLに含まれるパラメータに型を定義する応用
ウェブサイトのURLを見ていると、特定の番号や識別子が含まれていることがあります。例えば、特定の商品ページを開くときのURLに含まれる商品の管理番号などがこれに該当します。このURLの後ろにくっついている変化する値のことをパスパラメータ(Params)と呼びます。Expressでは、このパラメータも型安全に扱うことができます。
パスパラメータの型を定義する場合、ExpressのRequest型の最初(一番左側)の枠に設計図を指定します。これにより、URLから抽出される値がどのような名前で、どのような形式であるべきかを厳密に決めることができます。今回は、特定の商品IDを受け取る仕組みを作成してみましょう。
import express, { Request, Response } from 'express';
const app = express();
interface ProductParams {
productId: string;
}
app.get('/product/:productId', (req: Request<ProductParams>, res: Response) => {
const { productId } = req.params;
res.send(`指定された商品のIDは ${productId} です。`);
});
上記のプログラムでは、URLの中にある:productIdという部分に入ってきた文字を、req.params.productIdとして安全に取得しています。型が定義されているため、存在しないパラメータ名を間違えて書いてしまうといった単純な記述ミスを完全に排除することができます。ブラウザから特定のIDでアクセスした際の出力結果は以下の通りです。
指定された商品のIDは item-999 です。