This document provides all of the API documentation for reason-urql.
reason-urql comes with a set of custom hooks to use in your ReasonReact components, including useQuery, useMutation, and useSubscription. These are fully type safe and will correctly infer the type of your GraphQL response if using graphql_ppx.
Often, you'll want to fully open the Hooks module when using any of the custom hooks – this brings in some necessary types that will assist with pattern matching and type inference around the responses from your GraphQL API.
useQuery allows you to execute a GraphQL query.
| Argument | Type | Description |
|---|---|---|
request |
UrqlTypes.request |
The graphql_ppx request representing your query. Generated by calling .make() on the graphql_ppx module. If you're not using graphql_ppx, pass a Js.t of the following shape: {. "query": string, "variables": Js.Json.t, "parse": Js.Json.t => 'response } |
requestPolicy |
option(UrqlTypes.requestPolicy) |
Optional. The request policy to use to execute the query. Defaults to "cache-first". |
pause |
option(bool) |
A boolean flag instructing useQuery to pause execution of the subsequent query operation. |
useQuery returns a tuple containing the result of executing your GraphQL query as a record, response, and a function for re-executing the query imperatively, executeQuery.
| Return Value | Type | Description |
|---|---|---|
result |
UrqlTypes.hookResponse('response) |
A record containing fields for fetching, data, error, and response. response is a variant containing constructors for Data, Error, Fetching and NotFound. Useful for pattern matching to render conditional UI. |
executeQuery |
option(Client.Types.partialOperationContext) => unit |
A function for imperatively re-executing the query. Accepts a partialOperationContext for altering the request. |
open ReasonUrql;
open Hooks;
module GetPokemon = [%graphql
{|
query pokemon($name: String!) {
pokemon(name: $name) {
name
classification
image
}
}
|}
];
[@react.component]
let make = () => {
let request = GetPokemon.make(~name="Cubone", ());
let ({response}, executeQuery) = useQuery(~request, ());
switch (response) {
| Fetching => <div> "Loading"->React.string </div>
| Data(d) =>
<div>
<img src=d##pokemon##image>
<span> d##pokemon##name </span>
<span> d##pokemon##classification </span>
<button onClick={_event => executeQuery() |> ignore}>
{js|Refetch data for $d##pokemon##name|js} -> React.string
</button>
</div>
| Error(_e) => <div> "Error"->React.string </div>
| NotFound => <div> "NotFound"->React.string </div>
}
}Check out examples/2-query to see an example of using the useQuery hook.
useMutation allows you to execute a GraphQL mutation via the returned executeMutation function.
| Argument | Type | Description |
|---|---|---|
request |
UrqlTypes.request |
The graphql_ppx request representing your mutation. Generated by calling .make() on the graphql_ppx module. If you're not using graphql_ppx, pass a Js.t of the following shape: {. "query": string, "variables": Js.Json.t, "parse": Js.Json.t => 'response } |
useMutation returns a tuple containing the result of executing your GraphQL mutation as a record, result, and a function for executing the mutation imperatively, executeMutation. By default, useMutation does not execute your mutation when your component renders – rather, it is up to you to call executeMutation when you want to by attaching it to on an event handler or running it inside of an effect.
| Return Value | Type | Description |
|---|---|---|
result |
UrqlTypes.hookResponse('response) |
A record containing fields for fetching, data, error, and response. response is a variant containing constructors for Data, Error, Fetching and NotFound. Useful for pattern matching to render conditional UI. |
executeMutation |
unit => Js.Promise.t(Client.Types.operationResult) |
A function for imperatively executing the mutation. |
open ReasonUrql;
open Hooks;
module LikeDog = [%graphql
{|
mutation likeDog($key: ID!) {
likeDog(key: $key) {
likes
}
}
|}
];
[@react.component]
let make = (~id) => {
let request = Mutations.LikeDog.make(~key=id, ());
let (_, executeMutation) = useMutation(~request);
<button onClick={_e => executeMutation() |> ignore}>
"Like This Dog!"->React.string
</button>
}Check out examples/3-mutation to see an example of using the useMutation hook.
useSubscription allows you to execute a GraphQL subscription. You can accumulate the results of executing subscriptions by passing a handler function to useSubscription.
If using the useSubscription hook, be sure your client is configured with the subscriptionExchange.
| Argument | Type | Description |
|---|---|---|
request |
UrqlTypes.request |
The graphql_ppx request representing your subscription. Generated by calling .make() on the graphql_ppx module. If you're not using graphql_ppx, pass a Js.t of the following shape: {. "query": string, "variables": Js.Json.t, "parse": Js.Json.t => 'response } |
handler |
Hooks.handler |
Optional. A variant type to allow for proper type inference of accumulated A handler function allows you to accumulate subscription responses in the data field of the returned state record. |
useSubscription returns the result of executing your GraphQL subscription as a record of type, result.
| Return Value | Type | Description |
|---|---|---|
result |
UrqlTypes.hookResponse('response) |
A record containing fields for fetching, data, error, and response. response is a variant containing constructors for Data, Error, Fetching and NotFound. Useful for pattern matching to render conditional UI. |
open ReasonUrql;
open Hooks;
module SubscribeRandomInt = [%graphql
{|
subscription subscribeNumbers {
newNumber @bsDecoder(fn: "string_of_int")
}
|}
];
/* Accumulate subscriptions as new values arrive from your GraphQL endpoint. */
let handler = (prevSubscriptions, subscription) => {
switch (prevSubscriptions) {
| Some(subs) => Array.append(subs, [|subscription|])
| None => [|subscription|]
};
};
[@react.component]
let make = () => {
let {response} =
useSubscription(
~request=SubscribeRandomInt.make(),
~handler=Handler(handler)
);
switch (response) {
| Fetching => <div> "Loading"->React.string </div>
| Data(d) =>
d
|> Array.map(
(datum) =>
<circle
cx=datum##newNumber
cy=datum##newNumber
stroke="#222"
fill="none"
r="5"
/>,
)
|> React.array
| Error(_e) => <div> "Error"->React.string </div>
| NotFound => <div> "NotFound"->React.string </div>
}
}Check out examples/5-subscription to see an example of using the useSubscription hook.
The Query allows you to query your GraphQL API and render UI with the returned data. It is a render prop style alternative to the useQuery hook.
| Prop | Type | Description |
|---|---|---|
request |
UrqlTypes.request |
The graphql_ppx request representing your query. Generated by calling .make() on the graphql_ppx module. If you're not using graphql_ppx, pass a Js.t of the following shape: {. "query": string, "variables": Js.Json.t, "parse": Js.Json.t => 'response } |
requestPolicy |
option(UrqlTypes.requestPolicy) |
The request policy to use to execute the query. Defaults to "cache-first". |
pause |
option(bool) |
A boolean flag instructing Query to pause execution of the subsequent query operation. |
| Prop | Type | Description |
|---|---|---|
fetching |
bool |
A boolean flag to indicate if the request is currently executing. |
data |
'response |
The data returned by your GraphQL API. |
error |
Error.combinedError |
The error(s), if any, returned by the GraphQL operation. |
executeQuery |
option(Js.Json.t) => Js.Promise.t(Client.Types.operationResult) |
A callback to imperatively re-execute the query operation. Accepts a partial operation context to modify execution of the query. |
response |
UrqlTypes.response('response) |
A variant containing constructors for Data, Error, Fetching and NotFound. Useful for pattern matching to render conditional UI. |
open ReasonUrql;
module GetPokemon = [%graphql
{|
query pokemon($name: String!) {
pokemon(name: $name) {
name
classification
image
}
}
|}
];
let request = GetPokemon.make(~name="Abra", ());
[@react.component]
let make = () => {
<Query request>
{...({ response, executeQuery }) => {
switch (response) {
| Fetching => <div> "Loading"->React.string </div>
| Data(d) =>
<div>
<img src=d##pokemon##image>
<span> d##pokemon##name </span>
<span> d##pokemon##classification </span>
<button onClick={_event => executeQuery() |> ignore}>
{js|Refetch data for $d##pokemon##name|js} -> React.string
</button>
</div>
| Error(_e) => <div> "Error"->React.string </div>
| NotFound => <div> "NotFound"->React.string </div>
}
}}
</Query>
}Check out examples/2-query to see an example of using the Query component.
The Mutation component allows you to imperatively execute mutations. Use this component to pass the executeMutation function to a React element.
| Prop | Type | Description |
|---|---|---|
request |
UrqlTypes.request |
The graphql_ppx request representing your mutation. Generated by calling .make() on the graphql_ppx module. If you're not using graphql_ppx, pass a Js.t of the following shape: {. "query": string, "variables": Js.Json.t, "parse": Js.Json.t => 'response } |
| Prop | Type | Description |
|---|---|---|
fetching |
bool |
A boolean flag to indicate if the request is currently executing. |
data |
'response |
The data returned by your GraphQL API. |
error |
CombinedError.combinedError |
The error(s), if any, returned by the GraphQL operation. |
executeMutation |
option(Js.Json.t) => Js.Promise.t(Client.Types.operationResult) |
A callback to manually execute the mutation operation. Useful to imperatively execute the mutation. Accepts variables required to execute the mutation. |
response |
UrqlTypes.response('response) |
A variant containing constructors for Data, Error, Fetching and NotFound. Useful for pattern matching to render conditional UI. |
open ReasonUrql;
module LikeDog = [%graphql
{|
mutation likeDog($key: ID!) {
likeDog(key: $key) {
likes
}
}
|}
];
let request = GetAllDogs.make(~key="VmeRTX7j-", ());
[@react.component]
let make = () => {
<Mutation request>
{...({ executeMutation }) => {
<button onClick={_e => executeMutation() |> ignore}>
"Click Me to Execute the Mutation" -> React.string
</button>
}}
</Mutation>
}Check out examples/3-mutation to see an example of using the Mutation component.
The Subscription component allows you to subscribe to GraphQL subscriptions emitted by your API. Use this component to render UI with realtime data. If you want to accumulate subscriptions, use the SubscriptionWithHandler component.
If using the Subscription component, be sure your client is configured with the subscriptionExchange.
| Prop | Type | Description |
|---|---|---|
request |
UrqlTypes.request |
The graphql_ppx request representing your subscription. Generated by calling .make() on the graphql_ppx module. If you're not using graphql_ppx, pass a Js.t of the following shape: {. "query": string, "variables": Js.Json.t, "parse": Js.Json.t => 'response } |
| Prop | Type | Description |
|---|---|---|
fetching |
bool |
A boolean flag to indicate if the request is currently executing. |
data |
'response |
The data returned by your GraphQL API. |
error |
Error.combinedError |
The error(s), if any, returned by the GraphQL operation. |
response |
UrqlTypes.response('response) |
A variant containing constructors for Data, Error, Fetching and NotFound. Useful for pattern matching to render conditional UI. |
open ReasonUrql;
module SubscribeMessages = [%graphql
{|
subscription subscribeMessages {
newMessage {
id
message
}
}
|}
];
let request = SubscribeMessages.make();
[@react.component]
let make = () => {
<Subscription request>
...{({response}) =>
switch (response) {
| Fetching => <div> "Loading"->React.string </div>
| Data(d) =>
<div>
{js|The most recent message is: $d##newMessage##message|js}->React.string
</div>
| Error(_e) => <div> "Error"->React.string </div>
| NotFound => <div> "NotFound"->React.string </div>
}
}
</Subscription>
};The SubscriptionWithHandler component is identitcal to the Subscription component, except that it allows you to pass an additional handler prop to accumulate subscriptions as they are returned from your GraphQL API.
If using the SubscriptionWithHandler component, be sure your client is configured with the subscriptionExchange.
| Prop | Type | Description |
|---|---|---|
request |
UrqlTypes.request |
The graphql_ppx request representing your subscription. Generated by calling .make() on the graphql_ppx module. If you're not using graphql_ppx, pass a Js.t of the following shape: {. "query": string, "variables": Js.Json.t, "parse": Js.Json.t => 'response } |
handler |
(option('acc), 'response) => 'acc) |
A function that receives arguments for previous subscriptions and the current subscription and accumulates subscriptions. |
| Prop | Type | Description |
|---|---|---|
fetching |
bool |
A boolean flag to indicate if the request is currently executing. |
data |
'response |
The data returned by your GraphQL API. |
error |
Error.combinedError |
The error(s), if any, returned by the GraphQL operation. |
response |
UrqlTypes.response('response) |
A variant containing constructors for Data, Error, Fetching and NotFound. Useful for pattern matching to render conditional UI. |
open ReasonUrql;
module SubscribeRandomInt = [%graphql
{|
subscription subscribeNumbers {
newNumber @bsDecoder(fn: "string_of_int")
}
|}
];
let request = SubscribeRandomInt.make();
/* Accumulate subscriptions as new values arrive from your GraphQL endpoint. */
let handler = (prevSubscriptions, subscription) => {
switch (prevSubscriptions) {
| Some(subs) => Array.append(subs, [|subscription|])
| None => [|subscription|]
};
};
[@react.component]
let make = () => {
<SubscriptionWithHandler request handler>
...{({ response }) =>
switch (response) {
| Fetching => <div> "Loading"->React.string </div>
| Data(d) =>
d
|> Array.map(
(datum) =>
<circle
cx=datum##newNumber
cy=datum##newNumber
stroke="#222"
fill="none"
r="5"
/>,
)
|> React.array
| Error(_e) => <div> "Error"->React.string </div>
| NotFound => <div> "NotFound"->React.string </div>
}
}
</SubscriptionWithHandler>
}The Provider's responsibility is to pass the urql client instance down to Query, Mutation, and Subscription components through context. Wrap the root of your application with Provider.
| Prop | Type | Description |
|---|---|---|
client |
Client.t |
The urql client instance. |
open ReasonUrql;
let client = Client.make(~url="https://localhost:3000/graphql", ());
ReactDOMRe.renderToElementWithId(<Provider client><App /></Provider>, "root");The client is the central orchestrator of reason-urql, and is responsible for executing queries, mutations, and subscriptions passed to useQuery, useMutation, and useSubscription hooks or to the Query, Mutation, Subscription, and SubscriptionWithHandler components.
To create a client, simply call the make function from the Client module.
open ReasonUrql;
let client = Client.make(~url="https://localhost:3000/graphql", ());The client optionally accepts a fetchOptions argument for passing additional properties to fetch requests (i.e. authorization headers) and an exchanges argument for customizing the exchanges that operations are passed through.
Instantiate an urql client instance. By default, urql will execute requests with the following already in place if no configurations for fetchOptions or exchanges are provided:
- A
content-typeheader ofapplication/json. POSTas the HTTP method used to execute all operations.- The default exchanges.
| Argument | Type | Description |
|---|---|---|
url |
string |
The url of your GraphQL API. |
fetchOptions |
option(Client.fetchOptions)=? |
A variant type representing optional fetch options to be used by your client. You can pass your fetchOptions as a plain Fetch.requestInit by wrapping it in Client.FetchOpts, or instantiate it dynamically in a function wrapped by Client.FetchFn. |
exchanges |
option(array(Exchanges.exchange))=? |
The array of exchanges to be used by your client. |
Returns an instance of the urql client of type Client.t. This can then be passed to your root Provider component.
Create a client with just a url.
open ReasonUrql;
let client = Client.make(~url="https://localhost:3000/graphql", ());Create a client with fetchOptions.
open ReasonUrql;
let fetchOptions =
Client.FetchOpts(Fetch.RequestInit.make(
~method_=Post,
~headers=Fetch.HeadersInit.make({"Content-Type": "application/json"}),
(),
));
let client = Client.make(~url="https://localhost:3000/graphql", ~fetchOptions, ());Create a client with exchanges.
open ReasonUrql;
let client = Client.make(
~url="https://localhost:3000/graphql",
~exchanges=[|
Exchanges.debugExchange,
Exchanges.dedupExchange,
Exchanges.cacheExchange,
Exchanges.fetchExchange
|],
()
);Imperatively execute a GraphQL query operation.
| Argument | Type | Description |
|---|---|---|
client |
Client.t |
The urql client instance. |
request |
UrqlTypes.request |
The graphql_ppx request representing your query. Generated by calling .make() on the graphql_ppx module. If you're not using graphql_ppx, pass a Js.t of the following shape: {. "query": string, "variables": Js.Json.t, "parse": Js.Json.t => 'response } |
opts |
option(Client.Types.partialOperationContext)=? |
Additional options to pass to the operation. |
open ReasonUrql;
let client = Client.make(~url="https://localhost:3000/graphql", ());
module GetAllDogs = [%graphql
{|
query dogs {
dogs {
name
breed
likes
}
}
|}
];
Client.executeQuery(~client, ~request=GetAllDogs.make(), ())
|> Wonka.subscribe((. result) => {
switch(result.response) {
| Data(d) => /* Access data returned from executing the request. */
| Error(e) => /* Access any errors returned from executing the request. */
| NotFound => /* Fallback if neither Data nor Error return information. */
}
});Execute a GraphQL mutation operation.
| Argument | Type | Description |
|---|---|---|
client |
Client.t |
The urql client instance. |
request |
UrqlTypes.request |
The graphql_ppx request representing your mutation. Generated by calling .make() on the graphql_ppx module. If you're not using graphql_ppx, pass a Js.t of the following shape: {. "query": string, "variables": Js.Json.t, "parse": Js.Json.t => 'response } |
opts |
option(Types.partialOperationContext)=? |
Optional. Additional options to pass to the operation. |
open ReasonUrql;
let client = Client.make(~url="https://localhost:3000/graphql", ());
module LikeDog = [%graphql
{|
mutation likeDog($key: ID!) {
likeDog(key: $key) {
name
breed
likes
}
}
|}
];
let mutation = LikeDog.make(~key="VmeRTX7j-", ());
Client.executeMutation(~client, ~request, ())
|> Wonka.subscribe((. result) => {
switch(result.response) {
| Data(d) => /* Access data returned from executing the request. */
| Error(e) => /* Access any errors returned from executing the request. */
| NotFound => /* Fallback if neither Data nor Error return information. */
}
});Execute a GraphQL subscription operation. If using the executeSubscription method, be sure your client is configured with the subscriptionExchange.
| Argument | Type | Description |
|---|---|---|
client |
Client.t |
The urql client instance. |
request |
Types.graphqlRequest |
The graphql_ppx request representing your subscription. Generated by calling .make() on the graphql_ppx module. If you're not using graphql_ppx, pass a Js.t of the following shape: {. "query": string, "variables": Js.Json.t, "parse": Js.Json.t => 'response } |
opts |
option(Types.partialOperationContext)=? |
Optional. Additional options to pass to the operation. |
open ReasonUrql;'
module SubscribeMessages = [%graphql
{|
subscription subscribeMessages {
newMessage {
id
message
}
}
|}
];
let request = SubscribeMessages.make();
Client.executeSubscription(~client, ~request, ())
|> Wonka.subscribe((. result) => {
switch(result.response) {
| Data(d) => /* Access data returned from executing the request. */
| Error(e) => /* Access any errors returned from executing the request. */
| NotFound => /* Fallback if neither Data nor Error return information. */
}
});Exchanges are the mechanism by which urql modifies requests before they are sent to your GraphQL API and alters responses as they are received. If you want to add some additional functionality to your GraphQL operations, this is a great place to do that. The following exchanges are provided out of the box with urql.
The cacheExchange provides basic caching support for your GraphQL operations. It is of type Exchanges.exchange.
The dedupExchange will deduplicate pending operations waiting for a response. For example, if a user attempts to execute the same query by clicking a button in rapid succession, the dedupExchange will filter these events to a single request. It is of type Exchanges.exchange.
The fetchExchange is responsible for actually sending your request to your GraphQL API and handling the response. It is of type Exchanges.exchange.
The above three exchanges make up urql's defaultExchanges. When you create a client in reason-urql these exchanges are already applied by default. If you specify an exchanges array, be sure to include the specific exchanges you need. You almost always want the defaultExchanges, so make sure to include them using Array.concat or Array.append.
open ReasonUrql;
let client = Client.make(~url="https://mygraphql.com/api", ~exchanges=Array.append([|myCustomExchange|], Exchanges.defaultExchanges)The debugExchange is useful for tracking how operations are passing through the exchanges pipeline. It simply logs all incoming and outgoing operations to the console. Be sure to remove this in production! It is of type Exchanges.exchange.
The subscriptionExchange should be used in the event that you intend to support GraphQL subscriptions in your application through use of useSubscription, the Subscription or SubscriptionWithHandler components, or the client's executeSubscription method.
In order to use the subscriptionExchange, you'll need to do a little bit of setup. Specifically, you'll need to configure a subscription client of some kind that will handle the websocket connection to your GraphQL API. In examples/5-subscription we have some simple bindings to subscriptions-transport-ws that allow us to use this protocol (which is supported by Apollo). Here's an example of how to set everything up:
/* Create the subscriptionClient using APIs from SubscriptionsTransportWS. */
let subscriptionClient =
SubscriptionsTransportWS.subscriptionClient(
~url="ws://localhost:4001/graphql",
~subscriptionClientConfig=SubscriptionsTransportWS.subscriptionClientConfig(),
);
/* Implement the forwardSubscription function. This tells urql how to handle
incoming operations of operationType 'subscription'. */
let forwardSubscription = operation => subscriptionClient##request(operation);
/* Confirgure options for urql's subscriptionExchange. */
let subscriptionExchangeOpts =
Exchanges.subscriptionExchangeOpts(~forwardSubscription);
/* Create the subscriptionExchange. */
let subscriptionExchange =
Exchanges.subscriptionExchange(subscriptionExchangeOpts);
/* Include the subscriptionExchange in your client's exchanges array. */
let client = Client.make(
~url="https://localhost:3000/graphql",
~exchanges=Array.append(Exchanges.defaultExchanges, [|subscriptionExchange|]),
()
);composeExchanges is a helper function that will compose a single exchange from an array of exchanges. Operations will be run through the provided exchanges in the order that they were provided to composeExchanges, from left to right.
Errors in reason-urql are handed to your components and hooks via a record of type CombinedError.t. The record has the following type:
type combinedError = {
networkError: option(Js.Exn.t),
graphQLErrors: option(array(graphQLError)),
response: option(Fetch.response),
message: string,
};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.
The easiest way to interact with CombinedError is through the message field on the CombinedError.t record. If you just want to display the error message to your user, or operate directly on the error string, simply do the following:
switch (response) {
| Error(e) => <div> e.message->React.string </div>
| _ => <div />
}Dependening on the types of errors you get from your GraphQL API, you may want to do different things. Here's an example showing how to handle networkErrors and graphQLErrors indepedently.
let ({ response }, _) = useQuery(~request, ());
switch (response) {
| Error(e) =>
switch (e) {
| {networkError: Some(ne)} =>
<div>
{ne
->Js.Exn.message
->Belt.Option.getWithDefault("Network error")
->React.string}
</div>
| {graphQLErrors: Some(gqle)} =>
<div>
{gqle
|> Array.to_list
|> List.map(e => {
let msg =
Belt.Option.getWithDefault(
Js.Nullable.toOption(CombinedError.messageGet(e)),
"GraphQL error",
);
"[GraphQLError: " ++ msg ++ "]";
})
|> String.concat(", ")
|> React.string}
</div>
| _ => <div> "Unknown error."->React.string </div>
}
}
| _ => <div />
}