---
title: "Filtering Feeds"
description: "Control what data streams to your client with pagination, sorting, and custom views."
editUrl: true
head: []
template: "doc"
sidebar: {"order":4,"hidden":false,"attrs":{}}
pagefind: true
draft: false
---

When streaming data from views, you can control what the server sends to reduce bandwidth and improve performance.

---

## Feed Parameters

All Arete SDKs support these parameters when subscribing to feeds:

| Parameter | Type     | Description                                 |
| --------- | -------- | ------------------------------------------- |
| `take`    | `number` | Maximum number of entities to return        |
| `skip`    | `number` | Number of entities to skip (for pagination) |
| `key`     | `string` | Entity key for state view subscriptions     |

### Pagination

Use `take` and `skip` to paginate through large datasets:

```typescript
import { useArete } from "arete-react";
import { TOKEN_STACK } from "arete-stacks/token";

function TokenList() {
  const a4 = useArete(TOKEN_STACK);
  const [page, setPage] = useState(1);
  const pageSize = 10;

  // Paginated subscription
  const { data: tokens } = a4.views.Token.list.use({
    take: pageSize,
    skip: (page - 1) * pageSize,
  });

  return (
    <div>
      {tokens?.map(t => <TokenRow key={t.id.mint} token={t} />)}
      <button onClick={() => setPage(p => p + 1)}>Next Page</button>
    </div>
  );
}
```

```typescript
import { Arete } from "arete-typescript";
import { TOKEN_STACK } from "arete-stacks/token";

const a4 = await Arete.connect("wss://token.stack.arete.run", {
  stack: TOKEN_STACK,
});

// Get first 10 entities
for await (const token of a4.views.Token.list.use({ take: 10 })) {
  console.log(token.id.mint);
}

// Get page 3 (skip 20, take 10)
const page = 3;
const pageSize = 10;
for await (const token of a4.views.Token.list.use({
  take: pageSize,
  skip: (page - 1) * pageSize
})) {
  console.log(token.id.mint);
}
```

```rust
use a4_sdk::prelude::*;
use a4_stacks::token::{TokenStack, Token};

let a4 = Arete::<TokenStack>::connect().await?;

// Get first 10 entities
let mut stream = a4.views.token.list()
    .listen()
    .take(10);

// Get page 3 (skip 20, take 10)
let page = 3;
let page_size = 10;
let mut stream = a4.views.token.list()
    .listen()
    .skip((page - 1) * page_size)
    .take(page_size);

while let Some(token) = stream.next().await {
    println!("Token: {:?}", token.id.mint);
}
```

### State View Keys

For state views, pass the entity key to subscribe to a specific entity:

```typescript
const { data: token } = a4.views.Token.state.use(tokenAddress);
```

```typescript
const tokenAddress = "So11111111111111111111111111111111111111112";

// One-shot query
const token = await a4.views.Token.state.get(tokenAddress);

// Stream updates
for await (const token of a4.views.Token.state.use(tokenAddress)) {
  console.log("Token updated:", token.id.mint);
}
```

```rust
let token_address = "So11111111111111111111111111111111111111112";

let mut stream = views.state(token_address).watch();

while let Some(update) = stream.next().await {
    println!("Token updated: {:?}", update.data.id.mint);
}
```

---

## Custom Views

Beyond the default `state` and `list` views, stacks can define **custom views** with sorting and limits applied at the server level.

:::note[For stack builders]
Custom views are defined when [building a stack](/building-stacks/stack-definitions/) using the Rust DSL. If you're just consuming an existing stack, skip to the [Accessing Custom Views](#accessing-custom-views) section below.
:::

### Why Custom Views?

- **Reduced bandwidth** - Server applies sorting/limits before transmitting
- **Consistent ordering** - Sort order is defined once in your stack
- **Pre-configured limits** - Useful for "top N" or "latest N" views

### Defining Custom Views

Custom views are defined using the `#[view]` attribute on your entity struct:

```rust
use arete::prelude::*;

#[arete(idl = "idl/ore.json")]
pub mod ore_stream {
    #[entity(name = "OreRound")]
    #[view(name = "latest", sort_by = "id.round_id", order = "desc")]
    pub struct OreRound {
        pub id: RoundId,
        pub state: RoundState,
        // ... other fields
    }
}
```

### View Parameters

| Parameter | Type                | Description                                   |
| --------- | ------------------- | --------------------------------------------- |
| `name`    | `string`            | View name (e.g., `"latest"`)                  |
| `sort_by` | `string`            | Field path to sort by (e.g., `"id.round_id"`) |
| `order`   | `"asc"` \| `"desc"` | Sort order (default: `"desc"`)                |
| `take`    | `number`            | Optional limit on results                     |

### Accessing Custom Views

Custom views appear alongside the default views in your SDK:

```typescript
import { useArete } from "arete-react";
import { ORE_STREAM_STACK } from "arete-stacks/ore";

function LatestRounds() {
  const a4 = useArete(ORE_STREAM_STACK);

  // Default views
  const { data: allRounds } = a4.views.OreRound.list.use();
  const { data: round } = a4.views.OreRound.state.use(roundAddress);

  // Custom view - sorted by round_id desc
  const { data: latestRounds } = a4.views.OreRound.latest.use();

  return <div>{latestRounds?.map(r => <Round key={r.id.round_id} round={r} />)}</div>;
}
```

```typescript
import { Arete } from "arete-typescript";
import { ORE_STREAM_STACK } from "arete-stacks/ore";

const a4 = await Arete.connect("wss://ore.stack.arete.run", {
  stack: ORE_STREAM_STACK,
});

// Default views
const allRounds = await a4.views.OreRound.list.get();
const round = await a4.views.OreRound.state.get(roundAddress);

// Custom view - sorted by round_id desc
for await (const round of a4.views.OreRound.latest.use()) {
  console.log("Round:", round.id.round_id);
}
```

```rust
use a4_sdk::prelude::*;
use a4_stacks::ore::{OreStack, OreRound};

let a4 = Arete::<OreStack>::connect().await?;

// Default views
let mut list_stream = a4.views.ore_round.list().listen();
let round_address = "some-round-address";
let mut state_stream = a4.views.ore_round.state().listen(round_address);

// Custom view - sorted by round_id desc
let mut latest_stream = a4.views.ore_round.latest().listen();

while let Some(round) = latest_stream.next().await {
    println!("Round: {:?}", round.id.round_id);
}
```

### Example: Top 10 View

```rust
#[entity(name = "Token")]
#[view(name = "topByVolume", sort_by = "metrics.total_volume", order = "desc", take = 10)]
pub struct Token {
    pub id: TokenId,
    pub metrics: TokenMetrics,
}
```

```typescript
// Get top 10 tokens by volume
for await (const token of a4.views.Token.topByVolume.use()) {
  console.log(token.id.mint, token.metrics.total_volume);
}
```

See [Stack Definitions](/building-stacks/stack-definitions/) for complete documentation on defining entities and views.

---

## Client-Side Filtering

If you need dynamic filtering that changes at runtime, apply filters client-side after receiving data:

The React SDK provides a `where` clause for declarative client-side filtering:

```typescript
import { useArete } from "arete-react";
import { TOKEN_STACK } from "arete-stacks/token";

function HighVolumeTokens() {
  const a4 = useArete(TOKEN_STACK);

  // Client-side filtering with where clause
  const { data: tokens } = a4.views.Token.list.use({
    where: {
      volume: { gte: 10000 },
      price: { lte: 100 }
    },
    limit: 20,  // Client-side limit
  });

  return <TokenList tokens={tokens} />;
}
```

**Supported `where` operators:**

| Operator  | Description           |
| --------- | --------------------- |
| `gte`     | Greater than or equal |
| `lte`     | Less than or equal    |
| `gt`      | Greater than          |
| `lt`      | Less than             |
| _(value)_ | Exact match           |

Filter data as it streams using standard JavaScript:

```typescript
import { Arete } from "arete-typescript";
import { ORE_STREAM_STACK } from "arete-stacks/ore";

const a4 = await Arete.connect("wss://ore.stack.arete.run", {
  stack: ORE_STREAM_STACK,
});

// Filter in the streaming loop
for await (const round of a4.views.OreRound.latest.use()) {
  // Skip rounds below threshold
  if ((round.state.motherlode ?? 0) < 1_000_000_000) continue;

  console.log("High-value round:", round.id.round_id);
}
```

Use iterator adapters to filter streams:

```rust
use a4_sdk::prelude::*;
use a4_stacks::ore::{OreStack, OreRound};
use futures::StreamExt;

let a4 = Arete::<OreStack>::connect().await?;

// Filter using stream adapters
let mut stream = a4.views.ore_round.latest()
    .listen()
    .filter(|round| {
        let high_value = round.state.motherlode.flatten().unwrap_or(0) >= 1_000_000_000;
        futures::future::ready(high_value)
    });

while let Some(round) = stream.next().await {
    println!("High-value round: {:?}", round.id.round_id);
}
```
