Current section
Files
Jump to
Current section
Files
lib/alpa/pagination.ex
defmodule Alpa.Pagination do
@moduledoc """
Pagination helpers for Alpaca API list endpoints.
Fetches results from paginated endpoints. Currently supports single-page
fetching with configurable limits. Multi-page token-based pagination
will be added when API functions are updated to return `next_page_token`.
## Usage
# Fetch results (respects :limit option)
{:ok, orders} = Alpa.Pagination.all(&Alpa.Trading.Orders.list/1, status: "closed", limit: 500)
# Stream results lazily
Alpa.Pagination.stream(&Alpa.Trading.Orders.list/1, status: "closed")
|> Stream.take(100)
|> Enum.to_list()
## Limitations
Alpaca's API uses `next_page_token` for pagination, but the SDK's API
functions currently return only the parsed results without the token.
Until the response format is extended to include pagination metadata,
`all/2` and `stream/2` fetch a single page per call. Use the `:limit`
option to control how many results are returned.
"""
@doc """
Fetch all results from a paginated endpoint.
Calls the given function with the provided options and returns all results.
## Parameters
* `fetch_fn` - A function that accepts a keyword list of options
and returns `{:ok, results}` or `{:error, error}`
* `opts` - Options to pass to the fetch function
## Examples
iex> Alpa.Pagination.all(&Alpa.Trading.Orders.list/1, status: "closed", limit: 500)
{:ok, [%Alpa.Models.Order{}, ...]}
"""
@spec all((keyword() -> {:ok, list()} | {:error, term()}), keyword()) ::
{:ok, list()} | {:error, term()}
def all(fetch_fn, opts \\ []) do
case fetch_fn.(opts) do
{:ok, results} when is_list(results) -> {:ok, results}
{:ok, other} -> {:ok, other}
{:error, _} = error -> error
end
end
@doc """
Create a lazy stream of results from a paginated endpoint.
Returns a `Stream` that fetches a page on demand. Each element
in the stream is an individual result item (not a page).
## Examples
Alpa.Pagination.stream(&Alpa.Trading.Orders.list/1, status: "closed")
|> Stream.take(50)
|> Enum.each(&IO.inspect/1)
"""
@spec stream((keyword() -> {:ok, list()} | {:error, term()}), keyword()) :: Enumerable.t()
def stream(fetch_fn, opts \\ []) do
Stream.resource(
fn -> :initial end,
fn
:done ->
{:halt, :done}
:initial ->
case fetch_fn.(opts) do
{:ok, [_ | _] = results} ->
{results, :done}
{:ok, _} ->
{:halt, :done}
{:error, _} ->
{:halt, :done}
end
end,
fn _state -> :ok end
)
end
end