Generated policies
Client Error Response (api)
Use clientError for failures caused by invalid client input or client-side request conditions.
Correct examples
if (!tenant) clientError('tenantNotFound')
Incorrect examples
return res.status(400).json({ message: 'Invalid tenant' })
Fix
Replace direct 4xx responses for client failures with clientError and a translation key.
Explicit Input Errors (api)
Follow the Ensure Validation policy first. Use the shared validation API whenever possible and propose a reasonable extension before introducing manual validation. This policy defines the fallback only when that API and a reasonable extension cannot meet the requirement. Keep fallback validation in shared infrastructure and run it before making changes. Never raise or return the generic invalidRequest error. Reuse existing localized messages and ICU templates where their meaning matches the failure; otherwise use a specific reusable key describing the domain condition rather than a separate key for every field. Identify affected inputs through existing field-label keys and structured problems, without exposing raw sensitive values or provider responses. Raise the error through clientError; do not silently return success or check suppression flags inside business functions or validation helpers. A larger workflow may select specific input-error codes in its async context and invoke independent steps through runStep. clientError records eligible errors in the same context’s errors array and always throws; the shared step boundary controls continuation. Authorization, infrastructure and operation-consistency failures must propagate. Suppression does not roll back earlier writes, so validate first and keep dependent work within the same step.
Philosophy
Shared validation remains the first choice. Exceptional domain checks still need explicit diagnostics and consistent control flow. Keeping fallback errors in shared infrastructure lets the broader workflow decide whether independent work may continue, while the failing function always stops.
Correct examples
A reviewed fallback for domain validation that cannot reasonably use the shared API or an extension. The validation report comes from shared validation infrastructure.
if (validationReport.hasRelationCycle) {
clientError('relationCycleDetected', {
problems: validationReport.fields,
})
}
Incorrect examples
if (validationReport.hasRelationCycle) clientError('invalidRequest')
if (validationReport.hasRelationCycle) {
if (params.suppressErrors) return
clientError('relationCycleDetected')
}
Fix
Apply the Ensure Validation policy first. For a reviewed fallback, centralize validation and use clientError with a precise reusable localized message and structured field details; keep suppression in shared context infrastructure.
Http Status Ownership (api)
Do not set HTTP status codes directly outside the centralized API response layer.
Correct examples
return ok({ data })
clientError('invalidRequest')
Incorrect examples
return res.status(200).json(data)
return response.status(400).json(error)
Fix
Return the API result or use the appropriate centralized error helper instead of setting the status directly.
Import Order (api)
In API code, order imports from native modules to external dependencies, core modules, and local modules.
Correct examples
import fs from 'node:fs'
import express from 'express'
import core from '../core/module.js'
import local from './local.js'
Incorrect examples
import local from './local.js'
import express from 'express'
Fix
Move imports into the required layer order.
Localized Response Message (api)
Never return hardcoded human-readable messages from API responses; use localized translation keys.
Correct examples
return ok({ message: 'tenantNotFound' })
return res.send('emptyResponse')
Incorrect examples
return ok({ message: 'Tenant was not found.' })
return res.send('The tenant was not found.')
Fix
Replace hardcoded response text with a localization key.
Post Suffix Boundary (api)
Use the OnPost suffix only for HTTP POST actions in part endpoint modules under api/api, runnable API endpoint directories, and API core transport modules under core/api. Business operations, data operations, helpers, their filenames, and their imports must use protocol-neutral names. Map neutral operations to OnPost action names at the endpoint boundary so POST routes remain unchanged.
Philosophy
HTTP method selection belongs to the transport boundary; reusable domain and data operations must not depend on a protocol naming convention.
Correct examples
export const create = async params => saveItem(params)
export default { createOnPost: create }
Incorrect examples
export const createOnPost = async params => saveItem(params)
import { createOnPost } from 'core'
Fix
Rename the operation and its callers without OnPost, then expose the existing OnPost action name only in the endpoint module. Keep shared endpoint action maps in the API core transport layer.
Route File Placement (api)
In API routing, name part-action files after the part and type-action files after the type. For example, part actions of the blog, go into the blog.js.
Route Shape (api)
Part-action paths contain two segments, ${part}/${action}, while type-action paths contain three segments, ${part}/${type}/${action}. For example, /globalization/data is a part action, whereas /globalization/translation/findAll is a type action.
Server Error Response (api)
Use serverError for failures caused by server-side conditions.
Correct examples
serverError('storageUnavailable')
Incorrect examples
throw new Error('Storage is unavailable')
Fix
Replace direct Error throws for server failures with serverError and a translation key.
Cache Key Hierarchy (cache)
Use deploymentInstance-process-tenant-locale-part-type-item as the cache-key structure. Segments may be omitted progressively from left to right. This structure defines the cache hierarchy and scope, allowing data to be cached for an entire process, a specific tenant, or a tenant's locale. It must support invalidating one tenant's cache without affecting other tenants, as well as invalidating a single item, such as one product, without clearing the entire website's cache.
Database Credentials (database)
Generate MongoDB database usernames and passwords independently as random 20-character alphanumeric strings. Each must contain uppercase letters, lowercase letters, and digits. Do not use fixed values such as root or reuse a credential across databases.
Embed Cross Part Relations (database)
Across part boundaries, embed the complete related data in the item. Our deployment architecture may place parts in physically separate databases, where database views cannot resolve cross-part relations.
No Same Part Embedded Relations (database)
Do not embed relations within items that belong to the same part.
Bare Native Import (node)
Import native Node.js modules without the node: protocol.
Correct examples
import fs from 'fs'
import { readFile } from 'fs'
Incorrect examples
import fs from 'node:fs'
Fix
Remove the node: protocol from native Node.js import specifiers.
Business Import (part/partApi)
In a part API transport module, import business operations through the generated ${part}Business alias. Do not reference business files through relative paths, deep package paths, or handwritten barrels.
Correct examples
import { syncUserByUuid } from 'accountsBusiness'
Incorrect examples
import syncUserByUuid from '../../business/user/syncUserByUuid.js'
Fix
Replace the business-module path with a named import from the current part’s ${part}Business alias.
Cross Part Business Import (part/partApi)
A part may import only its own generated ${part}Business alias. Import another part through that part’s public bundle or alias; never import another part’s business bundle or internal business modules directly.
Correct examples
Importing file: /home/dev/blog/api/api/site/post.js
import { findCategory } from 'taxonomy'
import { findRelatedPosts } from 'blogBusiness'
Incorrect examples
Importing file: /home/dev/blog/api/api/site/post.js
import { findCategory } from 'taxonomyBusiness'
import findCategory from 'taxonomy/business/category/find.js'
Fix
Replace the cross-part business import with an import from the other part’s public bundle or alias.
File System Hierarchy (part/partApi)
Structure each part API directory with api, business, and data directories.
Outbound Requests (part/partApi)
Part API must use the shared http.js utility for every outbound HTTP request. Set a timeout and maximum response size in http.js. Check each response and return a localized error when a request fails. Do not log secrets or customer data. Retry only when safe; do not retry writes unless repeating them cannot create duplicates.
Correct examples
import { httpPost } from 'core'
const response = await httpPost(url, payload)
Incorrect examples
const response = await fetch(url, options)
import { request } from 'undici'
Fix
Add timeout and response-size limits to http.js, then use it for every outbound HTTP request. Check responses and handle failures with localized errors.
Role Api Placement (part/partApi)
Put part API actions available to every consuming role in api/api/common. Put actions available only to a particular role in api/api/${role}. For example, place a post read action shared by site and admin APIs in common/post.js, and an admin-only post write action in admin/post.js. Keep authorization checks appropriate to each action; directory placement does not replace authorization.
Correct examples
blog/api/api/common/post.js: shared read actions
blog/api/api/admin/post.js: admin-only write actions
Incorrect examples
blog/api/api/admin/post.js: a read action needed by every role
blog/api/api/common/post.js: an admin-only write action
Fix
Move each shared action to the part’s common API directory and each role-specific action to the corresponding role directory. Keep shared business logic outside the transport files.