メインコンテンツまでスキップ
バージョン: 0.4.2

🔧 ハンドラー

ハンドラーは、APIエンドポイントのコアビジネスロジックを含む非同期関数です。リクエストデータとコンテキストを含むペイロードを受け取り、レスポンスデータを含む同じペイロードオブジェクトを返します。


📋 ハンドラー種類

Nodeblocksは、ハンドラーチェーンでの役割に基づいて2つの主要なハンドラー種類を提供します:

ハンドラー

ビジネスロジック操作(データベース呼び出し、データ検証、変換など)を実行し、Result<RouteHandlerPayload, NodeblocksError>を返すハンドラー:

import { ok, err, Result } from 'neverthrow';
import { primitives, handlers, utils } from '@nodeblocks/backend-sdk';

const { NodeblocksError, AsyncRouteHandler, RouteHandlerPayload } = primitives;
const { mergeData } = handlers;
const { createBaseEntity } = utils;

// データベース操作用の非同期ハンドラー
export const createUser: AsyncRouteHandler<
Result<RouteHandlerPayload, NodeblocksError>
> = async (payload) => {
const { params, context } = payload;

if (!params.requestBody || Object.keys(params.requestBody).length === 0) {
return err(
new NodeblocksError(400, 'リクエストボディが必要です', 'createUser')
);
}

const baseEntity = createBaseEntity(params.requestBody);
try {
const createdUser = await context.db.users.insertOne(baseEntity);

if (!createdUser.insertedId) {
return err(
new NodeblocksError(400, 'ユーザーの作成に失敗しました', 'createUser')
);
}

return ok(mergeData(payload, { userId: baseEntity.id }));
} catch (_error) {
return err(new NodeblocksError(500, 'ユーザーの作成に失敗しました', 'createUser'));
}
};

// データ変換用の同期ハンドラー
export const formatUserData: RouteHandler<
Result<RouteHandlerPayload, NodeblocksError>
> = (payload) => {
const { requestBody } = payload.params;

const user = {
name: `${requestBody.firstName} ${requestBody.lastName}`
};

return ok(mergeData(payload, { user }));
};

注意: ハンドラーは、データベース呼び出しやAPIリクエストなどの非同期操作を実行するかどうかに応じて、同期(RouteHandler)または非同期(AsyncRouteHandler)のいずれかにできます。

ターミネーターハンドラー

ターミネーターハンドラーは、すべてのハンドラーチェーンの最終ハンドラーです。 前のハンドラーからResultを受け取り、エラーをチェックし、最終的なHTTPレスポンスをフォーマットします:

import { primitives } from '@nodeblocks/backend-sdk';
import { Result } from 'neverthrow';

const { NodeblocksError, RouteHandlerPayload } = primitives;

export const normalizeUserTerminator = (
result: Result<RouteHandlerPayload, NodeblocksError>
) => {
if (result.isErr()) {
throw result.error;
}
const payload = result.value;
const { context } = payload;

if (!context.data?.user) {
throw new NodeblocksError(
500,
'ユーザーの正規化で不明なエラー',
'normalizeUserTerminator'
);
}
const { _id, ...user } = context.data.user;
return user;
};

重要: ターミネーターハンドラーはResultを受け取り、エラーの場合は例外をスローし、成功の場合は最終レスポンスデータを返します。


🔗 ハンドラーコンポジション

ハンドラーはcompose関数を使用してチェーンされ、複雑な操作を構築できます:

import { compose } from '@nodeblocks/backend-sdk';

const createUserPipeline = compose(
validateUserInput,
createUser,
sendWelcomeEmail,
normalizeUserTerminator
);

このパターンにより、各ハンドラーが単一の責任を持ち、再利用可能で簡単にテストできるようになります。


🎯 ベストプラクティス

1. 単一責任

各ハンドラーは1つの明確な目的を持つべきです:

// 良い - 単一責任
const validateUserEmail = (payload) => { /* メール検証のみ */ };
const createUserAccount = (payload) => { /* アカウント作成のみ */ };

// 悪い - 複数責任
const validateAndCreateUser = (payload) => { /* 検証と作成の両方 */ };

2. エラーハンドリング

常に適切なエラー情報を含めてください:

return err(
new NodeblocksError(
400, // HTTPステータスコード
'詳細なエラーメッセージ', // ユーザー向けメッセージ
'handlerFunctionName' // デバッグ用の関数名
)
);

3. 型安全性

TypeScriptの型を使用してハンドラーを定義:

export const myHandler: AsyncRouteHandler<
Result<RouteHandlerPayload, NodeblocksError>
> = async (payload) => {
// 実装
};

4. テスタビリティ

ハンドラーを純粋関数として設計し、すべての依存関係をコンテキストを通じて注入:

export const createUser = async (payload: RouteHandlerPayload) => {
const { context } = payload;
// context.db、context.configurationなどを使用
};

📚 利用可能なハンドラーユーティリティ

データマージ

import { mergeData } from '@nodeblocks/backend-sdk';

const result = mergeData(payload, { newData: 'value' });

エンティティ作成

import { createBaseEntity } from '@nodeblocks/backend-sdk';

const entity = createBaseEntity(requestData);
// 自動的にid、createdAt、updatedAtを追加

結果処理

import { ok, err } from 'neverthrow';

// 成功の場合
return ok(updatedPayload);

// エラーの場合
return err(new NodeblocksError(400, 'エラーメッセージ', 'handlerName'));

➡️ 次へ

ルートコンポーネントについて学習して、ハンドラーがHTTPエンドポイントとどのように統合されるかを理解しましょう!