🪪 バリデーター
Validatorsは、スキーマバリデーションを超えた追加のバリデーションを実行する関数です。これらはルートhandlersの前に実行され、ビジネスロジック、リソースの存在、認証などをチェックできます。
🔍 バリデーターとは何ですか?
Validatorsは、handlersの前にリクエストパイプラインで実行される述語関数です。これらは以下を実行できます:
- ビジネスロジックのバリデーション(例:「このリソースは存在しますか?」)
- 認証のチェック(例:「ユーザーは認証されていますか?」)
- パラメータのバリデーション(例:「これは有効なUUIDですか?」)
- 制約の強制(例:「ユーザーに権限がありますか?」)
┌─────────────┐
│ Request │
├─────────────┤
│ Schema │ ← JSON Schemaバリデーション
│ Validators │ ← カスタムビジネスロジック
│ Handler │ ← ルートロジック
└─────────────┘
主な特徴:
- 非同期関数 - データベースクエリを実行できます
- 失敗時にスロー - 一貫したエラーレスポンスのために
NodeblocksErrorを使用します - 合成可能 - 複数のvalidatorsを一緒にチェーンできます
- コンテキスト対応 - データベース、リクエストパラメータ、コンテキストにアクセスできます
📋 利用可能なバリデーター
ValidatorsはBackend Blocksドキュメントでドメインごとに整理されています。各blockは、その操作のためのドメイン固有のvalidatorsを提供します。
バリデーターカテゴリ
| カテゴリ | Validators | ドキュメント |
|---|---|---|
| Authentication | isAuthenticated, verifyAuthentication | Authentication Validators » |
| Common | some, checkIdentityType, validateResourceAccess, requireParam, isUUID, isNumber, ownsResource | Common Validators » |
| Identity | isSelf | Identity Validators » |
| User | ownsProfile, validateUserProfileAccess | User Validators » |
| Chat | hasSubscription, ownsSubscription, ownsChannel, ownsMessage, validateChannelAccess, validateMessageAccess, hasOrganizationAccessToMessageTemplate, channelExists | Chat Validators » |
| Order | ownsOrder, validateOrderAccess | Order Validators » |
| Organization | hasOrgRole, validateOrganizationAccess | Organization Validators » |
| Category | doesCategoryExist | Category Validators » |
ℹ️ Note: 各validatorの詳細なドキュメント(パラメータ、動作、使用例を含む)については、上記のリンクされた特定のblockドキュメントを参照してください。
🛠️ カスタムバリデーターの作成
バリデーター関数シグネチャ
type Validator = (payload: primitives.RouteHandlerPayload) => Promise<void>;
基本的なバリデーターの例
import { primitives } from '@nodeblocks/backend-sdk';
const validateUserExists = async (payload: primitives.RouteHandlerPayload) => {
const { context, params } = payload;
const userId = params.requestParams?.userId;
const user = await context.db.users.findOne({ id: userId });
if (!user) {
throw new primitives.NodeblocksError(404, 'User not found', 'USER_NOT_FOUND');
}
};
高度なバリデーターの例
import { primitives } from '@nodeblocks/backend-sdk';
const validateUserPermission = async (payload: primitives.RouteHandlerPayload) => {
const { context, params } = payload;
const { userId, organizationId } = params.requestParams;
// ユーザーが存在するかチェック
const user = await context.db.users.findOne({ id: userId });
if (!user) {
throw new primitives.NodeblocksError(404, 'User not found', 'USER_NOT_FOUND');
}
// ユーザーが組織に属しているかチェック
const membership = await context.db.memberships.findOne({
userId,
organizationId,
status: 'active'
});
if (!membership) {
throw new primitives.NodeblocksError(403, 'Access denied', 'ACCESS_DENIED');
}
};
ファクトリ関数パターン
再利用可能なバリデーターファクトリを作成します:
const requireResourceExists = (collectionName: string, paramName: string) => {
return async (payload: primitives.RouteHandlerPayload) => {
const { context, params } = payload;
const resourceId = params.requestParams?.[paramName];
const resource = await context.db[collectionName].findOne({ id: resourceId });
if (!resource) {
throw new primitives.NodeblocksError(
404,
`${collectionName} not found`,
'RESOURCE_NOT_FOUND'
);
}
};
};
// 使用例
const validateProductExists = requireResourceExists('products', 'productId');
const validateOrderExists = requireResourceExists('orders', 'orderId');
🔗 ルートでのバリデーターの使用
単一のバリデーター
export const getCategoryRoute = withRoute({
method: 'GET',
path: '/categories/:categoryId',
validators: [doesCategoryExist],
handler: getCategoryHandler,
});
複数のバリデーター
export const protectedUserRoute = withRoute({
method: 'PATCH',
path: '/users/:userId',
validators: [
isAuthenticated(),
validateResourceAccess(['admin'])
],
handler: updateUserHandler,
});
条件付きバリデーター
const validators = [
// 非公開ルートに対してのみ認証をチェック
...(isPublicRoute ? [] : [isAuthenticated()])
];
export const getUserRoute = withRoute({
method: 'GET',
path: '/users/:userId',
validators,
handler: getUserHandler,
});
代替バリデーションパス
export const flexibleUserRoute = withRoute({
method: 'GET',
path: '/users/:identifier',
validators: [
// ここで標準のバリデーターを使用します。レガシーパラメータバリデーター(
// requireParam/isUUID/isNumberなど)は`some()`と直接使用できません。
some(validateHasUserId, validateHasEmail),
validateUserExists
],
handler: getUserHandler,
});
🚨 エラーハンドリング
NodeblocksErrorの使用
一貫したエラーレスポンスのために、常にNodeblocksErrorを使用します:
import { primitives } from '@nodeblocks/backend-sdk';
const validateResource = async (payload: primitives.RouteHandlerPayload) => {
// バリデーションロジック
if (!isValid) {
throw new primitives.NodeblocksError(
400, // HTTPステータスコード
'Validation failed', // 人間が読めるメッセージ
'VALIDATION_ERROR' // クライアント処理のためのエラーコード
);
}
};
エラーレスポンス形式
バリデーターがエラーをスローすると、クライアントは以下を受け取ります:
{
"error": {
"message": "Category does not exist"
}
}
📋 バリデーターリファレンス
各validatorの詳細なドキュメントについては、Backend Blocksドキュメントを参照してください:
- Authentication Validators
- Common Validators
- Identity Validators
- User Validators
- Chat Validators
- Order Validators
- Organization Validators
- Category Validators
📐 ベストプラクティス
1. 早期失敗
- バリデーターはハンドラーの前に実行されるため、無効なリクエストに対して早期に失敗します
- 必要でない限り、バリデーターで高価な操作を実行しないでください
2. 再利用可能なパターン
- 一般的なバリデーションパターンに対してファクトリ関数を作成します
- ビジネスロジックにはドメイン固有のバリデーターを使用します
3. 明確なエラーメッセージ
- 説明的なエラーメッセージを提供します
- クライアント側の処理のために一貫したエラーコードを使用します
4. データベース効率
- バリデータークエリにデータベースインデックスを使用します
- 頻繁にアクセスされるデータに対してキャッシュを検討します
5. 合成
- バリデーターを論理的にチェーンします(必須 → フォーマット → ビジネスロジック)
- バリデーターを単一の責任に集中させます
6. アクセス制御
- 保護されたリソースに対して適切なアクセス制御バリデーターを使用します
- 認証と認可バリデーターを組み合わせます
- 柔軟なバリデーションシナリオには
some()の使用を検討します
➡️ 次へ
- 複雑なルートを構築するためにRoute Compositionを学習してください
- 一貫したエラーレスポンスのためにError Handlingを探索してください
- APIを整理するためにService Patternsを確認してください