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

🧩 ブロック

ブロックは、特定の操作のアプリケーションロジックの核心を含むパラメータ駆動のビジネスロジック関数です。再利用可能でテスト可能かつ構成可能なビルディングブロックとして設計され、ビジネスロジックをペイロード処理やルーティングの関心事から分離します。

ブロック登録APIはありません — ブロックはSDK blocks モジュールのエクスポートされた関数であり、primitives.applyPayloadArgs 経由でルートに接続されます。


🔍 ブロックとは何か

ブロック は必要なデータのみを受信し、完全な RouteHandlerPayload を受信するわけではありません。ほとんどのドメインブロックは明示的なエラー処理のために Result を返します; ヘルパー関数やターミネーター関数はプレーンな値またはレスポンス形状を返す可能性があります。ルートは applyPayloadArgs によってペイロードとブロックの間を橋渡しし、ペイロードから値を抽出し、ブロックの結果を context.data に格納します。

┌─────────────┐
│ Route │ compose(applyPayloadArgs(...), flatMapAsync(...), lift(orThrow(...)))
├─────────────┤
│ Block ▷ │ パラメータ駆動のビジネスロジック
│ │ getIdentityById(db, identityId)
└─────────────┘

設計原則:

  • 関心の分離 — ブロックはビジネスロジックを含み、ルートは applyPayloadArgs を使用してペイロードとブロックの間を橋渡しする。
  • パラメータ駆動 — ブロックは抽出された引数Via必要なもののみを受け取り、完全なペイロードではない(ユニットテストが容易)。
  • エラー処理 — ブロックは Result 型を返す; orThrow はドメインエラーをチェーンの末尾でHTTPレスポンスにマッピングする。
  • 構成可能性 — ブロックは flatMapAsync と共にルートハンドラーパイプラインで連結される。
  • 規約ベースdefBlock、レジストリ、またはメタデータスキーマはない; src/blocks/ から関数をエクスポートし、ルートで接続する。

ブロックのライフサイクル

blocks/*.ts → routes/*.ts → features/*.ts → defService()
(定義) (接続) (スキーマ + ルート) (提供)
  1. 定義src/blocks/<domain>.ts で関数をエクスポート
  2. 接続src/routes/<domain>.tsapplyPayloadArgs を使用してルートハンドラーに構成
  3. 検証 — ルートとスキーマを src/features/<domain>.ts でペアリング
  4. 提供defService(feature) でフィーチャーをマウント

🧑‍💻 ブロックの定義

ブロックはプレーンなエクスポートされた関数である — 登録ステップなし:

import { Collection } from 'mongodb';
import { err, ok } from 'neverthrow';
import { primitives } from '@nodeblocks/backend-sdk';

const { BlockError } = primitives;

class ThingBlockError extends BlockError {}
class ThingNotFoundBlockError extends ThingBlockError {}

// src/blocks/my-domain.ts — アプリケーションコード、登録不要
export async function getCustomThingById(db: Collection, id: string) {
try {
const result = await db.findOne({ id: String(id) });
if (!result) {
return err(new ThingNotFoundBlockError('Thing not found'));
}
return ok(result);
} catch {
return err(new ThingBlockError('Failed to get thing'));
}
}

この例はアプリケーションブロックを定義しています; SDKエクスポートではありません。SDKブロックは単一の blocks名前空間からインポートします。例: const { getIdentityById } = blocks


🧑‍💻 ルートでのブロックの使用

ブロックは applyPayloadArgs によってルートに構成されます。ブロック関数、ペイロードから引数を抽出するためのパスタプル、および結果を context.data に格納するオプションのキーを取ります:

import { blocks, primitives, validators } from '@nodeblocks/backend-sdk';
const { getIdentityById, normalizeIdentity } = blocks;
const { applyPayloadArgs, compose, flatMapAsync, lift, orThrow, withRoute } =
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']]))
),
});

applyPayloadArgs(fn, paths, key?) は AsyncRouteHandler を返します -- ペイロードを第二引数として取りません。

applyPayloadArgs の動作

動作詳細
引数の抽出各パスタプル(例: ['context', 'db', 'identities'])は、Ramda の path を通じてペイロードから解決されます
任意の key指定すると、ブロック結果は context.data[key] に格納されます。省略すると、戻り値全体が context.data にマージされます
同期と非同期同期ブロックと非同期ブロックの両方に対応します。Promise ではない戻り値も自動的に処理されます
Result の伝播ブロックが Result.err() を返すと、チェーンは短絡します。Result.ok() の値はペイロードにマージされます

キーあり(最も一般的):

applyPayloadArgs(getIdentityById, [
['context', 'db', 'identities'],
['params', 'requestParams', 'identityId'],
], 'rawIdentity')
// → context.data.rawIdentity = identity オブジェクト

キーなし(戻り値を context.data に格納):

applyPayloadArgs(buildUpdatePayload, [
['params', 'requestBody'],
])
// → 戻り値が context.data に格納される

同期ブロック(例: normalizeIdentity):

applyPayloadArgs(normalizeIdentity, [['context', 'data', 'rawIdentity']], 'identity')
// normalizeIdentity は同期関数ですが、applyPayloadArgs は同じように処理します

⚠️ エラー処理とターミネーター

ドメインブロックは通常 Result<T, Error> を返します。ヘルパー関数とターミネーター関数は、代わりにプレーンな値またはレスポンス形状を返す場合があります。エラーの型はドメインによって異なります。

  • ほとんどのドメイン — ドメイン固有の *BlockError extends BlockError(例: ProfileBlockErrorAuthenticationBlockError
  • Identity ブロック — HTTP ステータスコードを組み込んだ NodeblocksErrorBlockError パターンの例外)

ブロックチェーンは lift(orThrow(errorMap, successMap)) で終了します。

  • errorMapBlockError のサブクラスを HTTP ステータスコードに対応付けます
  • successMap — ペイロードパスからレスポンスデータを抽出します(例: [['context', 'data', 'identity']]
lift(orThrow(
[[ProfileNotFoundBlockError, 404], [ProfileBlockError, 500]],
[['context', 'data', 'profile']]
))

errorMap が空([])の場合、一致しないエラーはそのまま再スローされます。これは、ブロックがすでにステータスコード付きの NodeblocksError を返す場合(Identity ルート)に使用されます。

完全なターミネーターパターンについては、ルート » を参照してください。


🔧 共通パターン

withLogging

本番環境のルートでは、可観測性のためにブロックステップをラップすることがよくあります。

flatMapAsync(withLogging(applyPayloadArgs(getIdentityById, paths, 'rawIdentity')))

ブロックターミネーター

blocks モジュールの一部の関数は、ハンドラー形式のターミネーターです。Result<RouteHandlerPayload, Error> を受け取り、HTTP レスポンスを組み立てます。たとえば deleteIdentityTerminator{ statusCode: 204 } を返します。

compose(
applyPayloadArgs(deleteIdentity, paths, 'deleteIdentity'),
lift(deleteIdentityTerminator)
)

新しいルートでは lift(orThrow(...)) を優先してください。ブロックターミネーターは、レガシーなレスポンス形状または特殊なレスポンス形状のために存在します。

mapMatchingErrorToFalse

認証ルートでは、特定のブロックエラーをチェーンの失敗ではなく ok(false) に変換するために使用します。

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

const { mapMatchingErrorToFalse } = primitives;
const { checkEmailIsUniqueInIdentities, AuthenticationConflictError } = blocks;

const safeCheck = mapMatchingErrorToFalse(checkEmailIsUniqueInIdentities, [
AuthenticationConflictError,
]);
// AuthenticationConflictError → err(...) ではなく ok(false)

⚡ パラメータ駆動ブロックの例外

ほとんどのブロックはドメインデータのみ(db、ID、DTO)を受け取ります。一部のモジュールは applyPayloadArgs で抽出した Express の型も受け取ります。

モジュール追加パラメーター
OAuthblocks/oauth/*RequestResponse
Authenticationblocks/authentication.tsRequest(ヘッダー、フィンガープリント)
Utilsblocks/utils.tsResponseredirectTo

これらのブロックも同じ方法で接続します。applyPayloadArgs がペイロードから context.request または context.response を抽出します。


🔄 ブロックとハンドラーの比較

観点ブロックハンドラー
入力抽出されたパラメーターのみ完全な RouteHandlerPayload
接続方法applyPayloadArgs(block, paths, key)compose() に直接渡す
テスト容易性プレーンな引数を渡すペイロードフィクスチャが必要

パラメータ駆動のロジックにはブロックを使用し、複数のペイロードフィールドへ直接アクセスする必要がある関数にはハンドラーを使用します。完全な例と WebSocket ハンドラーについては、ハンドラー » を参照してください。


📦 SDK ブロックモジュール

ブロックは、src/blocks/index.ts に対応する SDK の blocks 名前空間でドメイン別に整理されています。

モジュール説明リファレンス
Address住所検索と正規化SDK のみ — ドキュメント準備中
Authentication認証とトークンのロジック認証ブロック »
Avatarアバターの正規化とファイル管理アバターブロック »
Chatメッセージングとリアルタイム通信チャットブロック »
Common共通ブロックユーティリティ共通ブロック »
File Storage安全なファイル操作と署名付き URLファイルストレージブロック »
IdentityID ライフサイクルとセキュリティ管理ID ブロック »
Location階層型ロケーション管理ロケーションブロック »
MongoMongoDB データベース操作とユーティリティMongo ブロック »
Notification通知の作成と配信SDK のみ — ドキュメント準備中
OAuthサードパーティ OAuth プロバイダーとの統合OAuth ブロック »
Order注文のクエリとフィルタリング注文ブロック »
Organization組織とワークスペースのロジック組織ブロック »
Product商品管理操作商品ブロック »
Profileプロフィールの関係性とソーシャルエンゲージメントプロフィールブロック »
Utilsリダイレクトヘルパーとパスワード生成SDK のみ — ドキュメント準備中

➡️ 次のステップ

ルート » でブロックをハンドラーチェーンに合成する方法を学ぶか、表にあるドメインブロックのリファレンスを参照してください。