メインコンテンツまでスキップ
バージョン: 0.13.0 (Previous)

🛣️ ルート

ルートは、プロトコル、HTTP メソッド、パス、バリデーター、ハンドラーチェーンをまとめたものです。withRoute ヘルパーは RouteComposer、すなわち ServiceDefinition にルートメタデータを追加する関数を返します。ルートを Express に直接接続することはありません。機能へ合成し、defService でマウントします。

ルートは HTTP(デフォルト)と WebSocket(protocol: 'ws')の両方をサポートします。


🔍 ルートとは?

withRoute(service: ServiceDefinition) => ServiceDefinition を返します。service.routes にルートを追加(または置換)します。先行する withSchemaservice.withNextRoute を設定している場合、ルート登録時にそのルートハンドラーへバリデーションが注入されます。

Express の接続は defService(adapter) 実行時に行われます。これは Express ルーターを構築して HTTP ルートを登録し、渡された WebSocketServer に WebSocket ハンドラーを接続します。

ルートのライフサイクル

routes/*.ts → features/*.ts → services/*.ts → defService()
(withRoute) (スキーマとルートを合成) (機能を合成) (express.Router)
  1. 定義src/routes/<domain>.ts から withRoute({ method, path, validators, handler }) をエクスポートします。
  2. スキーマと組み合わせるsrc/features/<domain>.tscompose(schema, route) を使用します(スキーマは withNextRoute を設定します)。
  3. まとめる — サービスファクトリーで機能を結合します。
  4. 提供するdefService(partial(compose(...features), [deps])) が Express ルーターを生成します。

withRoute は、同じ method + path + protocol を持つ既存のルートを置換します。スキーマとの組み合わせについては機能 »およびスキーマ »を参照してください。


📐 RouteConfig

withRoutePartial<RouteConfig> を受け取るため、先行するコンポーザーとマージできます。省略時は protocol: 'http' を補い、WebSocket ルートでは method: 'GET' を補います。HTTP ルートでは methodpathhandler を指定する必要があります。withRoute が HTTP メソッドのデフォルトを作ることはありません。保存されるルートには、以下の必須フィールドがあります。

interface RouteConfig {
protocol: 'http' | 'ws';
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
path: string;
validators?: Validator[] | WsValidator[];
schema?: SchemaDefinition; // レガシー — 通常は withSchema の withNextRoute により適用
openapi?: OpenAPIOperation; // 通常は withSchema の withNextRoute により適用
handler:
| AsyncRouteHandler
| RouteHandler
| AsyncWsRouteHandler
| WsRouteHandler;
}

プロトコル

プロトコル目的最終ハンドラー出力
httpREST API エンドポイントターミネーター後のプレーンな JSON データ、または { statusCode, data }
wsリアルタイム WebSocket 接続RxJS Subject(ブロックチェーンまたは直接ハンドラー経由)

Result ベースのチェーンでは、中間ステップは Result 値を返します。ターミネーター(orThrow またはカスタムの *Terminator)が、defService がクライアントへ送信する値を生成します。

HTTP レスポンスの処理

defService は最終ハンドラー結果を次のように処理します。

  • 結果に statusCode プロパティがある場合 — res.status(statusCode).json(data)(削除時の { statusCode: 204 } など)
  • それ以外 — プレーンなレスポンスデータとして res.json(result)

defService はルーターに express.json() も追加し、すべての service.middleware エントリーを適用します。

メソッド

動詞目的
GETリソースを読み取り・取得する
POST新しいリソースを作成する
PUT置換または完全更新する
PATCH部分的に更新する
DELETE削除する

パス

Express 形式のテンプレートです。コレクションには複数形の名詞/identities)、操作にはサブパス(/identities/:identityId/lock)を使用します。HTTP ルートでは動的部分が params.requestParams になります。


✅ バリデーター

すべてのハンドラー処理のに実行される非同期フックです。HTTP では同じペイロードオブジェクト(RouteHandlerPayload)、WebSocket では WsRouteHandlerPayload を受け取ります。

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

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

リクエストを拒否する慣用的な方法は、throw new NodeblocksError(status, message) です。HTTP バリデーターは Promise<void> を返す必要があり、WebSocket バリデーターは void または Promise<void> を返せます。


🔗 ハンドラーチェーン

ルートは、合成されたハンドラーチェーンへビジネスロジックを委譲します。

  1. ブロックまたはハンドラー — ビジネスロジックを実行します(中間ステップは Result を返します)。
  2. ターミネーター — ブロックルートには lift(orThrow(...))、レガシーハンドラールートには lift(*Terminator) を使用します。

ブロックベースのルート(推奨)

新しいルート(identity、authentication、organization、product)には、applyPayloadArgs を使ったパラメーター駆動ブロックを推奨します。

import { blocks, primitives, validators } from '@nodeblocks/backend-sdk';

const { getIdentityById, normalizeIdentity } = blocks;
const { compose, flatMapAsync, withRoute, lift, applyPayloadArgs, orThrow } =
primitives;
const { isAuthenticated, checkIdentityType } = validators;

export const getIdentityRoute = withRoute({
method: 'GET',
path: '/identities/:identityId',
validators: [isAuthenticated(), checkIdentityType(['admin'])],
handler: compose(
applyPayloadArgs(
getIdentityById,
[
['context', 'db', 'identities'],
['params', 'requestParams', 'identityId'],
],
'rawIdentity'
),
flatMapAsync(
applyPayloadArgs(
normalizeIdentity,
[['context', 'data', 'rawIdentity']],
'identity'
)
),
lift(orThrow([], [['context', 'data', 'identity']]))
),
});

型付けされたブロックエラーがあるルートでは、orThrow でマッピングします。

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

const { OrganizationBlockError } = blocks;

lift(
orThrow(
[[OrganizationBlockError, 500]],
[['context', 'data', 'organization']]
)
);

ブロックの完全なパターンはブロック »を参照してください。

ハンドラーベースのルート(レガシー)

category、order、attributes、invitation、および chat の一部は、カスタムターミネーターとともに handlers.* を直接合成します。

import { handlers, primitives, validators } from '@nodeblocks/backend-sdk';

const { createCategory, getCategoryById, normalizeCategoryTerminator } = handlers;
const { compose, flatMapAsync, withRoute, lift, withLogging } = primitives;
const { isAuthenticated, checkIdentityType } = validators;

export const createCategoryRoute = withRoute({
method: 'POST',
path: '/categories',
validators: [isAuthenticated(), checkIdentityType(['admin'])],
handler: compose(
withLogging(createCategory),
flatMapAsync(withLogging(getCategoryById)),
lift(withLogging(normalizeCategoryTerminator))
),
});

レガシーハンドラーのパターンと移行に関する注意はハンドラー »を参照してください。

合成パターン

ブロックチェーン:

handler: compose(
applyPayloadArgs(block1, [/* paths */], 'result1'),
flatMapAsync(applyPayloadArgs(block2, [/* paths */], 'result2')),
lift(orThrow([[BlockError1, 404], [BlockError2, 500]], [['context', 'data', 'result2']]))
)

ハンドラーチェーン:

handler: compose(
handler1,
flatMapAsync(handler2),
lift(terminatorHandler)
)

ルートラッパー

ハンドラーステップの周囲に適用する一般的なコンビネーターです。

  • withLogging — 秘匿化を伴う構造化ログ
  • withPaginationfind クエリの自動ページネーション
  • withPaginatedProperty — ネストした配列プロパティのページネーション
  • withSoftDelete — 読み書き時の透過的な論理削除フィルタリング

🔌 WebSocket ルート

WebSocket ルートには protocol: 'ws' を使用します。WebSocketServerdefService の第 2 引数として渡します。

defService(chatFeature, wss);

defService は接続時に params.requestQuery(WebSocket URL から解析)を注入します。

SDK パターン:ブロック合成チェーン

SDK の本番 WebSocket ルート(streamChatMessagesRoute)は、context.data から RxJS Subject を取り出して終わるブロック合成チェーンを使います。

import { blocks, primitives } from '@nodeblocks/backend-sdk';

const { streamChatMessages, normalizeChatMessageStream, ChatMessageBadRequestError, ChatMessageUnknownError } = blocks;
const { compose, applyPayloadArgs, flatMapAsync, lift, orThrow, withLogging, withRoute } = primitives;

export const streamChatMessagesRoute = withRoute({
protocol: 'ws',
path: '/messages/listen',
handler: compose(
withLogging(
applyPayloadArgs(
streamChatMessages,
[
['context', 'db', 'chatMessages'],
['params', 'requestQuery', 'channelId'],
],
'streamSubject'
)
),
flatMapAsync(
withLogging(
applyPayloadArgs(
normalizeChatMessageStream,
[
['context', 'fileStorageDriver'],
['context', 'data', 'streamSubject'],
],
'normalizedStreamSubject'
)
)
),
lift(
withLogging(
orThrow(
[
[ChatMessageBadRequestError, 400],
[ChatMessageUnknownError, 500],
],
[['context', 'data', 'normalizedStreamSubject']]
)
)
)
),
});

カスタム WebSocket ハンドラー

SDK は、RxJS Subject を直接返すハンドラー向けに WsRouteHandler および AsyncWsRouteHandler 型も定義しています。ブロックチェーンパターン以外のカスタム実装にはこれらを使用してください。

完全な WebSocket パターンはWebSocket サービスの作成 »を参照してください。

WebSocket と HTTP の比較

観点HTTP ルートWebSocket ルート
プロトコル'http'(デフォルト)'ws'
メソッド必須(GETPOST など)内部的には GET がデフォルト
ハンドラー出力プレーンなデータまたは { statusCode, data }RxJS Subject
通信リクエスト・レスポンス双方向ストリーミング
マウントdefService(feature)defService(feature, wss)

📐 推奨事項

  1. パスの意味論 — リソースには名詞(/identities)、操作にはサブパス(/identities/:id/lock)を使用します。
  2. 完全な Result チェーンResult ベースのチェーンはターミネーター(orThrow または *Terminator)で終えます。
  3. ブロックを優先する — 新しいルートにはパラメーター駆動ブロックを使い、ハンドラーはレガシードメインだけに使用します。
  4. ロジックを小さく保つ — 単一の巨大な処理より、複数のコンポーザブルを優先します。
  5. バリデーターを先に置く — DB 呼び出しの前に軽量な検査を行います。
  6. ステートレスにする — ルートはグローバル状態を変更せず、すべてを payload 経由で受け取るようにします。

📦 SDK ルートモジュール

ルートは、src/routes/index.ts に対応する SDK の routes 名前空間でドメイン別に整理されています。

モジュール説明参照
Address住所検索エンドポイントSDK のみ — ドキュメントは未作成
Attributes属性管理ルートAttribute ルート »
Authenticationログイン、ログアウト、MFA、トークンAuthentication ルート »
Categoryカテゴリー CRUD ルートCategory ルート »
Chatチャネル、メッセージ、購読(WS を含む)Chat ルート »
IdentityIdentity ライフサイクルルートIdentity ルート »
Invitation招待管理ルートInvitation ルート »
LocationLocation 管理ルートLocation ルート »
Notification通知エンドポイントSDK のみ — ドキュメントは未作成
OAuthGoogle、Twitter、LINE OAuth ルートOAuth ルート »
Order注文管理ルートOrder ルート »
Organization組織ルートOrganization ルート »
Product商品管理ルートProduct ルート »
ProfileプロフィールとソーシャルルートProfile ルート »

➡️ 次へ

ルートをスキーマと合成する方法は機能 »、バリデーションはスキーマ »、ハンドラーチェーンのパターンはブロック »およびハンドラー »を参照してください。