TypeScriptでExpressリクエスト拡張
TypeScriptでExpressリクエストオブジェクトを拡張する
Node.js, Express, TypeScript を使用したプログラミングにおいて、TypeScriptの型システムを活用してExpressのリクエストオブジェクトを拡張することで、より安全かつ効率的なコードを書くことができます。
拡張の目的
- ドキュメンテーション
リクエストオブジェクトの構造を明確化し、他の開発者が理解しやすくなります。 - コード補完
IDEがリクエストオブジェクトのプロパティやメソッドを認識し、自動補完機能を提供します。 - 型安全
リクエストオブジェクトのプロパティの型を明示的に定義することで、実行時に発生する型エラーを防ぎます。
拡張方法
- インターフェースの定義
リクエストオブジェクトに追加したいプロパティやメソッドを定義するインターフェースを作成します。
interface MyRequest extends Request {
customProperty: string;
customMethod: () => void;
}
- ミドルウェアの定義
拡張されたインターフェースを使用するミドルウェアを作成します。
import express from 'express';
const app = express();
app.use((req: MyRequest, res, next) => {
// 拡張されたプロパティやメソッドを使用
console.log(req.customProperty);
req.customMethod();
next();
});
- リクエストオブジェクトの型付け
Expressのルーティングハンドラーで拡張されたインターフェースを使用します。
app.get('/path', (req: MyRequest, res) => {
// 拡張されたプロパティやメソッドを使用
console.log(req.customProperty);
req.customMethod();
res.send('Hello, world!');
});
例
リクエストオブジェクトにカスタムプロパティ userId
とカスタムメソッド getUserData
を追加する例:
interface MyRequest extends Request {
userId: string;
getUserData: () => User;
}
app.use((req: MyRequest, res, next) => {
req.userId = '123';
req.getUserData = () => {
// ユーザーデータを取得するロジック
return { id: req.userId, name: 'John Doe' };
};
next();
});
注意点
- カスタムプロパティやメソッドは、実際にリクエストオブジェクトに存在する必要があります。
- ミドルウェアやルーティングハンドラーで拡張されたインターフェースを使用する必要があります。
- 拡張したインターフェースは、元の
Request
インターフェースを継承する必要があります。
例1: カスタムプロパティとメソッドを追加する
interface MyRequest extends Request {
userId: string;
getUserData: () => User;
}
app.use((req: MyRequest, res, next) => {
req.userId = '123';
req.getUserData = () => {
// ユーザーデータを取得するロジック
return { id: req.userId, name: 'John Doe' };
};
next();
});
- ミドルウェア
ミドルウェアでリクエストオブジェクトにカスタムプロパティとメソッドを設定します。 - インターフェースの定義
MyRequest
インターフェースを定義し、userId
プロパティとgetUserData
メソッドを追加します。
例2: リクエストボディの型付け
interface User {
id: string;
name: string;
}
app.post('/users', (req: Request<User>, res) => {
const user: User = req.body;
console.log(user);
// ...
});
- リクエストボディのアクセス
req.body
を使用して、型安全にリクエストボディにアクセスできます。 - リクエストボディの型付け
Request<User>
インターフェースを使用して、リクエストボディの型をUser
に指定します。
例3: カスタムヘッダーの型付け
interface MyRequest extends Request {
headers: {
authorization: string;
};
}
app.use((req: MyRequest, res, next) => {
const authorizationHeader = req.headers.authorization;
// ...
});
- カスタムヘッダーのアクセス
req.headers.authorization
を使用して、型安全にカスタムヘッダーにアクセスできます。 - カスタムヘッダーの型付け
MyRequest
インターフェースでheaders
プロパティを拡張し、カスタムヘッダーauthorization
の型を指定します。
例4: リクエストパラメータの型付け
app.get('/users/:id', (req: Request<{ id: string }>, res) => {
const userId = req.params.id;
// ...
});
- リクエストパラメータのアクセス
req.params.id
を使用して、型安全にリクエストパラメータにアクセスできます。 - リクエストパラメータの型付け
Request<{ id: string }>
インターフェースを使用して、リクエストパラメータid
の型を指定します。
Express-Validator を使用したバリデーションと拡張
- 拡張
バリデーション結果をリクエストオブジェクトに保存し、後続の処理で利用できます。 - バリデーション
リクエストデータのバリデーションを簡潔に記述できます。
import { body, validationResult } from 'express-validator';
app.post('/users', [
body('name').isLength({ min: 3 }).withMessage('Name must be at least 3 characters'),
body('email').isEmail().withMessage('Invalid email'),
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const user: User = r eq.body;
// ...
});
TypeScriptの型ガード を使用した条件付き拡張
- 拡張
型ガードが成功した場合にのみ、特定のプロパティやメソッドを追加できます。 - 型ガード
リクエストオブジェクトの型を条件付きで絞り込むことができます。
function isAuthenticatedRequest(req: Request): req is AuthenticatedRequest {
return req.isAuthenticated();
}
app.get('/protected', (req, res) => {
if (isAuthenticatedRequest(req)) {
const user = req.user;
// ...
} else {
res.status(401).send('Unauthorized');
}
});
カスタムミドルウェア を使用した拡張
- 柔軟性
任意の処理を追加することが可能です。 - カスタムミドルウェア
独自のロジックを実装し、リクエストオブジェクトを拡張できます。
function addCustomProperties(req: Request, res, next) {
req.customProperty = 'value';
req.customMethod = () => {
// ...
};
next();
}
app.use(addCustomProperties);
デコレーター を使用した拡張
- 再利用性
デコレーターを複数のクラスやメソッドに適用できます。 - デコレーター
クラスやメソッドにメタデータを追加し、拡張することができます。
function Authenticated() {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
const originalMethod = descriptor.value;
descriptor.valu e = function (...args: any[]) {
if (!this.is Authenticated()) {
throw new Error('Unauthorized');
}
return originalMethod.apply(this, args);
};
};
}
class UserController {
@Authenticated
protected getUserData() {
// ...
}
}
node.js express typescript