Link Loom Node.js Service Development Skill
This skill allows you to develop backend services for the Link Loom ecosystem, adhering to strict architectural, stylistic, and documentation standards.
Table of Contents
- Coding Standards
- Architecture & Concepts
- Naming Conventions
- Directory Structure
- Component Guidelines
- Registration & Indexing
- General Best Practices
- Resources & Documentation
- Instructions
- Examples
- Edge Cases
1. Coding Standards
Flat-Style & Defensive Coding (Critical)
- No Nested If-Else: Avoid deep nesting.
- Guard Clauses: Validate inputs at the beginning of the function.
- Negative Check First: Check for failure conditions (missing params, falsy values) immediately and return errors.
- Happy Path Last: The successful execution logic should remain at the end of the function.
Bad:
async myFunc(params) {
if (params) {
if (params.id) {
// logic...
return success;
} else {
return error;
}
} else {
return error;
}
}
Good (Required):
async myFunc({ params }) {
// 1. Defensive Checks
if (!params) return this._utilities.io.response.error('Missing params');
if (!params.id) return this._utilities.io.response.error('Missing ID');
// 2. Logic
const result = await this._doWork(params.id);
// 3. Negative Check on Result
if (!result) return this._utilities.io.response.error('Work failed');
// 4. Happy Path Return
return this._utilities.io.response.success(result);
}
2. Architecture & Concepts
Domain Driven Design (DDD) parity
CRITICAL: The backend structure dictates the frontend structure.
- Ensure that modules are organized by domain (e.g.,
workflow-orchestration/control-plane/...). - Changes in this structure must be reflected in the frontend to maintain 1:1 parity.
Understanding what to build is as important as how to build it.
| Concept | Usage |
|---|---|
| Service | Default choice. Contains business logic, database interactions, and complex operations. Extends BaseModel patterns usually. |
| Function | Single-purpose, often stateless utility or cloud function logic (e.g., timed, startup). Do not make a Function if it manages entity state. |
| App | A long-running app, self-contained module or mini-application logic. |
| Model | Data definition and schema. Must extend BaseModel. |
3. Naming Conventions
Strictly adhere to these naming conventions.
| Type | File Pattern | Class Name Pattern | Example |
|---|---|---|---|
| Service | kebab-case.service.js | [Domain][Entity]Service | WorkflowOrchestrationFlowDefinitionService (File: flow-definition.service.js) |
| Route | kebab-case.route.js | [Domain][Entity]Route | WorkflowOrchestrationFlowDefinitionRoute (File: flow-definition.route.js) |
| Model | kebab-case.model.js | [Domain][Entity]Model | WorkflowOrchestrationFlowDefinitionModel (File: flow-definition.model.js) |
| Utility | kebab-case.util.js | camelCase (func) | dateFormatter |
Rule: Class names MUST be the concatenation of the Full Domain Path + Entity Name.
- Path:
src/models/workflow-orchestration/control-plane/flow-design/flow-definition/ - Entity:
FlowDefinition - Class:
WorkflowOrchestrationFlowDefinitionModel
Rule: Class names MUST be the concatenation of the Full Domain Path + Entity Name.
- Path:
src/models/workflow-orchestration/control-plane/flow-design/flow-definition/ - Entity:
FlowDefinition - Class:
WorkflowOrchestrationFlowDefinitionModel
4. Directory Structure
Place files in the correct location based on their responsibility.
src/
├── routes/
│ ├── router.js # Main router aggregator
│ └── api/
│ └── <module-name>/
│ ├── <module>.routes.js # Module-specific router (e.g., chat.routes.js)
│ └── <feature>.route.js # Route handler class
├── services/
│ ├── index.js # Main service exporter
│ └── <module-name>/
│ └── <feature>.service.js # Logic
├── models/
│ ├── index.js # Main model exporter
│ └── <model-name>.model.js # Schema
5. Component Guidelines
Models
- Inheritance: MUST extend
BaseModel(from@link-loom/sdk). - Statuses:
entityStatusesMUST always include acolorproperty (hex code) for UI consistency. - Structure: Sub-models/Sub-entities MUST be placed in a
sub-entities/folder within the model's directory. - Strict Definitions: NO "black box" properties. All properties must be explicitly defined in
initializeEntityProperties. This prepares for TypeScript migration. - Synchronization: Every property MUST be present in 4 places:
- Class Declaration: Top of the class (e.g.,
name;). - Initialization: Inside
initializeEntityProperties(e.g.,this.name = ...). - Getter: Inside
get get()(e.g.,name: this.name?.value). - Swagger: Inside
@swaggerdefinition.
- Class Declaration: Top of the class (e.g.,
- No Redundant Props: Do not define
id,created,modified, orstatusmanually. These are inherited. - Swagger: MUST include full Swagger
@swaggerdocumentation for the schema. - Initialization: Implement explicit
initializeEntityProperties(args)method. - Getters: Implement
sanitizedandgetaccessors.
Services
- Constructor Order:
- Base Properties (
dependencies) - Custom Properties (private vars)
- Assignments
- Base Properties (
- Get Method: Must use a
switch(params.queryselector)to handle variants (id,all, etc.). - Private Methods: Use private methods (
#getById,#getAll) for specific logic, called by the main public methods. - Validation: Every public method must start with input validation (defensive coding).
Routes
- Micro-Routers: Do not add routes directly to
src/routes/router.js. Add them tosrc/routes/api/<module>/<module>.routes.js, then import that file in the main router. - Classes: Route handlers are classes.
- Swagger: Every route method must have full Swagger documentation defining inputs/outputs.
- CRUD Mapping: Typically map
get,create,update,deletehandlers, unless it is a command route (e.g.,execute).
6. Registration & Indexing
- Services: Must be exported in
src/services/index.js. - Models: Must be exported in
src/models/index.js. - Routes: Must be defined in the module's
*.routes.jsfile (e.g.,chat.routes.js) using the standard configuration object (httpRoute, route, handler, method).
7. General Best Practices
- Language: English ONLY for code and static text, unless explicitly requested otherwise by the user.
- Documentation: Avoid excessive comments. Document only complex algorithms. Code should be self-documenting.
- KISS Principle: keep it simple, stupid. Avoid overengineering. If a process is simple, keep the code simple.
- Naming: Use semantic variable names. NEVER use single-letter names like
x,ac,t. Names must indicate intent. - Clean Code: Remove unused imports, dependencies, and functions. No dead code.
- Git: Use Conventional Commits if asked to generate commit messages.
- Design Patterns: Act as an experienced architect. Use patterns (Factory, Singleton, Proxy, etc) only when necessary to solve a specific problem. Do not force patterns where simple logic suffices.
- Context: Do not infer if unsure. Always ask the user for clarification if requirements are not clear. Challenge user requests that lead to "garbage code" or antipatterns.
- Backend Specifics:
- Reuse: ALWAYS check
link-loom/loom-sdkdocs. Do not reinvent utilities or base classes that already exist. - Model Awareness: Understand existing backend models before creating any functionality to deeply understand the domain.
- Reuse: ALWAYS check
- Strictness: Since you are the expert, do not let the user start to write bad code patterns, warn him.
- Linting: MANDATORY. Code must be written adhering to the project's linter configuration (e.g.,
.prettierrc,.eslintrc.js).
8. Resources & Documentation
CRITICAL: Before inventing new utilities or patterns, check the local documentation.
- Link Loom SDK Docs:
link-loom/github/loom-sdk/docs- Core Utilities:
link-loom/github/loom-sdk/docs/core/utilities.module.md - Infrastructure:
link-loom/github/loom-sdk/docs/infrastructure/(Email, Storage, Database) - Data Types:
link-loom/github/loom-sdk/docs/core/data-types.module.md
- Core Utilities:
Use view_file on these paths to understand available tools before coding.
9. Instructions
- Read Context: Before creating a file, check the
index.jsorrouter.jsto see where it fits. - Use Assets: Copy the base structure from the
assets/templates. - Apply Standards: Refactor the template code to match the "Flat-Style" and specific logic requirements (switch cases, defensive checks).
- Document: Add Swagger JSDoc immediately.
- Register: Update the corresponding
index.jsorroutes.jsfile to register the new component.
10. Examples
Service Implementation
See assets/service.js.
Route Implementation
See assets/route.js.
App Implementation
See assets/app.js.
Model Implementation
See assets/model.js.
11. Edge Cases
- Missing Organization ID: Almost all create methods require
organization_id. Fail if missing. - Transaction Safety: If using database transactions, ensure errors are caught and logged properly before returning the error response.
- Legacy Code: If you see code that doesn't follow "Flat-Style", do not copy it. Upgrade it to the new standard.