メインコンテンツまでスキップ
バージョン: 0.9.0 (最新)

🪪 バリデーター

Validatorsは、スキーマバリデーションを超えた追加のバリデーションを実行する関数です。これらはルートhandlersの前に実行され、ビジネスロジック、リソースの存在、認証などをチェックできます。


🔍 バリデーターとは何ですか?

Validatorsは、handlersの前にリクエストパイプラインで実行される述語関数です。これらは以下を実行できます:

  • ビジネスロジックのバリデーション(例:「このリソースは存在しますか?」)
  • 認証のチェック(例:「ユーザーは認証されていますか?」)
  • パラメータのバリデーション(例:「これは有効なUUIDですか?」)
  • 制約の強制(例:「ユーザーに権限がありますか?」)
┌─────────────┐
│ Request │
├─────────────┤
│ Schema │ ← JSON Schemaバリデーション
│ Validators │ ← カスタムビジネスロジック
│ Handler │ ← ルートロジック
└─────────────┘

主な特徴:

  • 非同期関数 - データベースクエリを実行できます
  • 失敗時にスロー - 一貫したエラーレスポンスのためにNodeblocksErrorを使用します
  • 合成可能 - 複数のvalidatorsを一緒にチェーンできます
  • コンテキスト対応 - データベース、リクエストパラメータ、コンテキストにアクセスできます

📋 利用可能なバリデーター

ValidatorsはBackend Blocksドキュメントでドメインごとに整理されています。各blockは、その操作のためのドメイン固有のvalidatorsを提供します。

バリデーターカテゴリ

カテゴリValidatorsドキュメント
AuthenticationisAuthenticated, verifyAuthenticationAuthentication Validators »
Commonsome, checkIdentityType, validateResourceAccess, requireParam, isUUID, isNumber, ownsResourceCommon Validators »
IdentityisSelfIdentity Validators »
UserownsProfile, validateUserProfileAccessUser Validators »
ChathasSubscription, ownsSubscription, ownsChannel, ownsMessage, validateChannelAccess, validateMessageAccess, hasOrganizationAccessToMessageTemplate, channelExistsChat Validators »
OrderownsOrder, validateOrderAccessOrder Validators »
OrganizationhasOrgRole, validateOrganizationAccessOrganization Validators »
CategorydoesCategoryExistCategory 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ドキュメントを参照してください:


📐 ベストプラクティス

1. 早期失敗

  • バリデーターはハンドラーの前に実行されるため、無効なリクエストに対して早期に失敗します
  • 必要でない限り、バリデーターで高価な操作を実行しないでください

2. 再利用可能なパターン

  • 一般的なバリデーションパターンに対してファクトリ関数を作成します
  • ビジネスロジックにはドメイン固有のバリデーターを使用します

3. 明確なエラーメッセージ

  • 説明的なエラーメッセージを提供します
  • クライアント側の処理のために一貫したエラーコードを使用します

4. データベース効率

  • バリデータークエリにデータベースインデックスを使用します
  • 頻繁にアクセスされるデータに対してキャッシュを検討します

5. 合成

  • バリデーターを論理的にチェーンします(必須 → フォーマット → ビジネスロジック)
  • バリデーターを単一の責任に集中させます

6. アクセス制御

  • 保護されたリソースに対して適切なアクセス制御バリデーターを使用します
  • 認証と認可バリデーターを組み合わせます
  • 柔軟なバリデーションシナリオにはsome()の使用を検討します

➡️ 次へ

  • 複雑なルートを構築するためにRoute Compositionを学習してください
  • 一貫したエラーレスポンスのためにError Handlingを探索してください
  • APIを整理するためにService Patternsを確認してください