# `WhatsApp.Page`
[🔗](https://github.com/jeffhuen/whatsapp_sdk/blob/main/lib/whatsapp/page.ex#L1)

Paginated API response with cursor-based navigation.

Supports eager fetching (list one page) and lazy streaming
(process all pages via `Stream`).

## WhatsApp Pagination Format

The WhatsApp Cloud API uses cursor-based pagination:

    %{
      "data" => [...],
      "paging" => %{
        "cursors" => %{"before" => "QVFIUl...", "after" => "QVFIUk..."},
        "next" => "https://graph.facebook.com/v23.0/.../message_templates?after=QVFIUk...",
        "previous" => "https://graph.facebook.com/v23.0/.../message_templates?before=QVFIUl..."
      }
    }

## Usage

    # Parse a response into a Page
    page = WhatsApp.Page.from_response(response_body, &deserialize/1)

    # Check for more pages
    WhatsApp.Page.has_next?(page)

    # Stream all items across all pages lazily
    page
    |> WhatsApp.Page.stream(client)
    |> Stream.filter(&active?/1)
    |> Enum.to_list()

# `t`

```elixir
@type t() :: %WhatsApp.Page{
  cursors: %{before: String.t() | nil, after: String.t() | nil},
  data: list(),
  next_url: String.t() | nil,
  previous_url: String.t() | nil
}
```

# `from_response`

```elixir
@spec from_response(map(), (map() -&gt; any()) | nil) :: t()
```

Parse a paginated API response into a Page struct.

`response_map` is the decoded JSON map with `"data"` and `"paging"` keys.
`deserialize_fn` is a function that transforms each data item. Pass `nil`
to keep raw maps.

## Examples

    iex> response = %{"data" => [%{"id" => "1"}], "paging" => %{"cursors" => %{"after" => "abc"}}}
    iex> page = WhatsApp.Page.from_response(response, nil)
    iex> page.data
    [%{"id" => "1"}]

# `has_next?`

```elixir
@spec has_next?(t()) :: boolean()
```

Returns `true` if the page has a next page URL.

## Examples

    iex> WhatsApp.Page.has_next?(%WhatsApp.Page{next_url: "https://..."})
    true

    iex> WhatsApp.Page.has_next?(%WhatsApp.Page{next_url: nil})
    false

# `has_previous?`

```elixir
@spec has_previous?(t()) :: boolean()
```

Returns `true` if the page has a previous page URL.

## Examples

    iex> WhatsApp.Page.has_previous?(%WhatsApp.Page{previous_url: "https://..."})
    true

    iex> WhatsApp.Page.has_previous?(%WhatsApp.Page{previous_url: nil})
    false

# `stream`

```elixir
@spec stream(t(), WhatsApp.Client.t() | nil, keyword()) :: Enumerable.t()
```

Create a lazy stream that auto-pages through all items.

Uses `Stream.unfold/2` to yield individual items from the current page,
then automatically fetches the next page when data is exhausted.

The stream stops when there are no more pages or on fetch error.

## Options

  * `:deserialize_fn` - Function to apply to items from fetched pages.
    Items from the initial page are yielded as-is (already deserialized
    by `from_response/2`).
  * `:fetch_fn` - Custom fetch function `(client, url) -> {:ok, map()} | {:error, term()}`.
    Defaults to `WhatsApp.Client.request_url/2`. Useful for testing.

## Examples

    # Stream all templates across all pages
    page
    |> WhatsApp.Page.stream(client, deserialize_fn: &deserialize/1)
    |> Enum.to_list()

    # Take only the first 50 items (lazy — fetches only needed pages)
    page
    |> WhatsApp.Page.stream(client)
    |> Enum.take(50)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
