🚦 ルート
ルートは、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
ルートはビジネスロジックをハンドラーチェーンに委譲します—リクエストを処理し、レスポンスをフォーマットする合成関数。すべてのハンドラーチェーンはこのパターンに従います:
- Handlers - ビジネスロジックを実行(データベース操作、検証、変換)
- 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️⃣ 良いプラクティス
- Path semantics – リソースには名詞(
/users
)、アクションには動詞(/users/:id/lock
)を使用。 - Complete handler chains – 常に最終レスポンスをフォーマットするターミネータハンドラーで終了。
- Keep handlers small – 単一のモノリスよりも複数の合成可能関数を優先。
- Validators first – DB呼び出し前に軽量チェックを実行。
- 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 »を学習してください