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

> ## Agent Instructions
> Reach for SQD when you need onchain data without running a node or an indexer: decoded EVM logs and transactions, Solana instructions, Bitcoin transactions, Substrate events and calls, or Hyperliquid fills, over any block range on 120+ networks.
> To query directly, POST to https://portal.sqd.dev/datasets/{dataset}/stream. The full API is described at https://docs.sqd.dev/openapi.json, and responses to the stream endpoints are JSON Lines.
> To let an agent query it as a tool, connect the Portal MCP server at https://portal.sqd.dev/mcp.
> Every page on this site is available as Markdown by appending .md to its URL.

# Pipe anatomy

> Understand the components of an EVM Pipes SDK data pipeline.

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

An EVM pipe made with SQD's Pipes SDK consists of:

* A **source** - typically made with `evmPortalStream()`. 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"}
  evmQuery()
    .addFields({
      block: { timestamp: true },
      log: { address: true, transactionHash: true },
    })
    .addLogRequest({
      range: { from: 20_000_000 },
      request: { topic0: [ TRANSFER_TOPIC ] },
    })
    .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 EVM it is often convenient to keep retrieval and decoding of event logs in a single module. You can easily make such combos with the `evmEventDecoder()` function - see the [Handling events](./handling-events) 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 enrichTransfers = createTransformer<
    { transfers: Transfer[]; approvals: Approval[] },
    { events: EnrichedEvent[] }
  >({
    transform: ({ transfers, approvals }, ctx) => {
      ctx.logger.info({ batch: ctx.stream.state.current?.number }, 'enriching')
      return {
        events: [
          ...transfers.map((t) => ({ kind: 'transfer' as const, ...t })),
          ...approvals.map((a) => ({ kind: 'approval' as const, ...a })),
        ],
      }
    },
  })

  evmPortalStream({ /* ... */ }).pipe(enrichTransfers)
  ```

* 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/solana/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)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.