Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve API docs, including instructions on graphql_ppx_re. #125

Merged
merged 3 commits into from
Nov 9, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, this is quite nice. I didn't know that it worked like this 🤔
How do I make sure I'm using this feature?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has to do with urql's observable system, which is powered by wonka. Essentially, every operation that passes through the Client can have subscribers, and this is particularly important in the case of queries that are dependent on mutation results. When a mutation is executed, urql will aggressively invalidate the cache for any entity that has a shared __typename with the mutation. This then notifies subscribers to those cache entries (really any query that requests data with the same __typename) that they should update: https://github.com/FormidableLabs/urql#document-caching. The first example project in this repo shows this pretty well – if you hit the button to fire executeMutation, the query will be notified that the cache was invalidated and pull anew from it, causing a re-render. Hope that makes sense! You don't need to do anything to opt into this, it should just happen for you.


`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 the graphql_ppx module, passing variables as labeled arguments. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you mention graphql_ppx but in other places you removed the mention

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, fixed here: f67eb9f

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