Naming Conventions
This document outlines the standard naming conventions and coding patterns for the backend hooks codebase. Follow these guidelines to ensure consistency and maintainability.
Directory Structure
Hooks are organized under apps/api/src/app/hooks in a collection → hook hierarchy. Each hook has its own directory containing 3 files:
apps/api/src/app/hooks/
├── finance-general-ledger/ ← hook collection (kebab-case)
│ ├── collection.json ← collection metadata
│ ├── create-general-ledger-account/ ← individual hook (kebab-case)
│ │ ├── hook.json ← hook metadata (name, slug, path, version, etc.)
│ │ ├── context.md ← hook description
│ │ └── create-general-ledger-account.finance-general-ledger.hook.ts
│ └── delete-general-ledger-account/
│ ├── hook.json
│ ├── context.md
│ └── delete-general-ledger-account.finance-general-ledger.hook.ts
File Naming
Hook files follow a strict kebab-case pattern: hook-name.collection-name.hook.ts
- Format:
hook-slug.collection-slug.hook.ts - Extension:
.hook.ts
Examples
| Status | Filename | Reason |
|---|---|---|
| ✅ DO | calculate-new-transaction.finance-general-ledger.hook.ts | Correct kebab-case + collection + extension |
| ✅ DO | complete-manufacturing-order.manufacturing-ai.hook.ts | Correct kebab-case + collection + extension |
| ❌ DON'T | FinanceGeneralLedger.ts | Wrong case, missing .hook, missing collection |
| ❌ DON'T | calculateNewTransaction.hooks.ts | camelCase, old .hooks extension, missing collection |
Class Naming
Class names combine the collection name and hook name in PascalCase, without underscores or unique IDs.
- Format:
PascalCaseCollectionPascalCaseHookName
Examples
| Status | Class Name | Reason |
|---|---|---|
| ✅ DO | FinanceGeneralLedgerCreateGeneralLedgerAccount | Collection + hook name in PascalCase |
| ✅ DO | ManufacturingAiGenerateManufacturingSummary | Collection + hook name in PascalCase |
| ❌ DON'T | FinanceGeneralLedger_iqbvltad | Old format with underscore + unique ID |
| ❌ DON'T | createGeneralLedgerAccount | camelCase, missing collection prefix |
Method Naming
The entry-point method for every hook is always execute(). This is the method that the event engine calls.
- Entry point: Always
public static execute() - Internal helpers: Use descriptive
camelCasenames (e.g.,calculateTotal,validateUser)
Examples
| Status | Method Name | Context |
|---|---|---|
| ✅ DO | execute() | Entry-point method — always this name |
| ✅ DO | calculateSubtotal() | Internal helper method |
| ❌ DON'T | calculateBillOfMaterials() | As an entry point — use execute() |
| ❌ DON'T | Run() | PascalCase, too generic |
Code Structure & Patterns
We follow a strict "Bob Request" pattern for all hook methods, wrapped with the @LogicHook decorator.
The @LogicHook Decorator
Every hook class must be decorated with @LogicHook which provides metadata to the event engine:
import { BobRequest, returnEventResult } from '@logic-bee/event-engine'
import { NaoQueryOptions } from '@logic-bee/flow-query'
import { naoFormatErrorById, SuperJoi } from '@logic-bee/utils'
import { LogicHook } from '@logic-bee/logic-hooks'
@LogicHook({
name: 'Create general ledger account',
slug: 'create-general-ledger-account',
path: 'finance-general-ledger/create-general-ledger-account',
legacy: 'FinanceGeneralLedger_iqbvltad.createGeneralLedgerAccount',
hookCollection: 'finance-general-ledger',
version: 1,
concurrency: 'parallel',
timeout: 30000,
idempotent: false,
pattern: 'hook',
patternScore: 0
})
export class FinanceGeneralLedgerCreateGeneralLedgerAccount {
/**
* Describe what this method does clearly.
*/
public static execute() {
return async (
bob: BobRequest<{
data: {
data: any
naoQueryOptions: NaoQueryOptions
}
}>
) => {
let ok = true,
error: any = null,
data
try {
/**START_CODE_LOGIC*/
// Your business logic goes here
ok = true
data = { result: "success" }
/**END_CODE_LOGIC*/
} catch (err) {
error = err
ok = false
}
// -->Return: result
return returnEventResult(bob, { ok, error, data })
}
}
}
@LogicHook Properties
| Property | Description |
|---|---|
name | Human-readable hook name |
slug | Kebab-case identifier |
path | collection-slug/hook-slug |
legacy | Old format reference (for migration) |
hookCollection | Collection slug this hook belongs to |
version | Hook version number |
concurrency | 'parallel' or 'sequential' |
timeout | Execution timeout in milliseconds |
idempotent | Whether the hook is safe to retry |
pattern | Hook pattern type (e.g., 'hook') |
patternScore | Priority/ordering score |
Comments & Annotations
Use specific comment styles to denote actions and logic blocks.
- Logic Blocks: Always wrap your core logic with
/**START_CODE_LOGIC*/and/**END_CODE_LOGIC*/. - Action Steps: Use
// -->Action: descriptionto describe significant steps in the process.
// -->Update: manufacturing order with finalize reason and user
// -->Verb: {subject} {qualifier} │ │ │ │ │ └── condition, source, or constraint │ └──────────── the exact entity being acted on └──────────────────── what operation
Examples
// ✅ DO: Use directional comments for steps
// -->Get: inventory settings
const inventorySettings = await ...
// -->Check: if the item exists
if (!item) { ... }
// -->Calculate: total cost
const total = ...
// ❌ DON'T: Use random comment styles
// getting the settings now
// checking item
Common Pitfalls
- DON'T put logic outside the
try...catchblock. - DON'T forget to return
returnEventResult. - DON'T use
console.logfor debugging; usenaoLoggerorlogg. - DON'T name variables with single letters (e.g.,
x,y); use descriptive names (e.g.,stockItem,totalCost). - DON'T use
@bob-api/*imports; use@logic-bee/*.