# Declarative JSON Pipeline Validation and Construction in ApraPipes: A Complete Technical Guide

> Learn how ApraPipes declaratively validates and constructs JSON pipelines using Node.js bindings. Understand the JsonParser, PipelineValidator, and ModuleFactory stages for efficient pipeline management.

- Repository: [Apra Labs/aprapipes](https://github.com/apra-labs/aprapipes)
- Tags: how-to-guide
- Published: 2026-02-25

---

**ApraPipes validates and constructs declarative JSON pipelines through a three-stage process involving `JsonParser`, `PipelineValidator`, and `ModuleFactory`, exposed via Node.js bindings as `validatePipeline()` and `createPipeline()`.**

The `apra-labs/aprapipes` library provides a robust mechanism for declarative JSON pipeline validation and construction, enabling developers to define complex media processing workflows in JSON while ensuring type safety and structural integrity before execution. This system separates pipeline description from instantiation, allowing static analysis and error detection prior to resource allocation.

## The Three-Stage Pipeline Lifecycle

ApraPipes processes every declarative JSON document through a strict lifecycle: parsing, validation, and construction. Each stage is implemented as a distinct C++ class with specific responsibilities.

### Stage 1: Parsing JSON with JsonParser

The `apra::JsonParser` class reads JSON input—either from a file or a string—and produces a `PipelineDescription` object. This object serves as the declarative representation of modules, their properties, and the connections between them. The parser handles schema compliance at the syntactic level, ensuring the JSON structure matches the expected pipeline format.

*Source*: [[`base/include/declarative/JsonParser.h`](https://github.com/apra-labs/aprapipes/blob/main/base/include/declarative/JsonParser.h)](https://github.com/apra-labs/aprapipes/blob/main/base/include/declarative/JsonParser.h)

### Stage 2: Multi-Phase Validation with PipelineValidator

Once parsed, the `PipelineDescription` passes to `apra::PipelineValidator`, which executes a series of independent validation phases. Each phase produces `ValidationIssue` objects (categorized as errors, warnings, or info) that aggregate into a `PipelineValidator::Result`. This result exposes helper methods including `hasErrors()`, `errors()`, and `format()` for programmatic and human-readable error reporting.

*Source*: [[`base/include/declarative/PipelineValidator.h`](https://github.com/apra-labs/aprapipes/blob/main/base/include/declarative/PipelineValidator.h)](https://github.com/apra-labs/aprapipes/blob/main/base/include/declarative/PipelineValidator.h), [[`base/src/declarative/PipelineValidator.cpp`](https://github.com/apra-labs/aprapipes/blob/main/base/src/declarative/PipelineValidator.cpp)](https://github.com/apra-labs/aprapipes/blob/main/base/src/declarative/PipelineValidator.cpp)

### Stage 3: Construction with ModuleFactory

After successful validation, `apra::ModuleFactory::build` instantiates concrete C++ module objects, wires their input/output pins according to the connection graph, and returns a runnable `PipeLine` instance. This stage translates the declarative description into executable objects that support lifecycle control methods: `init`, `run`, `pause`, `stop`, and `terminate`. Build-time errors are reported as `Issue` objects similar to validation issues.

*Source*: [[`base/src/declarative/ModuleFactory.cpp`](https://github.com/apra-labs/aprapipes/blob/main/base/src/declarative/ModuleFactory.cpp)](https://github.com/apra-labs/aprapipes/blob/main/base/src/declarative/ModuleFactory.cpp)

## Validation Phases Explained

The `PipelineValidator` implements five distinct validation phases to ensure pipeline integrity before construction.

### Module Validation

This phase verifies that every module type referenced in the JSON exists in the registry and that all required properties are present. Missing modules or undefined types trigger immediate errors.

### Property Validation

Each property value is checked against the expected type, range, and format defined by the module's metadata. Type mismatches (e.g., string where integer expected) or out-of-range values generate validation issues.

### Connection Validation

The validator guarantees that source and destination pins exist, that data-type compatibility is respected between connected pins, and that no dangling connections remain unconnected. This prevents runtime connection failures.

### Graph Validation

This phase detects cycles in the processing graph, identifies unreachable modules (dead code), and verifies overall graph consistency. Cycles are prohibited in ApraPipes pipelines to ensure acyclic data flow.

### Path Validation

When enabled, this optional phase verifies that filesystem paths referenced by module properties actually exist on the host system, preventing runtime file-not-found errors.

## Node.js API Integration

The `@apralabs/aprapipes` Node.js package exposes the C++ validation and construction logic through two high-level functions that encapsulate the three-stage workflow.

### Static Validation with validatePipeline()

The `validatePipeline(config)` function parses the JSON and runs the full validation suite without constructing the pipeline. It returns an object `{ valid: boolean, issues: Issue[] }`, making it safe for quick static checks and editor integrations. This function never allocates native resources or creates runnable objects.

*Implementation*: [[`base/bindings/node/addon.cpp`](https://github.com/apra-labs/aprapipes/blob/main/base/bindings/node/addon.cpp) → `ValidatePipeline`](https://github.com/apra-labs/aprapipes/blob/main/base/bindings/node/addon.cpp#L44)

### Runtime Construction with createPipeline()

The `createPipeline(config)` function executes the complete lifecycle: parsing, validation, and construction. If validation fails, it throws an exception containing a concise error list. On success, it returns a wrapped JavaScript `Pipeline` object with async methods (`init`, `run`, `stop`, `terminate`) that control the native C++ pipeline instance.

*Implementation*: [[`base/bindings/node/pipeline_wrapper.cpp`](https://github.com/apra-labs/aprapipes/blob/main/base/bindings/node/pipeline_wrapper.cpp) → `CreatePipeline`](https://github.com/apra-labs/aprapipes/blob/main/base/bindings/node/pipeline_wrapper.cpp#L9)

## JSON Schema Contract

The declarative pipeline format adheres to a formal JSON schema defined in [`docs/declarative-pipeline/pipeline-schema.json`](https://github.com/apra-labs/aprapipes/blob/main/docs/declarative-pipeline/pipeline-schema.json). This schema serves as the contract between the C++ validator, the Node.js bindings, and external editor tools, ensuring consistent validation across environments. Both the runtime validator and design-time UI validation use this shared schema to enforce structure.

*Schema*: [[`docs/declarative-pipeline/pipeline-schema.json`](https://github.com/apra-labs/aprapipes/blob/main/docs/declarative-pipeline/pipeline-schema.json)](https://github.com/apra-labs/aprapipes/blob/main/docs/declarative-pipeline/pipeline-schema.json)

## Code Examples

### Example 1: Static Validation Only

```javascript
const aprapipes = require('@apralabs/aprapipes');

const json = `
{
  "modules": {
    "source": { "type": "FileReaderModule", "props": { "path": "video.mp4" } },
    "sink":   { "type": "StatSink" }
  },
  "connections": [{ "from": "source", "to": "sink" }]
}
`;

const result = aprapipes.validatePipeline(json);
if (!result.valid) {
  console.error('Pipeline is invalid:');
  result.issues.forEach(i => console.error(`[${i.level}] ${i.code}: ${i.message}`));
} else {
  console.log('Pipeline passed validation');
}

```

### Example 2: Build and Run a Pipeline

```javascript
const { createPipeline } = require('@apralabs/aprapipes');

async function runDemo() {
  const pipeline = await createPipeline({
    modules: {
      src: { type: 'FileReaderModule', props: { path: 'sample.yuv' } },
      sink: { type: 'StatSink' }
    },
    connections: [{ from: 'src', to: 'sink' }]
  });

  await pipeline.init();      // Prepare internal resources
  await pipeline.run();      // Starts processing in background threads
  // … let it process for a while …
  await pipeline.stop();     // Gracefully stop processing
  await pipeline.terminate(); // Release native resources
}

runDemo().catch(console.error);

```

## Summary

- **Three-stage architecture**: ApraPipes processes declarative JSON pipelines through parsing (`JsonParser`), validation (`PipelineValidator`), and construction (`ModuleFactory`).
- **Comprehensive validation**: Five distinct phases check modules, properties, connections, graph integrity, and filesystem paths, producing detailed `ValidationIssue` reports.
- **Node.js integration**: The `@apralabs/aprapipes` package exposes `validatePipeline()` for static checks and `createPipeline()` for full instantiation with lifecycle control.
- **Shared schema contract**: The JSON schema at [`docs/declarative-pipeline/pipeline-schema.json`](https://github.com/apra-labs/aprapipes/blob/main/docs/declarative-pipeline/pipeline-schema.json) ensures consistent validation across C++ runtime and external tools.

## Frequently Asked Questions

### What happens if validation fails during `createPipeline()`?

If validation fails during `createPipeline()`, the function throws a JavaScript exception containing a concise list of validation errors. The pipeline is not constructed, and no native resources are allocated, ensuring safe failure without memory leaks.

### How does ApraPipes detect cycles in the pipeline graph?

The **Graph Validation** phase in `PipelineValidator` analyzes the connection topology to detect cycles. Since ApraPipes requires acyclic data flow for proper media processing, any circular dependencies between modules trigger a validation error before construction begins.

### Can I validate a pipeline without constructing it?

Yes. The `validatePipeline(config)` function in the Node.js bindings performs parsing and validation without invoking `ModuleFactory::build`. This allows safe static analysis and editor integration without allocating runtime resources or creating executable pipeline instances.

### What is the relationship between the JSON schema and the C++ validator?

The JSON schema at [`docs/declarative-pipeline/pipeline-schema.json`](https://github.com/apra-labs/aprapipes/blob/main/docs/declarative-pipeline/pipeline-schema.json) serves as the canonical contract shared between the C++ `PipelineValidator` and external tools. While the C++ validator implements detailed semantic checks (type compatibility, graph cycles), the schema enforces syntactic structure, ensuring consistent validation across runtime and design-time environments.