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

🚦 ルート

ルートは、HTTPメソッド、パス、バリデータ、ビジネスロジックをまとめてバンドルします。NodeBlocksは、withRouteヘルパーを提供し、メタデータを保存し、サービスルーターが生成されるときにExpressに自動的にプラグインします。


1️⃣ アナトミー

src/routes/user.ts
import {compose, flatMapAsync, withRoute, lift} from '../primitives';
import {createUser, getUserById, normalizeUserTerminator} from '../handlers';

export const createUserRoute = withRoute({
method: 'POST',
path: '/users',
validators: [verifyAuthentication(getBearerTokenInfo)], // optional array of validator functions
handler: compose(
createUser, // Handler: write to DB
flatMapAsync(getUserById), // Handler: read back the created entity
lift(normalizeUserTerminator) // Terminator handler: format final HTTP response
),
});

以下はwithRouteによって返される最小限の形状です。

interface Route {
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
path: string; // Expressスタイルのパス、:paramsをサポート
validators?: Validator[]; // ハンドラーチェーン**の前**に実行されるオプションのリスト
handler: RouteHandler; // 合成可能な関数チェーン
}

Method

REST規則に従うHTTP動詞:

動詞目的
GETリソースの読み取り/取得
POST新しいリソースの作成
PATCHリソースの部分更新
DELETEリソースの削除

Path

Expressスタイルのテンプレート。コレクションには複数名詞を使用(/users)し、アクションにはサブパスを使用(/users/:id/lock)。動的部分はrequestParamsになります。

Validators

データベース作業のに実行される同期または非同期フック。同じpayloadオブジェクトを受け取ります。 リクエストを拒否する慣用的な方法は、throw new NodeblocksError(status, message)を使用することです:

import {NodeblocksError} from '../primitives/error';

export const validateEmail: Validator = ({ params }) => {
if (!params.requestBody.email?.includes('@')) {
throw new NodeblocksError(400, 'Invalid email');
}
};

Handler

ルートはビジネスロジックをハンドラーチェーンに委譲します—リクエストを処理し、レスポンスをフォーマットする合成関数。すべてのハンドラーチェーンはこのパターンに従います:

  1. Handlers - ビジネスロジックを実行(データベース操作、検証、変換)
  2. Terminator Handler - HTTPレスポンスをフォーマットする最終ハンドラー
handler: compose(
handler1, // Returns Result<RouteHandlerPayload, Error>
flatMapAsync(handler2), // Returns Result<RouteHandlerPayload, Error>
lift(terminatorHandler) // Handles Result, returns formatted response
)

ハンドラーには、署名、mergeData、Resultパターン、ターミネータ要件をカバーした専用の説明があります。➜ Handler explanation


2️⃣ 良いプラクティス

  1. Path semantics – リソースには名詞(/users)、アクションには動詞(/users/:id/lock)を使用。
  2. Complete handler chains – 常に最終レスポンスをフォーマットするターミネータハンドラーで終了。
  3. Keep handlers small – 単一のモノリスよりも複数の合成可能関数を優先。
  4. Validators first – DB呼び出し前に軽量チェックを実行。
  5. Stateless – ルートはグローバル状態を変更しない。すべてpayload経由で到着。

Handler Chain Pattern

handler: compose(
handler1, // Business logic: returns Result
flatMapAsync(handler2), // More business logic: returns Result
lift(terminatorHandler) // Response formatting: handles Result, returns data
)

3️⃣ バリデータの追加

import {withRoute, compose} from '../primitives';
import {NodeblocksError} from '../primitives/error';
import {ok, err} from 'neverthrow';
import {mergeData} from '../handlers';

// 常にアクセスをブロックするカスタムバリデータ
const youShallNotPass = () => {
throw new NodeblocksError(400, 'YOU SHALL NOT PASS!');
};

// 秘密データの取得ハンドラー
const getSecretData = async (payload) => {
return ok(mergeData(payload, { hiddenSecret: '🧱🧱🎉🧱🧱' }));
};

// レスポンスフォーマットのターミネータハンドラー
const secretTerminator = (result) => {
if (result.isErr()) {
throw result.error;
}
const payload = result.value;
const { context } = payload;

return { secret: context.data.secret };
};

export const getSecretRoute = withRoute({
method: 'GET',
path: '/secret',
validators: [youShallNotPass],
handler: compose(
getSecretData, // Handler: データ取得
lift(secretTerminator) // Terminator: 最終レスポンスフォーマット
),
});

バリデータは失敗時にNodeblocksErrorをスローし、エラーミドルウェアにバブルアップします。


➡️ 次へ

ルートとスキーマを再利用可能なユニットに構成する方法についてはFeature »を学習してください