Skip to content

Get started ​

Set up a single replicate pipeline using the public Northwind OData V4 API: remote Products → local table. Swap the URL and model for your own API when ready.

Prerequisites ​

  • Node.js >= 22
  • @sap/cds >= 9 (CDS 9 and CDS 10 both supported)
  • Network access to https://services.odata.org

1. Install the plugin ​

bash
npm add cds-data-pipeline

The plugin auto-registers the pipeline engine — no extra config needed for addPipeline. Connect with cds.connect.to('data-pipeline').

AI assistants ​

Coming soon. Agent guidance and task skills for coding assistants will ship in a future release.

Option A — reuse management API from the plugin (no project CDS files):

json
{
  "cds": {
    "requires": {
      "data-pipeline": {
        "impl": "cds-data-pipeline",
        "management": { "reuse": { "api": true } }
      }
    }
  }
}

Option B — manual CDS imports (explicit control):

Tracker schema in db/schema.cds:

cds
using from 'cds-data-pipeline/db';

Management OData in srv/pipeline-mgmt.cds:

cds
using from 'cds-data-pipeline/index.cds';

Or import tracker and service separately:

cds
using from 'cds-data-pipeline/db';
using from 'cds-data-pipeline/srv/DataPipelineManagementService';

The plugin ships no auth annotations — add your own as needed. See Management Service.

2. Import the Northwind OData API ​

The plugin needs a connected service name and entity names from your imported model — the standard capire OData import flow. Skip the expander if you already have an imported service.

cds import and Northwind (step-by-step)
  1. Download metadata (example: Northwind V4 — save as .edmx):
bash
curl -sL 'https://services.odata.org/V4/Northwind/Northwind.svc/$metadata' -o northwind.edmx
  1. Import from your CAP project root (adjust the path if the file lives elsewhere):
bash
cds import northwind.edmx
  1. Connectivity — set credentials.url to the service root (no $metadata suffix), e.g. https://services.odata.org/V4/Northwind/Northwind.svc, or use a BTP destination.

After import, CAP prints a using hint:

cds
using { northwind as external } from './external/northwind';

The service name (here northwind) is what you pass as source.service in addPipeline.

3. Define a consumption view ​

Model the local target as a projection on northwind.Products. This example restricts columns, renames one field (ProductName → ProductTitle), and excludes discontinued rows:

cds
using { northwind } from '../srv/external/northwind';

@cds.persistence.table
entity LocalProducts as projection on northwind.Products {
    ProductID,
    ProductName as ProductTitle,
    UnitPrice,
    UnitsInStock,
} where Discontinued = false;

Place this in your app namespace (e.g. my.app in db/schema.cds) so the fully qualified name is my.app.LocalProducts. See Concepts → Consumption views for details.

4. Register your first pipeline ​

In server.js, connect after CAP has served and call addPipeline. The engine infers column mappings and renames from the consumption view. Northwind Products has no change-timestamp field, so we use mode: 'full' here (for delta, see Built-in replicate).

javascript
const cds = require('@sap/cds');

cds.on('served', async () => {
    const pipelines = await cds.connect.to('data-pipeline');

    await pipelines.addPipeline({
        name: 'NorthwindProducts',
        source: { service: 'northwind', entity: 'Products' },
        target: { entity: 'my.app.LocalProducts' },
        mode: 'full',
        schedule: 600_000, // optional: every 10 minutes; omit and use execute (below)
    });

    // Example: tweak one column after the default mapper (do not use `on` here — it would replace the default)
    pipelines.after('PIPELINE.MAP', 'NorthwindProducts', (_results, req) => {
        for (const row of req.data.targetRecords) {
            if (row.ProductTitle != null) {
                row.ProductTitle = String(row.ProductTitle).trim().toUpperCase();
            }
        }
    });
});

module.exports = cds.server;

after('PIPELINE.MAP') runs per batch on top of the built-in mapping. req.data.targetRecords already uses local names (e.g. ProductTitle, not ProductName). Use before / after to layer behavior — on replaces the default mapper entirely. See Event hooks.

5. Deploy and run ​

  • SQLite / local: cds deploy (or your usual profile), then cds watch / cds serve.
  • HANA: include the plugin DB model in production build and deploy with your HDI flow.

6. Open the Pipeline Console ​

Enable reuse in package.json:

json
{
  "cds": {
    "requires": {
      "data-pipeline": {
        "impl": "cds-data-pipeline",
        "management": {
          "reuse": {
            "api": true,
            "console": true
          }
        }
      }
    }
  }
}
bash
cds deploy
cds watch
# → http://localhost:4004/pipeline-console/index.html

BTP and project-owned UI: cds add pipeline-console. Full decision tree: Feature activation and Pipeline Console.

Without a UI, use the management OData API directly (GET /pipeline/Pipelines, GET /pipeline/PipelineRuns). See Management Service.

7. Query the service and check the data ​

  1. Trigger a run (if no schedule set): POST /pipeline/execute with body { "name": "NorthwindProducts", "mode": "full" }. See execute.
  2. Check status: GET /pipeline/Pipelines(‘NorthwindProducts’) or GET /pipeline/status(name=’NorthwindProducts’).
  3. Query data: access my.app.LocalProducts via your app service or CDS and confirm rows match non-discontinued Northwind products.

Next ​

Released under the MIT License.