Reason-apollo
React-apollo with Reason
Install and setup
Install
yarn add reason-apollo
# Add graphql_ppx
yarn add --dev graphql_ppx
# Add JS dependencies
yarn add react-apollo apollo-client apollo-cache-inmemory apollo-link apollo-link-context apollo-link-error apollo-link-http graphql graphql-tag
bsconfig
Add reason-apollo
to your bs-dependencies
and
graphql_ppx/ppx
to your ppx_flags
bsconfig.json
"bs-dependencies": [
"reason-react",
"reason-apollo"
],
"ppx-flags": [
"graphql_ppx/ppx"
]
send introspection query
This will generate a graphql_schema.json
which will be used to safely type your GraphQL queries/mutations.
yarn send-introspection-query http://my-api.example.com/api
Why reason-apollo?
Watch it's usage in this video:
Usage
Create the Apollo Client
Client.re
/* Create an InMemoryCache */
let inMemoryCache = ApolloInMemoryCache.createInMemoryCache(());
/* Create an HTTP Link */
let httpLink =
ApolloLinks.createHttpLink(~uri="http://localhost:3010/graphql", ());
let instance = ReasonApollo.createApolloClient(
~link=httpLink,
~cache=inMemoryCache,
()
);
ApolloProvider
Index.re
/*
Enhance your application with the `ReasonApollo.Provider`
passing it your client instance
*/
ReactDOMRe.renderToElementWithId(
<ReasonApollo.Provider client=Client.instance>
<App />
</ReasonApollo.Provider>
, "index");
Query
MyComponent.re
/* Create a GraphQL Query by using the graphql_ppx */
module GetPokemon = [%graphql {|
query getPokemon($name: String!){
pokemon(name: $name) {
name
}
}
|}];
module GetPokemonQuery = ReasonApollo.CreateQuery(GetPokemon);
let make = (_children) => {
/* ... */,
render: (_) => {
let pokemonQuery = GetPokemon.make(~name="Pikachu", ());
<GetPokemonQuery variables=pokemonQuery##variables>
...(({result}) => {
switch result {
| Loading => <div> (ReasonReact.string("Loading")) </div>
| Error(error) => <div> (ReasonReact.string(error##message)) </div>
| Data(response) => <div> (ReasonReact.string(response##pokemon##name)) </div>
}
})
</GetPokemonQuery>
}
}
Mutation
MyMutation.re
module AddPokemon = [%graphql {|
mutation addPokemon($name: String!) {
addPokemon(name: $name) {
name
}
}
|}];
module AddPokemonMutation = ReasonApollo.CreateMutation(AddPokemon);
let make = (_children) => {
/* ... */,
render: (_) => {
<AddPokemonMutation>
...((
mutation /* Mutation to call */,
_ /* Result of your mutation */
) => {
let newPokemon = AddPokemon.make(~name="Bob", ());
<div>
<button onClick=((_mouseEvent) => {
mutation(
~variables=newPokemon##variables,
~refetchQueries=[|"getAllPokemons"|],
()
) |> ignore;
})>
(ReasonReact.string("Add Pokemon"))
</button>
</div>
})
</AddPokemonMutation>
}
}
ApolloConsumer
If you simply want to have access to the ApolloClient, you can use the ApolloConsumer
<ApolloConsumer>
...((apolloClient) => {
/* We have access to the client! */
})
</ApolloConsumer>
Tips and Tricks
Use @bsRecord
on response object
The @bsRecord
modifier is an extension of the graphql syntax for BuckleScipt/ReasonML. It allows you to convert a reason object to a reason record and reap the benefits of pattern matching. For example, let's say I have a nested object of options. I would have to do something like this:
switch response##object {
| Some(object) => {
switch object##nestedValue {
| Some(nestedValue) => nestedValue
| None => ""
}
}
| None => ""
}
Kind of funky, huh? Let's modify the response and convert it to a reason record.
type object = {
nestedValue: option(string)
}
module GetObject = [%graphql {|
object @bsRecord {
nestedValue
}
|}
];
This time we can pattern match more precisely.
switch response##object {
| Some({ nestedValue: Some(value) }) => value
| Some({ nestedValue: None }) => ""
| None => ""
}
Use an alias for irregular field names
You might find yourself consuming an API with field names like Field
. Currently, reason object field names are required to be camel case. Therefore if you have a request like this:
{
Object {
id
title
}
}
You will attempt to access the response object but it will throw an error:
response##Object /* Does not work :( */
Instead, use an alias
to modify the response:
{
object: Object {
id
title
}
}
Then you can access the object like this:
response##object
FAQ
I've added the schema file, but my build fails saying it couldn't be found?
In some cases, it seems like there are some differences between the provided send-introspection-query
and output from tools you might be using to download the schema (such as apollo-codegen
or graphql-cli
).
If your build is failing, please make sure to try with the provided script. In your project root, run:
$ yarn send-introspection-query <url>