> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sqd.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Pipe anatomy

> Understand the components of a Solana Pipes SDK data pipeline.

<Frame>
  <img src="https://mintcdn.com/sqd-2119b3c3/_EXKoCgFErRp1X2G/en/sdk/pipes-sdk/solana/guides/basic-development/anatomy-pipe.svg?fit=max&auto=format&n=_EXKoCgFErRp1X2G&q=85&s=5788dd76460b673f8816c71dd4dadc80" alt="Pipe anatomy" width="800" height="648" data-path="en/sdk/pipes-sdk/solana/guides/basic-development/anatomy-pipe.svg" />
</Frame>

A Solana pipe made with SQD's Pipes SDK consists of:

* A **source** - typically made with `solanaPortalStream()`. Can have one or more outputs.

* **Queries** - tell the source which data has to be retrieved to compute each output. A query is defined by a chain call terminated by `.build()`. Here's an example:

  ```ts theme={"system"}
  solanaQuery()
    .addFields({
      block: { timestamp: true },
      instruction: { programId: true, transactionIndex: true },
    })
    .addInstructionRequest({
      range: { from: 300_000_000 },
      request: { programId: [ ORCA_WHIRLPOOL_PROGRAM_ID ] },
    })
    .build()
  ```

* **Per-query transforms** (optional) - you can pass data from each query through a chain of simple transforms:
  ```ts theme={"system"}
  query
    .pipe(data => data.map(item => ({
      funkyNumber: item.header.timestamp + item.header.number,
      ...item
    })))
    .pipe(someOtherSimpleTransformCallback)
  ```
  Source object streams the data you get out of each chain of transforms as the value of the corresponding output field.

* Making utils that return reusable **query-transform combos** is a very useful pattern. In particular, on Solana it is often convenient to keep retrieval and decoding of program instructions in a single module. You can easily make such combos with the `solanaInstructionDecoder()` function - see the [Handling instructions](./handling-instructions) guide.

* **Whole pipe transformers** (optional) - use this if you need to compute something based on data originating from multiple queries, or if you need access to per-batch context (cursor, logger, profiler, rollback callbacks). Use `createTransformer()` so the SDK can thread cursor and rollback information ([1](../architecture-deep-dives/cursor-management), [2](../architecture-deep-dives/fork-handling)) through your transform:
  ```ts theme={"system"}
  import { createTransformer } from '@subsquid/pipes'

  const enrichSwaps = createTransformer<
    { swaps: Swap[]; swapsV2: SwapV2[] },
    { events: EnrichedSwap[] }
  >({
    transform: ({ swaps, swapsV2 }, ctx) => {
      ctx.logger.info({ batch: ctx.stream.state.current?.number }, 'enriching')
      return {
        events: [
          ...swaps.map((s) => ({ version: 'v1' as const, ...s })),
          ...swapsV2.map((s) => ({ version: 'v2' as const, ...s })),
        ],
      }
    },
  })

  solanaPortalStream({ /* ... */ }).pipe(enrichSwaps)
  ```

* Pipe termination: a plain async iterator or a **target**.
  * If you use the pipe as an async iterator it will throw exceptions if the underlying chain is experiencing reorgs, see [Fork handling](../architecture-deep-dives/fork-handling).
  * We offer four targets out of the box:

    * [Postgres via Drizzle](../../reference/basic-components/target/postgres-drizzle)
    * [ClickHouse](../../reference/basic-components/target/clickhouse)
    * [BigQuery](../../reference/basic-components/target/bigquery)
    * [Parquet files](../../reference/basic-components/target/parquet)

    You can make your own using [`createTarget()`](../../reference/basic-components/target/create-target).


## Related topics

- [Pipe anatomy](/en/sdk/pipes-sdk/evm/guides/basic-development/anatomy.md)
- [Developing pipes](/en/sdk/pipes-sdk/solana/guides/basic-development/flow.md)
- [Quickstart](/en/sdk/pipes-sdk/evm/quickstart.md)
- [Migrate to 1.0](/en/sdk/pipes-sdk/solana/migration.md)
- [Changelog](/changelog.md)
