Skip to content
Talk to our solutions team

Pipeline library

Bulk data movement: read a source, reshape the records, write a sink. Bound by Datapipes, and by AI Flow in its streaming forms.

Terminal window
kis datapipes -f pipeline.yaml -v batch=2026-06 -e env.yaml

This library stands alone. A datapipe binds it and nothing else — no shell:, no ssh:, no git:. See Flow engines for the reasoning; the short version is that a pipeline streams records, and the common atoms are units of work rather than stages in a stream.

SourceDatapipe taskScript functions
CSVread-csv:csv.read
Excelread-excel:excel.read
Parquetread-parquet:parquet.read
Databaseread-db:db.query
RESTread-rest:, read-rest-json:, read-rest-json-pipe:rest.request
Standard outputread-stdout:stdout.exec
Entityreadentity:entity.query
Entity in a datastorereadentity-in-datastore:entity.query

Three REST readers exist because three shapes of API do. read-rest: is the plain one; read-rest-json: unwraps a JSON body; read-rest-json-pipe: streams rather than materialising, which is the one to use when the response is larger than you want in memory.

SinkDatapipe taskScript functions
CSVwritecsv:csv.write
Excelwriteexcel:excel.write
Parquetwriteparquet:parquet.write
Databasewritedb:db.execute
RESTwriterest:rest.request
Entity in a datastorewriteentity-in-datastore:entity.mutate
OperationDatapipe taskScript functions
Reshape recordsmorph:morph.execute
Aggregateaggregate:, aggsum:aggregate.compute
Sum a numeric columnsum:compute.sum
Concatenatesumstr:compute.concat
Duplicate a streamreplicate:compute.replicate
Run a script per recordplugin:(you are already in one)
Print recordsprint:log.info
Do nothingnoop:

morph: is the general transform and where most pipeline logic lives — renaming fields, changing types, dropping rows, deriving values. It runs a script per record: language: picks the runtime, execute: carries the body, and the record arrives as the function’s argument. plugin: is the escape hatch when a record needs real logic, and it runs the same script engine as everything else.

noop: is not filler. It gives a pipeline a named join point or a placeholder stage while the real one is being written, without changing the shape of the graph.

The three parts read in order — source, transform, sink:

name: monthly-export
pipeline: true
tasks:
- name: source
read-db:
query: "select id, name, total from orders where month = '{{batch}}'"
next:
go: reshape
- name: reshape
morph:
language: javascript
execute: |
function main(record) {
return {
id: record.id,
customer: record.name,
amount_cents: record.total * 100,
};
}
next:
go: sink
- name: sink
writeparquet:
path: "./exports/orders-{{batch}}.parquet"

Note pipeline: true — it selects the streaming execution model. Without it the same file is read as an ordinary flow, and the pipeline tasks are not available.

A script reaches all of these namespaces and the common ones, so it can read a CSV, shell out and write to S3 in one function. That flexibility is real, and it is also the reason to keep it for one-off work.

For anything recurring, the pipeline is the better home: it streams rather than loading, it records what completed, and the stages are legible to whoever inherits it. Reach for a script when the job is a one-off reshape, and for a pipeline when it will run again.