Pagination

It’s often the case that you want to avoid querying an entire list from your API in order to minimize the amount of data transfers over the network. To support this, GraphQL APIs will “paginate” a field, allowing users to query a slice of the list. The strategy used to access slices of a list fall into two categories. Offset-based pagination relies offset and limit arguments and mimics the mechanisms provided by most database engines. Cursor-based pagination is a bi-directional strategy that relies on first/after or last/before arguments and is designed to handle modern pagination features such a infinite scrolling.

Regardless of the strategy used, houdini follows a simple pattern: wrap your document in a “paginated” function (ie, paginatedQuery or paginatedFragment), mark the field with @paginate, and provide the “page size” via the first, last or limit arguments to the field. paginatedQuery and paginatedFragment behave identically: they return a field containing a svelte store with your full dataset and functions you can call to load the next or previous page, as well as a readable store with a boolean loading state. For example, a field supporting offset-based pagination would look something like:

const { data, loadNextPage, loading } = paginatedQuery(graphql`
    query UserList {
        friends(limit: 10) @paginate {
            id
        }
    }
`)

and a field that supports cursor-based pagination starting at the end of the list would look something like:

const { data, loadPreviousPage } = paginatedQuery(graphql`
    query UserList {
        friends(last: 10) @paginate {
            edges {
                node {
                    id
                }
            }
        }
    }
`)

If you are paginating a field with a cursor-based strategy (forward or backwards), the current page info can be looked up with the pageInfo store returned from the paginated function:

<script>
    const { data, loadNextPage, pageInfo } = paginatedQuery(graphql`
        query UserList {
            friends(first: 10) @paginate {
                edges {
                    node {
                        id
                    }
                }
            }
        }
    `)
</script>

{#if $pageInfo.hasNextPage}
    <button onClick={() => loadNextPage()}> load more </button>
{/if}

Paginated Fragments

paginatedFragment functions very similarly to paginatedQuery with a few caveats. Consider the following:

const { loadNextPage, data, pageInfo } = paginatedFragment(graphql`
    fragment UserWithFriends on User {
        friends(first: 10) @paginate {
            edges {
                node {
                    id
                }
            }
        }
    }
`)

In order to look up the next page for the user’s friend. We need a way to query the specific user that this fragment has been spread into. In order to pull this off, houdini relies on the generic Node interface and corresponding query:

interface Node {
    id: ID!
}

type Query {
    node(id: ID!): Node
}

In short, this means that any paginated fragment must be of a type that implements the Node interface (so it can be looked up in the api). You can read more information about the Node interface in this section of the graphql community website. This is only a requirement for paginated fragments. If your application only uses paginated queries, you do not need to implement the Node interface and resolver.

For information about how to customize this behavior, check out the Caching Data guide.

Mutation Operations

A paginated field can be marked as a potential target for a mutation operation by passing a name argument to the @paginate directive. For more information, see this document.

const { loadNextPage, data, pageInfo } = paginatedFragment(graphql`
    fragment UserWithFriends on User {
        friends(first: 10) @paginate(name: "User_Friends") {
            edges {
                node {
                    id
                }
            }
        }
    }
`)