Nodes
Introduction
Nodes are the fundamental building blocks of the Nanoservice-ts framework. They encapsulate discrete pieces of business logic that can be composed together to create complex workflows. Each Node is designed to perform a specific task, making your code more modular, maintainable, and reusable.
Think of Nodes as specialized microservices that:
- Have clearly defined inputs and outputs
- Perform a single responsibility
- Can be tested in isolation
- Are composable through workflows
This modular approach allows developers to build complex applications by connecting simple, focused components rather than writing monolithic code.
Creating a Node
Using the CLI
The easiest way to create a new Node is using the Nanoservice-ts CLI:
When you run this command, you’ll see the following interactive prompts:
This command generates a complete Node package in your project’s src/nodes/
directory with the name you provided.
Node Structure
When you create a Node using the CLI, it generates the following folder structure:
Each Node is essentially a self-contained package with its own dependencies, configuration, and tests. This structure allows Nodes to be developed, tested, and versioned independently.
Node Implementation
The Node Class
The core of a Node is its implementation class, which extends the NanoService
base class from the @nanoservice-ts/runner
package. Here’s the template generated by the CLI:
Key Components of a Node Class
-
Type Definitions:
InputType
: Defines the structure of data the Node expects to receive.- You can also define an
OutputType
for better type safety of the response data.
-
Constructor:
- Initializes the Node and sets up JSON Schema for input and output validation.
- You can also set metadata like name, version, description, and category here.
-
Handle Method:
- The core method where your business logic resides.
- Takes a
Context
object and the typed inputs. - Returns a
NanoServiceResponse
containing either success data or an error. - Implements proper error handling with the
GlobalError
class.
Node Configuration
Each Node has a config.json
file that defines its metadata, input/output schemas, and examples:
This configuration file helps document the Node’s capabilities and provides examples for users.
Package Configuration
Each Node has its own package.json
file with dependencies and scripts:
This allows each Node to have its own dependencies and build process.
Testing Nodes
Nanoservice-ts generates test files for your Nodes automatically. The test setup includes:
Test Helper
The helper.ts
file provides a mock Context object for testing:
Test File
The index.test.ts
file contains basic tests for your Node:
Running Tests
You can run tests using the scripts defined in package.json
:
The Context Object
The Context (ctx
) object is a crucial part of the Nanoservice-ts framework. It’s passed to each Node’s handle
method and provides:
- Request Data: Information about the incoming request (for HTTP triggers).
- Response Data: Output from previous Nodes in the workflow.
- Configuration: Node-specific configuration.
- Logging: Methods for logging information, warnings, and errors.
- Error Handling: Standardized error reporting.
The Context object facilitates data flow between Nodes in a workflow and provides essential services to each Node.
Input and Output Validation
Nanoservice-ts uses JSON Schema for input and output validation. You can define schemas in your Node’s constructor:
These schemas ensure that:
- Your Node receives correctly formatted inputs
- Your Node produces correctly formatted outputs
- Errors are caught early and reported clearly
Error Handling
Proper error handling is essential in Nanoservice-ts. The framework provides the GlobalError
class for standardized error reporting:
This approach ensures that errors are properly captured, logged, and can be handled by the workflow.
Built-in Nodes
Nanoservice-ts comes with several built-in Nodes that provide common functionality. These are registered in the src/Nodes.ts
file:
Key Built-in Nodes
-
@nanoservice-ts/api-call
:- Makes HTTP requests to external APIs
- Supports various HTTP methods (GET, POST, PUT, DELETE, etc.)
- Handles request headers, body, and response parsing
-
@nanoservice-ts/if-else
:- Provides conditional branching in workflows
- Evaluates conditions and executes different steps based on the results
- Essential for creating dynamic workflows
Best Practices for Node Development
Single Responsibility Principle
Each Node should do one thing and do it well. If a Node is becoming complex, consider breaking it into multiple Nodes.
Clear Input/Output Contracts
Define precise input and output types and schemas. This makes your Nodes more predictable and easier to use.
Proper Error Handling
Always catch and properly report errors. Use the GlobalError
class for standardized error reporting.
Comprehensive Testing
Write thorough tests for your Nodes. Test both success and error scenarios.
Descriptive Naming
Use clear, descriptive names for your Nodes and their inputs/outputs. This makes workflows easier to understand.
Statelessness
Design Nodes to be stateless. Any state should be passed through the Context object or stored externally.
Reusability
Design Nodes to be reusable across different workflows. Avoid hardcoding workflow-specific logic.
Advanced Node Concepts
Custom Node Types
While the basic Node template is sufficient for most use cases, you can create specialized Node types for specific purposes:
- Transformation Nodes: Convert data from one format to another
- Integration Nodes: Connect to external services or APIs
- Decision Nodes: Implement complex business rules
- Aggregation Nodes: Combine data from multiple sources
Node Composition
Complex operations can be achieved by composing multiple Nodes in a workflow rather than creating complex Nodes. This approach:
- Improves maintainability
- Enhances reusability
- Simplifies testing
- Makes workflows more flexible
Node Versioning
As your application evolves, you may need to update your Nodes. Consider versioning strategies:
- Semantic versioning for Node packages
- Backward compatibility considerations
- Deprecation policies
- Migration strategies
Conclusion
Nodes are the foundation of the Nanoservice-ts framework. By understanding how to create, configure, and test Nodes, you can build modular, maintainable applications that are easy to extend and adapt to changing requirements.
The Node-based architecture encourages good software design practices like separation of concerns, modularity, and testability, leading to more robust and maintainable applications.
Next, learn about how to compose Nodes into Workflows to create complete applications.