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

🚨 エラー処理

適切なエラー処理は堅牢なバックエンドサービスを構築するために不可欠です。Nodeblocks SDKは一貫してユーザーフレンドリーなエラーレスポンスを保証する包括的なエラー処理パターンを提供します。


🎯 エラーレスポンス形式

nodeBlocksErrorMiddleware() が登録されると、Nodeblocksサービスエラーは一貫したJSON形式を使用します:

{
"error": {
"message": "Error message description",
"data": ["追加のエラー詳細"]
}
}

ノート: ハンドラとブロックチェーンはドメインの BlockError インスタンスを NodeblocksError にマッピングするために orThrow を使用します。エラーミドルウェアは { error: { message, data?, stack? } } エンベロープで失敗をシリアライズします;クライアントはSDKからトップレベルの statuscode フィールドを受け取りません。

バリデーションエラー

リクエストバリデーションが失敗した場合、詳細が含まれます:

{
"error": {
"message": "Validation Error",
"data": [
"リクエスト本文には必須プロパティ 'name' がなければなりません",
"リクエスト本文には必須プロパティ 'email' がなければなりません",
"リクエスト本文は追加プロパを持っていてはなりません"
]
}
}

data アレイの形式はスキーマモードに依存します:

  • OpenAPIルート はボディエラーに リクエスト本文 ... を接頭辞として付け、パラメータエラーに パスパラメータ ... または クエリパラメータ ... と接頭辞を付けます。
  • 従来JSONスキーマルート は接頭辞なしの生のAJVメッセージを使用します(例:必須プロパティ 'name' がなければなりません)。

📋 一般的なエラーコード

400 Bad Request

  • バリデーションエラー - リクエスト本文の形式が無効または必須フィールドが不足
  • [エンティティ]の作成に失敗しました - データベース挿入操作が挿入IDを返さない
  • [エンティティ]の更新に失敗しました - 更新操作が何も変更しない(変更なし)
  • 名前「...」の組織は同じ所有者に対してすでに存在します - 同じ所有者に対する重複組織名(409ではなく400)

401 Unauthorized

  • トークンを検証できませんでした - トークンの署名またはペイロード検証が失敗
  • 認証情報が不正です - 無効なログイン情報
  • トークンがセキュリティチェックに失敗しました / トークンがセキュリティチェックに失敗しました - トークン指紋またはセキュリティ検証が失敗
  • 無効なトークン - 認証ペイロードが有効なユーザーアクセストークンではない(checkIdentityTypeisSelfhasOrgRole などのバリデーターで一般的)

ノート: 不足または不適切な認証ヘッダーは通常、401ではなく422として返されます。

422 Unprocessable Entity

  • 認証ヘッダーが不足しています - Authorization ヘッダーが存在しない
  • トークンヘッダーの認証が不正です - 不適切な Authorization ヘッダー値
  • ${headerName} ヘッダーの形式が不正です - ヘッダーが期待された形式と一致しない
  • リフレッシュトークンが見つかりません - クッキーまたはリクエストからリフレッシュトークンが不足
  • クッキーとベアラーが一致しません - クッキーとベアラートークンのアイデンティティの不一致
  • トークンが見つかりませんでした - トークンリフレッシュ中にリフレッシュトークンが見つからない
  • ${email} の登録に失敗しました - メールがすでに登録されている(登録競合;409ではなく422)
  • 1人以上のメンバーが無効なロールを持っています - 組織メンバーアップサートで無効なロール

403 Forbidden

  • アイデンティティはこのリソースにアクセスする権限がありません - アイデンティティが必要タイプ権限を持っていない
  • アイデンティティは組織にアクセスする権限がありません - アイデンティティが許可された組織ロールを持っていない
  • アイデンティティは組織のメンバーではありません - アイデンティティが対象組織のメンバーでない
  • アイデンティティIDが一致しません - 認証されたアイデンティティが要求されたリソースのアイデンティティと一致しない(isSelf

404 Not Found

  • [エンティティ] 見つかりません - エンティティが存在しない(ハンドラレイヤー;例:Profile not foundChannel not foundProduct not found
  • [エンティティ] は存在しません - エンティティが存在しない(バリデーターレイヤー;例:Category does not existChannel does not exist
  • 削除操作 - 対象レコードが欠落している場合、削除ルートは [エンティティ] 見つかりません を返す(例:DELETE時に Profile not found)、[エンティティ] の削除に失敗しました ではない

409 Conflict

  • メールがすでに使用されています - 登録またはメール変更中の重複メール
  • プロフィールはすでにフォローされています - フォロー関係がすでに存在
  • 組織はすでにフォローされています - 組織フォロー関係がすでに存在
  • 製品はすでによいねされています - 製品いいね関係がすでに存在
  • メールはすでに確認済みまたは変更なし - すでに確認済みの場合のメール確認
  • 組織には少なくとも1人のオーナーが残っていなければなりません - メンバー変更に後、組織にオーナーがいなくなる

500 内部サーバーエラー

  • [エンティティ] の作成に失敗しました - 作成時のデータベース接続問題または予期せぬ失敗
  • [エンティティ] の取得に失敗しました - 取得時のデータベース接続問題または予期せぬ失敗
  • [エンティティ] の検索に失敗しました - リスト時のデータベース接続問題、無効なフィルター構文、または予期せぬ失敗
  • [エンティティ] の更新に失敗しました - 更新時のデータベース接続問題または予期せぬ失敗
  • [エンティティ] の削除に失敗しました - 削除時のデータベース接続問題または予期せぬ失敗

🔧 サービス固有エラーパターン

ノート: このセクションは主要サービスの一般的なエラーを強調しています。完全なエンドポイント별エラーテーブルについては、サービスコンポーネント オーバービューから各サービスのドキュメントページを参照してください。

認証サービスエラー

{
"error": {
"message": "token could not be verified"
}
}
{
"error": {
"message": "wrong credentials provided"
}
}
{
"error": {
"message": "unable to register \"user@example.com\""
}
}

プロフィールサービスエラー

{
"error": {
"message": "Profile not found"
}
}
{
"error": {
"message": "Identity is not authorized to access this resource"
}
}
{
"error": {
"message": "Profile is already followed"
}
}

組織サービスエラー

{
"error": {
"message": "Organization not found"
}
}
{
"error": {
"message": "Failed to create organization"
}
}
{
"error": {
"message": "There must be at least one owner remaining in the organization"
}
}

製品サービスエラー

{
"error": {
"message": "Product not found"
}
}
{
"error": {
"message": "Failed to create product"
}
}
{
"error": {
"message": "Product is already liked"
}
}

カテゴリサービスエラー

バリデーターで保護されたルート(GET、更新、削除、有効化、無効化)は、ハンドラが実行される前に doesCategoryExist バリデーターから Category does not exist を返します:

{
"error": {
"message": "Category does not exist"
}
}

ハンドラもまたバリデーターなしで到達した場合 Category not found をエミットできます:

{
"error": {
"message": "Category not found"
}
}
{
"error": {
"message": "Failed to create category"
}
}

属性サービスエラー

{
"error": {
"message": "Attribute group not found"
}
}
{
"error": {
"message": "Attribute not found"
}
}
{
"error": {
"message": "Failed to create attribute group"
}
}

オーダーサービスエラー

{
"error": {
"message": "Order not found"
}
}
{
"error": {
"message": "Failed to create order"
}
}

チャットサービスエラー

{
"error": {
"message": "Channel not found"
}
}

channelExists バリデーターはそれを使用するルートに Channel does not exist を返します:

{
"error": {
"message": "Channel does not exist"
}
}

IDでのメッセージの取得はブロックレイヤーを使用し、Chat message not found. (末尾のピリオド注意)を返します;更新と削除はハンドラを使用して Chat message not found を返します:

{
"error": {
"message": "Chat message not found."
}
}
{
"error": {
"message": "Chat message not found"
}
}

ノート: 従来ハンドラ getChatMessageByIdMessage not found を返しますが、GET /messages/:messageId ルートは代わりにブロック getChatMessageByIdBlock を使用します。いくつかのブロックパスはまた末尾のピリオドを省略することがあります — 呼び出すルートに正確な文字列に一致してください。


📐️ エラー処理ベストプラクティス

1. 一貫したエラーメッセージ

すべてのサービスで一貫したユーザーフレンドリーなエラーメッセージを使用:

// ✅ 良い: 明確で実行可能なエラーメッセージ
"Profile not found"
"Failed to create organization"
"バリデーションエラー"

// ❌ 避ける: 技術的または不明確なメッセージ
"Database connection failed"
"Internal server error"
"Something went wrong"

2. 適切なHTTPステータスコード

異なるエラータイプに適切なHTTPステータスコードを使用:

  • 400 - Bad Request(バリデーションエラー、不足フィールド、noop更新)
  • 401 - Unauthorized(認証失敗、無効なトークン)
  • 422 - Unprocessable Entity(不適切なauthヘッダー、リフレッシュトークンの状態エラー、登録競合)
  • 403 - Forbidden(認可失敗)
  • 404 - Not Found(エンティティが存在しない)
  • 409 - Conflict(重複関係、メールがすでに使用されている)
  • 500 - Internal Server Error(データベース問題、予期せぬ失敗)

3. バリデーションエラー詳細

data アレイに具体的なバリデーションエラー詳細を含める。OpenAPI検証ルートは接頭辞付きメッセージを使用します:

{
"error": {
"message": "Validation Error",
"data": [
"リクエスト本文には必須プロパティ 'name' がなければなりません",
"リクエスト本文には必須プロパティ 'email' がなければなりません",
"リクエスト本文は追加プロパを持っていてはなりません"
]
}
}

従来JSONスキーマルートは datarequest body 接頭辞なしの生のAJVメッセージを返します。

4. 認証エラー処理

バリデーターを使用して自動的に認証エラーを処理:

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

const { withRoute } = primitives;
const { isAuthenticated } = validators;

// バリデーターは適切なステータスコードでNodeblocksErrorを自動的に投げます
export const protectedRoute = withRoute({
method: 'GET',
path: '/protected',
validators: [isAuthenticated()], // 失敗モードに応じて401または422を投げます
handler: protectedHandler,
});

ハンドラやバリデーターでカスタム認証チェックが必要な場合、context.authenticate を呼び出します — 失敗時に NodeblocksError を投げます(不足/不適切なヘッダーの場合422、無効なトークンの場合401):

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

const validateAuth: primitives.Validator = async (payload) => {
await payload.context.authenticate?.(payload);
// トークンが不足または無効な場合、NodeblocksErrorを投げます
};

ほとんどのルートには組み込みの isAuthenticated() バリデーターを優先してください。

5. 認可エラー処理

認可チェックにバリデーターを使用:

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

const { withRoute } = primitives;
const { isAuthenticated, checkIdentityType, isSelf, some } = validators;

// アイデンティティタイプを確認(例:管理者のみ)
export const adminRoute = withRoute({
method: 'GET',
path: '/admin',
validators: [
isAuthenticated(),
checkIdentityType(['admin']), // adminでなければ403を投げます
],
handler: adminHandler,
});

// または管理者または自身によるリソースへのアクセスを許可
export const resourceRoute = withRoute({
method: 'GET',
path: '/resource/:id',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
isSelf(['params', 'requestParams', 'id'])
), // すべての失敗時に最初のバリデーターエラーを投げます(401、403、または500 — 常に403ではない)
],
handler: resourceHandler,
});

カスタム認可の場合、checkIdentityTypeisSelfhasOrgRole、および ownsResource などの組み込みバリデーターを優先してください。手動で投げる必要がある場合は、SDKのメッセージ文言を使用:

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

const checkAdminRole: primitives.Validator = async (payload) => {
// 手動チェックの代わりに checkIdentityType(['admin']) を優先。
// アイデンティティタイプIDは設定から取得されます(例:typeIds.admin = '100')、リテラル文字列ではありません。
throw new primitives.NodeblocksError(
403,
'アイデンティティはこのリソースにアクセスする権限がありません',
'checkAdminRole'
);
};

⚙️ エラーミドルウェアのセットアップ

アプリケーション全体で一貫したエラー処理を保証するために、nodeBlocksErrorMiddleware を使用:

import express from 'express';
import { middlewares, services, drivers } from '@nodeblocks/backend-sdk';

const { nodeBlocksErrorMiddleware } = middlewares;
const { profileService } = services;
const { withMongo } = drivers;

const connectToDatabase = withMongo('mongodb://localhost:27017/?authSource=admin', 'dev', 'user', 'password');

// ProfileServiceDataStoreには4つのコレクションすべてが必要ですが、この例では
// 基本的なプロフィールCRUDのみを行使します。
express()
.use(
profileService(
{
...(await connectToDatabase('profiles')),
...(await connectToDatabase('identities')),
...(await connectToDatabase('organizations')),
...(await connectToDatabase('products')),
},
{
authSecrets: {
authEncSecret: 'your-encryption-secret',
authSignSecret: 'your-signing-secret',
},
authMode: 'bearer',
identity: {
typeIds: {
admin: '100',
guest: '000',
regular: '001',
},
},
},
),
)
.use(nodeBlocksErrorMiddleware()) // 最後に設定してください
.listen(8089, () => console.log('Server running'));

⚠️ 重要: ルートとサービスの後に nodeBlocksErrorMiddleware() を追加して、すべてのエラーが適切にJSONレスポンスとしてフォーマットされることを確認してください。


🔍 エラーデバッグ

開発モード

NODE_ENV === 'development' の場合、ミドルウェアはエラースタックトレースを含めます:

{
"error": {
"message": "Profile not found",
"stack": "NodeblocksError: Profile not found\n at getProfileById..."
}
}

投げられた NodeblocksErrordata プロパティを含める場合(例:バリデーションエラー)、それはそのまま転送されます:

{
"error": {
"message": "Validation Error",
"stack": "NodeblocksError: バリデーションエラー\n at withSchema...",
"data": [
"リクエスト本文には必須プロパティ 'name' がなければなりません"
]
}
}

本番モード

本番環境では、ミドルウェアは投げられたエラーからエラーメッセージと data を返します — スタックトレースは追加しません。メッセージは NodeblocksError に渡されたものがそのまま使用されます:

{
"error": {
"message": "Profile not found"
}
}