メインコンテンツまでスキップ
バージョン: 🚧 Canary

🚨 Error Handling

Proper error handling is crucial for building robust backend services. The Nodeblocks SDK provides comprehensive error handling patterns that ensure consistent, user-friendly error responses across all services.


🎯 Error Response Format

When nodeBlocksErrorMiddleware() is registered, Nodeblocks service errors use a consistent JSON format:

{
"error": {
"message": "Error message description",
"data": ["Additional error details"]
}
}

Note: Handler and block chains use orThrow to map domain BlockError instances to NodeblocksError. The error middleware serializes failures in an { error: { message, data?, stack? } } envelope; clients do not receive top-level status or code fields from the SDK.

Validation Errors

When request validation fails, additional details are included:

{
"error": {
"message": "Validation Error",
"data": [
"request body must have required property 'name'",
"request body must have required property 'email'",
"request body must NOT have additional properties"
]
}
}

The data array format depends on the schema mode:

  • OpenAPI routes prefix body errors with request body … and parameter errors with path parameter … or query parameter ….
  • Legacy JSON Schema routes use raw AJV messages (for example, must have required property 'name') without the request body prefix.

📋 Common Error Codes

400 Bad Request

  • Validation Error - Invalid request body format or missing required fields
  • Failed to create [entity] - Database insert operation failed to return an inserted ID
  • Failed to update [entity] - Update operation doesn't modify any data (no changes detected)
  • Organization with name … already exists with same owner - Duplicate organization name for the same owner (400, not 409)

401 Unauthorized

  • token could not be verified - Token signature or payload verification failed
  • wrong credentials provided - Invalid login credentials
  • Token fails security check / Token fails security checks - Token fingerprint or security validation failed
  • Invalid token - Authenticated payload is not a valid user access token (common in validators such as checkIdentityType, isSelf, hasOrgRole)

Note: Missing or malformed authorization headers are typically returned as 422 (see below), not 401.

422 Unprocessable Entity

  • Missing authorization header - No Authorization header present
  • Incorrect authorization of token header - Malformed Authorization header value
  • Incorrect format of ${headerName} header - Header does not match the expected format
  • Unable to find refresh token - Refresh token missing from cookie or request
  • Cookie and bearer did not match - Cookie and bearer token identity mismatch
  • Token was not found - Refresh token not found during token refresh
  • unable to register "${email}" - Email already registered (registration conflict; 422, not 409)
  • One or more members have invalid roles - Invalid roles in organization member upsert

403 Forbidden

  • Identity is not authorized to access this resource - Identity lacks required type permissions
  • Identity is not authorized to access this organization - Identity lacks a permitted organization role
  • Identity is not a member of the organization - Identity is not a member of the target organization
  • Identity ID does not match - Authenticated identity does not match the requested resource identity (isSelf)

404 Not Found

  • [Entity] not found - Entity doesn't exist (handler layer; e.g. Profile not found, Channel not found, Product not found)
  • [Entity] does not exist - Entity doesn't exist (validator layer; e.g. Category does not exist, Channel does not exist)
  • Delete operations - When the target record is missing, delete routes return [Entity] not found (e.g. Profile not found on DELETE), not Failed to delete [entity]

409 Conflict

  • Email already in use - Duplicate email during registration or email change
  • Profile is already followed - Follow relationship already exists
  • Organization is already followed - Organization follow relationship already exists
  • Product is already liked - Product like relationship already exists
  • Email already verified or no changes made - Email confirmation when already verified
  • There must be at least one owner remaining in the organization - Organization would have no owners after member change

500 Internal Server Error

  • Failed to create [entity] - Database connection issues or unexpected failures during creation
  • Failed to get [entity] - Database connection issues or unexpected failures during retrieval
  • Failed to find [entities] - Database connection issues, invalid filter syntax, or unexpected failures during listing
  • Failed to update [entity] - Database connection issues or unexpected failures during update
  • Failed to delete [entity] - Database connection issues or unexpected failures during deletion

🔧 Service-Specific Error Patterns

Note: This section highlights common errors for major services. For complete per-endpoint error tables, see each service's documentation page linked from the Service component overview.

Authentication Service Errors

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

Profile Service Errors

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

Organization Service Errors

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

Product Service Errors

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

Category Service Errors

Validator-guarded routes (GET, update, delete, enable, disable) return Category does not exist from the doesCategoryExist validator before the handler runs:

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

Handlers may also emit Category not found if reached without the validator:

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

Attribute Service Errors

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

Order Service Errors

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

Chat Service Errors

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

The channelExists validator returns Channel does not exist on routes that use it:

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

GET message by ID uses the block layer and returns Chat message not found. (note the trailing period); update and delete use handlers and return Chat message not found:

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

Note: The legacy handler getChatMessageById returns Message not found, but the GET /messages/:messageId route uses the block getChatMessageByIdBlock instead. Some block paths also omit the trailing period — match the exact string for the route you are calling.


📐️ Error Handling Best Practices

1. Consistent Error Messages

Use consistent, user-friendly error messages across all services:

// ✅ Good: Clear, actionable error messages
"Profile not found"
"Failed to create organization"
"Validation Error"

// ❌ Avoid: Technical or unclear messages
"Database connection failed"
"Internal server error"
"Something went wrong"

2. Proper HTTP Status Codes

Use appropriate HTTP status codes for different error types:

  • 400 - Bad Request (validation errors, missing fields, no-op updates)
  • 401 - Unauthorized (authentication failures, invalid tokens)
  • 422 - Unprocessable Entity (malformed auth headers, refresh-token state errors, registration conflicts)
  • 403 - Forbidden (authorization failures)
  • 404 - Not Found (entity doesn't exist)
  • 409 - Conflict (duplicate relationships, email already in use)
  • 500 - Internal Server Error (database issues, unexpected failures)

3. Validation Error Details

Include specific validation error details in the data array. OpenAPI-validated routes use prefixed messages:

{
"error": {
"message": "Validation Error",
"data": [
"request body must have required property 'name'",
"request body must have required property 'email'",
"request body must NOT have additional properties"
]
}
}

Legacy JSON Schema routes return raw AJV messages in data without the request body prefix.

4. Authentication Error Handling

Use validators to handle authentication errors automatically:

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

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

// Validators automatically throw NodeblocksError with appropriate status codes
export const protectedRoute = withRoute({
method: 'GET',
path: '/protected',
validators: [isAuthenticated()], // Throws 401 or 422 depending on failure mode
handler: protectedHandler,
});

For custom authentication checks in handlers or validators, call context.authenticate — it throws NodeblocksError on failure (422 for missing/malformed headers, 401 for invalid tokens):

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

const validateAuth: primitives.Validator = async (payload) => {
await payload.context.authenticate?.(payload);
// Throws NodeblocksError (401 or 422) if token is missing or invalid
};

Prefer the built-in isAuthenticated() validator for most routes.

5. Authorization Error Handling

Use validators for authorization checks:

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

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

// Check identity type (e.g., admin only)
export const adminRoute = withRoute({
method: 'GET',
path: '/admin',
validators: [
isAuthenticated(),
checkIdentityType(['admin']), // Throws 403 if not admin
],
handler: adminHandler,
});

// Or allow admin OR self-access to a resource
export const resourceRoute = withRoute({
method: 'GET',
path: '/resource/:id',
validators: [
isAuthenticated(),
some(
checkIdentityType(['admin']),
isSelf(['params', 'requestParams', 'id'])
), // Throws the first validator error if all fail (401, 403, or 500 — not always 403)
],
handler: resourceHandler,
});

For custom authorization, prefer built-in validators like checkIdentityType, isSelf, hasOrgRole, and ownsResource. If you must throw manually, use the SDK message wording:

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

const checkAdminRole: primitives.Validator = async (payload) => {
// Prefer checkIdentityType(['admin']) instead of manual checks.
// Identity type IDs come from configuration (e.g. typeIds.admin = '100'), not literal strings.
throw new primitives.NodeblocksError(
403,
'Identity is not authorized to access this resource',
'checkAdminRole'
);
};

⚙️ Error Middleware Setup

To ensure consistent error handling across your application, use the 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 requires all four collections, even if this example
// only exercises basic profile 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()) // Must be last
.listen(8089, () => console.log('Server running'));

⚠️ Important: Always add nodeBlocksErrorMiddleware() after your routes and services to ensure all errors are properly formatted as JSON responses.


🔍 Error Debugging

Development Mode

When NODE_ENV === 'development', the middleware includes the error stack trace:

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

If the thrown NodeblocksError includes a data property (e.g. validation errors), it is forwarded as-is:

{
"error": {
"message": "Validation Error",
"stack": "NodeblocksError: Validation Error\n at withSchema...",
"data": [
"request body must have required property 'name'"
]
}
}

Production Mode

In production, the middleware returns the error message and any data from the thrown error — it does not add a stack trace. The message is whatever was passed to NodeblocksError:

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