npm package: @sprocketbot/gql-stores
Description: Wrapper for @urql/core that presents a store-based API instead of a context-based API
Latest version: 0.1.0-beta.4

# @sprocketbot/gql-stores

This package wraps [@urql/core](https://formidable.com/open-source/urql/docs/api/core/) as an alternative to [@urql/svelte](https://formidable.com/open-source/urql/docs/api/svelte/).

Instead of the context based approach taken by urql; this package uses a store approach to make operations easier to abstract outside of your components.

## Examples

### Simple Query

#### `People.store.ts`
```typescript
// Based on the GraphQL Zero API
// First, instantiate an urql client
import {Client} from "@urql/core";
export const client = new Client({url: "https://graphqlzero.almansi.me/api"});

// Create your return type (cannot be introspected)
type PeopleQueryValue = {
    users: {
        data: {
            id: string;
            name: string;
        }[];
    };
};

// Write your query; be careful that it matches the type your wrote
const query = gql<PeopleQueryValue>`
    query {
        users {
            data {
                name
                id
            }
        }
    }
`;

// Finally, instantiate your store:
export const PeopleQuery = new QueryStore(client, query, {});
```

#### `+page.svelte`
```html
<script lang="ts">
    import {PeopleQuery} from "./People.store";
</script>

<!-- Simple Rendering -->
<ul>
    <!-- Wait for fetching to complete -->
    <!-- Data is undefined until fetch completes -->
    {#if $PeopleQuery.fetching}
        <li>Loading...</li>
        <!-- Check for success -->
    {:else if $PeopleQuery.success}
        <!-- Access query results -->
        {#each $PeopleQuery.data.users.data as person}
            <li>{person.name}</li>
        {/each}
    {/if}
</ul>

<!-- Checking for errors -->
{#if $PeopleQuery.errors}
    {#if $PeopleQuery.errors.message}
        <!-- Combines all other messages -->
        {$PeopleQuery.errors.message}
    {/if}
    {#if $PeopleQuery.errors.graphQLErrors}
        {#each $PeopleQuery.errors.graphQLErrors as err}
            <!-- Access individual messages -->
            <p>{err.message}</p>
        {/each}
    {/if}
{/if}
```

### Paginated Query

#### `Posts.store.ts`
```typescript
// Based on the GraphQL Zero API
// First, instantiate an urql client
import {Client} from "@urql/core";
export const client = new Client({url: "https://graphqlzero.almansi.me/api"});

// Define types

export type PostsValue = {
    posts: {
        links: {
            prev?: {page: number};
            next?: {page: number};
        };
        data: {
            id: number;
            user: {id: number; name: string};
            title: string;
            body: string;
        }[];
    };
};

export type PostsVariables = {
    page: number;
};

// Define Query; note the variables generic
const query = gql<PostsValue, PostsVariables>`
    query ($page: Int!) {
        posts(options: {paginate: {limit: 5, page: $page}}) {
            links {
                prev {
                    page
                }
                next {
                    page
                }
            }
            data {
                user {
                    id
                    name
                }
                title
                body
                id
            }
        }
    }
`;

// Instantiate store; initial variables state is required
export const Posts = new QueryStore(client, query, {page: 1});
```

#### `+page.svelte`
```html
<script lang="ts">
    import {Posts} from "./Posts.store";

    // Helper functions
    function prevPage() {
        const data = $Posts.data
        if (!data || !data.posts.links.prev) return;
        // Update variables
        // This triggers a requery
        Posts.variables = { page: data.posts.links.prev.page}
    }
    function nextPage() {
        const data = $Posts.data
        if (!data || !data.posts.links.next) return;
        // Update variables
        // This triggers a requery
        Posts.variables = { page: data.posts.links.next.page}
    }
</script>

<!-- If we are fetching and don't have data; show loading -->
<!-- In this case, if we have data we will continue to show that -->
{#if $Posts.fetching && !$Posts.data}
    Loading...
{:else if $Posts.data}
    <!-- If we have data -->
    <div>
        <!-- Pagination -->
        <button
            disabled={!$Posts.data.posts.links.prev || $Posts.fetching}
            on:click={prevPage}
        >
            {`< ${$Posts.data.posts.links.prev?.page ?? " "} -`}
        </button>
        <span>{$Posts.variables.page}</span>
        <button
            disabled={!$Posts.data.posts.links.next || $Posts.fetching}
            on:click={nextPage}
        >
            {`- ${$Posts.data.posts.links.next?.page ?? " "} >`}
        </button>
    </div>
    <!-- Render posts; this is the same as the people example -->
    {#each $Posts.data.posts.data as post}
        <div>
            <h2>{post.title}</h2>
            <h4>{post.user.name}</h4>
            <p>{post.body}</p>
        </div>
    {/each}
{/if}
```

### Mutation
#### `CreateUser.mutation.ts`
```typescript
import {Mutation} from "$lib/Mutation";
import {gql} from "@urql/core";
import {client} from "../client";

export type CreatePersonValue = {
    createUser: {
        name: string;
        email: string;
        id: number;
    };
};
export type CreatePersonVariables = {
    input: {
        name: string;
        username: string;
        email: string;
    };
};

const mutation = gql<CreatePersonValue, CreatePersonVariables>`
    mutation ($input: CreateUserInput!) {
        createUser(input: $input) {
            name
            email
            id
        }
    }
`;

export const CreatePerson = Mutation(client, mutation);


// Usage
let createUserInput: CreatePersonVariables["input"] = {
    name: "Dave",
    username: "dave4", 
    email: "dave@dave.com"
};

const createPerson = await CreatePerson({ input: createUserInput })
```