Skip to content

Commit

Permalink
Improve API docs, including instructions on graphql_ppx_re. (#125)
Browse files Browse the repository at this point in the history
* Improve API docs, including instructions on graphql_ppx_re. Use CombinedError.t.

* Additional edits after reviewing diff.

* Remove last references to graphql_ppx.
  • Loading branch information
parkerziegler authored Nov 9, 2019
1 parent 1a95992 commit a28d9c3
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 173 deletions.
25 changes: 18 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,25 +30,32 @@ Reason bindings for Formidable's Universal React Query Library, [`urql`](https:/
yarn add reason-urql bs-fetch
```

#### 2. Add `graphql_ppx_re`.
#### 2. Add `graphql_ppx_re` or `graphql_ppx`.

This project uses [`graphql_ppx_re`](https://github.com/baransu/graphql_ppx_re) (which is based on [`graphql_ppx`](https://github.com/mhallin/graphql_ppx)) to type check your GraphQL queries, mutations, and subscriptions **at compile time**. You'll need to add it as a devDependency.
To get the most out of compile time type checks for your GraphQL queries, mutations, and subscriptions, we recommend using one of the PPX rewriters available for Reason / OCaml. Currently, there are two options in the community – [`graphql_ppx_re`](https://github.com/baransu/graphql_ppx_re), which is under active maintenance, and [`graphql_ppx`](https://github.com/mhallin/graphql_ppx), which is no longer actively maintained. If using `[email protected]`, you'll have to use `graphql_ppx_re`.

```sh
yarn add @baransu/graphql_ppx_re --dev

# or
yarn add graphql_ppx --dev
```

#### 3. Update `bsconfig.json`.

Add `reason-urql` to your `bs-dependencies` and `graphql_ppx_re` to your `ppx_flags` in `bsconfig.json`.
Add `reason-urql` to your `bs-dependencies` and `graphql_ppx_re` or `graphql_ppx` (depending on which library you're using) to your `ppx_flags` in `bsconfig.json`.

```json
{
"bs-dependencies": ["reason-urql"],
"ppx-flags": ["@baransu/graphql_ppx_re/ppx"]
"ppx-flags": ["@baransu/graphql_ppx_re/ppx"],
// or
"ppx-flags": ["graphql_ppx"]
}
```
If you're using `bs-platform` 6.x, use this value for the `"ppx_flags"` instead:

If you're using `bs-platform` 6.x, you'll need to use `@baransu/graphql_ppx_re/ppx6`:

```json
{
"ppx-flags": ["@baransu/graphql_ppx_re/ppx6"]
Expand All @@ -57,13 +64,17 @@ If you're using `bs-platform` 6.x, use this value for the `"ppx_flags"` instead:

#### 4. Send an introspection query to your API.

Finally, you'll need to send an introspection query to your GraphQl API. This allows `@baransu/graphql_ppx_re` to generate a `graphql_schema.json` at the root of your project that it can use to type check your queries. **You should check this file into version control** and keep it updated as your API changes. To do this:
Finally, you'll need to send an introspection query to your GraphQl API, using a tool like [`graphql-cli`](https://github.com/Urigo/graphql-cli/). You should generate a file called `graphql_schema.json` at the root of your project that your chosen PPX preprocessor can use to type check your queries. **You should check this file into version control** and keep it updated as your API changes.

If using `grapqhl_ppx_re`, follow the instructions [here](https://github.com/baransu/graphql_ppx_re#usage).

If using `graphql_ppx`, you'll already have a little utility to help you generate the `graphql_schema.json` file:

```sh
yarn send-introspection-query <your_graphql_endpoint>
```

Simply re-run this script at anytime to regenerate the `graphql_schema.json` file. See the [docs for `graphql_ppx_re`](https://github.com/baransu/graphql_ppx_re#usage) for more assistance.
Simply re-run this script at anytime to regenerate the `graphql_schema.json` file according to your latest backend schema. See the [docs for `graphql_ppx_re`](https://github.com/baransu/graphql_ppx_re#usage) and the docs for [`grapqhl_ppx`](https://github.com/mhallin/graphql_ppx) for more assistance.

### Older Versions

Expand Down
2 changes: 2 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,8 @@ type combinedError = {
response: option(Fetch.response),
message: string,
};
type t = combinedError;
```

In this case, `networkError` returns the original JavaScript error thrown if a network error was encountered. `graphQLErrors` represent an `array` of errors of type `graphQLError`. These represent the errors encountered in the validation or execution stages of interacting with your GraphQL API. `response` is the raw `response` object returned by `fetch`. `message` is a stringified version of either the `networkError` or the `graphQLErrors``networkError` will take precedence.
Expand Down
89 changes: 84 additions & 5 deletions docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ open ReasonUrql;
/* Be sure to open the Hooks module to bring necessary types into scope. */
open Hooks;
/* Create a graphql_ppx module with your GraphQL query. */
/* Create a module with your GraphQL query. */
module DogsQuery = [%graphql
{|
query dogs {
Expand All @@ -54,11 +54,11 @@ module DogsQuery = [%graphql
[@react.component]
let make = () => {
/* Build your request by calling .make on the graphql_ppx module. */
/* Build your request by calling .make on your query. */
let request = DogsQuery.make();
/* Pass the request to useQuery. */
let ({ response }, executeQuery) = useQuery(~request, ())
let ({ response }, executeQuery) = useQuery(~request, ());
/* Pattern match on the response variant.
This variant has constructors for Fetching, Data(d), Error(e), and NotFound. */
Expand All @@ -83,8 +83,87 @@ let make = () => {
}
```

Sweet 😎! We've executed a query with our `useQuery` hook. Notice that we didn't have to write _any_ types to get 💯% type inference and type saftey on the response. We use the `graphql_ppx` module you pass to `useQuery` to ensure that you're using the data returned by your query in a fully type safe way.
Sweet 😎! We've executed a query with our `useQuery` hook. Notice that we didn't have to write _any_ types to get 💯% type inference and type safety on the response. We use type information included in the query module you pass to `useQuery` to ensure that you're using the data returned by your query in a fully safe way.

## What's Next?
## Can I See an Example?

Check out the example in `examples/2-query` to see a more involved example of using `useQuery`, in addition to `reason-urql`'s `Query` component.

## Writing Your First Mutation

Awesome, so we've written our first query with `reason-urql`. What about mutations?

Fortunately, writing mutations in `reason-urql` is just as easy as writing queries – just use the `useMutation` hook.

```reason
open ReasonUrql;
/* Be sure to open the Hooks module to bring necessary types into scope. */
open Hooks;
/* Create a module with your GraphQL mutation. */
module LikeDogMutation = [%graphql
{|
mutation likeDog($key: ID!) {
likeDog(key: $key) {
likes
name
breed
}
}
|}
];
[@react.component]
let make = (~key: string) => {
/* Build your request by calling .make on your mutation, passing variables as labeled arguments. */
let request = LikeDogMutation.make(~key, ());
/* Pass the request to useMutation. */
let (_, executeMutation) = useMutation(~request, ());
<button onClick=(_e) => executeMutation()>
"Execute the Mutation (and Reward a Good Dog)"->React.string
</button>
}
```

Great – we've successfully executed a mutation to like a dog! Existing queries that reference the mutated data will be notified that data has changed, meaning you don't need to think at all about refetching your data – it's all updated for you by `reason-urql`.

`useMutation` returns a two-dimensional tuple, containing `(result, executeMutation)`. `result` contains the `response` variant, which allows you to pattern match on the API response from your mutation. For example, if you wanted to show different UI when your mutation was `Fetching`, or if there was an `Error(e)` you can do something like the following:

```reason
[@react.component]
let make = (~key: string) => {
/* Build your request by calling .make on your mutation, passing variables as labeled arguments. */
let request = LikeDogMutation.make(~key, ());
/* Pass the request to useMutation. */
let ({ response }, executeMutation) = useMutation(~request, ());
let button = React.useMemo1(() =>
<button onClick=(_e) => executeMutation()>
"Execute the Mutation (and Reward a Good Dog)"->React.string
</button>,
[|executeMutation|]
);
switch (response) {
| Fetching =>
/* If the mutation is still executing, display a LoadingSpinner on top of our button. */
<LoadingSpinner>
button
<LoadingSpinner>
| Data(_d) => button
/* If an error is encountered when executing the mutation, show some error UI to the user. */
| Error(e) =>
<Error message=e.message>
button
</Error>
| _ => React.null
}
}
```

## Can I See an Example?

Check out the example in `examples/3-mutation` to see a more involved example of using `useMutation`, in addition to `reason-urql`'s `Mutation` component.
4 changes: 2 additions & 2 deletions src/ReasonUrql.re
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Client = {
type clientResponse('response) =
UrqlClient.ClientTypes.clientResponse('response) = {
data: option('response),
error: option(UrqlCombinedError.combinedError),
error: option(UrqlCombinedError.t),
response: UrqlClient.ClientTypes.response('response),
};

Expand Down Expand Up @@ -34,7 +34,7 @@ module Hooks = {
UrqlTypes.hookResponse('ret) = {
fetching: bool,
data: option('ret),
error: option(UrqlCombinedError.combinedError),
error: option(UrqlCombinedError.t),
response: UrqlTypes.response('ret),
};
include UrqlUseMutation;
Expand Down
4 changes: 2 additions & 2 deletions src/UrqlClient.re
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,12 @@ module ClientTypes = {
it has been converted by urqlClientResponseToReason. */
type response('response) =
| Data('response)
| Error(UrqlCombinedError.combinedError)
| Error(UrqlCombinedError.t)
| NotFound;

type clientResponse('response) = {
data: option('response),
error: option(UrqlCombinedError.combinedError),
error: option(UrqlCombinedError.t),
response: response('response),
};
};
Expand Down
4 changes: 2 additions & 2 deletions src/UrqlTypes.re
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ type request('response) = {
type response('response) =
| Fetching
| Data('response)
| Error(UrqlCombinedError.combinedError)
| Error(UrqlCombinedError.t)
| NotFound;

type hookResponse('ret) = {
fetching: bool,
data: option('ret),
error: option(UrqlCombinedError.combinedError),
error: option(UrqlCombinedError.t),
response: response('ret),
};

Expand Down
2 changes: 1 addition & 1 deletion src/components/UrqlMutation.re
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type mutationRenderPropsJs = {
type mutationRenderProps('response) = {
fetching: bool,
data: option('response),
error: option(UrqlCombinedError.combinedError),
error: option(UrqlCombinedError.t),
executeMutation:
unit => Js.Promise.t(UrqlClient.ClientTypes.operationResult),
response: UrqlTypes.response('response),
Expand Down
2 changes: 1 addition & 1 deletion src/components/UrqlQuery.re
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type queryRenderPropsJs = {
type queryRenderProps('response) = {
fetching: bool,
data: option('response),
error: option(UrqlCombinedError.combinedError),
error: option(UrqlCombinedError.t),
executeQuery:
option(Js.Json.t) => Js.Promise.t(UrqlClient.ClientTypes.operationResult),
response: UrqlTypes.response('response),
Expand Down
2 changes: 1 addition & 1 deletion src/components/UrqlSubscription.re
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type subscriptionRenderPropsJs('ret) = {
type subscriptionRenderProps('ret) = {
fetching: bool,
data: option('ret),
error: option(UrqlCombinedError.combinedError),
error: option(UrqlCombinedError.t),
response: UrqlTypes.response('ret),
};

Expand Down
Loading

0 comments on commit a28d9c3

Please sign in to comment.