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

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)

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), [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)

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.cppValidatePipeline](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.cppCreatePipeline](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. 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)

Code Examples

Example 1: Static Validation Only

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

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 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →