# GraphQL Usage with Reactive Data Client

```bash
npm install @data-client/graphql
```

## Define Endpoint and Schema

```ts title="schema/endpoint.ts"
export const gql = new GQLEndpoint('https://nosy-baritone.glitch.me');
export default gql;
```

```typescript title="schema/User.ts"
import { GQLEntity } from '@data-client/graphql';

export default class User extends GQLEntity {
  name: string | null = null;
  email = '';
  age = 0;
}
```

```js title="schema/User.ts"
import { GQLEntity } from '@data-client/graphql';

export default class User extends GQLEntity {}
```

[Entity](https://dataclient.io/graphql/api/GQLEntity.md)s are immutable. Use `readonly` in typescript to enforce this.

> **Tip**
>
> Using GQLEntities is not required, but is important to achieve data consistency.

## Query the Graph

**Single**

```tsx title="pages/UserDetail.tsx"
import { useSuspense } from '@data-client/react';
import User from 'schema/User';
import gql from 'schema/endpoint';

export const userDetail = gql.query(
  (v: { name: string }) => `query UserDetail($name: String!) {
    user(name: $name) {
      id
      name
      email
    }
  }`,
  { user: User },
);

export default function UserDetail({ name }: { name: string }) {
  const { user } = useSuspense(userDetail, { name });
  return (
    <article>
      <h2>{user.name}</h2>
      <div>{user.email}</div>
    </article>
  );
}
```

**List**

```tsx title="pages/UserList.tsx"
import { useSuspense } from '@data-client/react';
import User from 'schema/User';
import gql from 'schema/endpoint';

const userList = gql.query(
  `{
    users {
      id
      name
      email
      }
    }`,
  { users: [User] },
);

export default function UserList() {
  const { users } = useSuspense(userList, {});
  return (
    <section>
      {users.map(user => (
        <UserSummary key={user.pk()} user={user} />
      ))}
    </section>
  );
}
```

[useSuspense()](https://dataclient.io/docs/api/useSuspense.md) guarantees access to data with sufficient [freshness](https://dataclient.io/graphql/api/GQLEndpoint.md#dataexpirylength).
This means it may issue network calls, and it may [suspend](https://dataclient.io/docs/getting-started/data-dependency.md#boundaries) until the fetch completes.
Param changes will result in accessing the appropriate data, which also sometimes results in new network calls and/or
suspends.

- Fetches are centrally controlled, and thus automatically deduplicated
- Data is centralized and normalized guaranteeing consistency across uses, even with different [endpoints](https://dataclient.io/graphql/api/GQLEndpoint.md).
  - (For example: navigating to a detail page with a single entry from a list view will instantly show the same data as the list without
    requiring a refetch.)

<details>

<summary>SWAPI Demo</summary>

```tsx
import { useSuspense } from '@data-client/react';
import { GQLEndpoint, GQLEntity } from '@data-client/graphql';

const gql = new GQLEndpoint(
  'https://swapi-graphql.netlify.app/graphql',
);
class Person extends GQLEntity {
  readonly id: string = '';
  readonly name: string = '';
  readonly height: string = '';
}
const PageInfo = {
  hasNextPage: false,
  startCursor: '',
  endCursor: '',
};
const allPeople = gql.query(
  (v: { first?: number; after?: string }) => `
query People($first: Int, $after:String) {
  allPeople(first: $first, after:$after) {
    people{
      id,name,height
    },
    pageInfo {
      hasNextPage,
      startCursor,
      endCursor
    }
  }
}
`,
  { allPeople: { people: [Person], pageInfo: PageInfo } },
);
function StarPeople() {
  const { people, pageInfo } = useSuspense(allPeople, {
    first: 5,
  }).allPeople;
  return (
    <div>
      {people.map(person => (
        <div key={person.id}>
          name: {person.name} height: {person.height}
        </div>
      ))}
    </div>
  );
}
render(<StarPeople />);
```

</details>

## Mutate the Graph

We're using [SWAPI](https://graphql.org/swapi-graphql) as our example, since it offers mutations.

```tsx title="pages/CreateReview.tsx"
import { useController } from '@data-client/react';
import { GQLEndpoint, GQLEntity } from '@data-client/graphql';

const gql = new GQLEndpoint(
  'https://swapi-graphql.netlify.app/graphql',
);

class Review extends GQLEntity {
  readonly stars: number = 0;
  readonly commentary: string = '';
}

const createReview = gql.mutation(
  (v: {
    ep: string;
    review: { stars: number; commentary: string };
  }) => `mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
    createReview(episode: $ep, review: $review) {
      stars
      commentary
    }
  }`,
  { createReview: Review },
);

export default function NewReviewForm() {
  const ctrl = useController();
  return (
    <Form onSubmit={variables => ctrl.fetch(createReview, variables)}>
      <FormField name="ep" />
      <FormField name="review" type="compound" />
    </Form>
  );
}
```

The first argument to GQLEndpoint.query or GQLEndpoint.mutate is either the query string
_or_ a function that returns the query string. The main value of using the latter is enforcing
the function argument types.

## Mixing with REST Demo

Example app: [github-app](https://github.com/reactive/data-client/tree/master/examples/github-app) ([`src/pages/ProfileDetail/UserRepos.tsx`](https://github.com/reactive/data-client/blob/master/examples/github-app/src/pages/ProfileDetail/UserRepos.tsx), [`src/resources/Repository.tsx`](https://github.com/reactive/data-client/blob/master/examples/github-app/src/resources/Repository.tsx))
