We use cookies and other tracking technologies to improve your browsing experience on our site, analyze site traffic, and understand where our audience is coming from. To find out more, please read our privacy policy.

By choosing 'I Accept', you consent to our use of cookies and other tracking technologies.

We use cookies and other tracking technologies to improve your browsing experience on our site, analyze site traffic, and understand where our audience is coming from. To find out more, please read our privacy policy.

By choosing 'I Accept', you consent to our use of cookies and other tracking technologies. Less

We use cookies and other tracking technologies... More

Login or register
to apply for this job!

Login or register to start contributing with an article!

Login or register
to see more jobs from this company!

Login or register
to boost this post!

Show some love to the author of this blog by giving their post some rocket fuel 🚀.

Login or register to search for your ideal job!

Login or register to start working on this issue!

Engineers who find a new job through JavaScript Works average a 15% increase in salary 🚀

Blog hero image

When Reason meets GraphQL

Grégoire Vda 13 December, 2017 | 3 min read

In this post I’ll show you how to integrate graphQL into a Reason todo list app made with ReasonReact library, thanks to Apollo. I’ll try to be concise and show you working code. The code I show comes from this repo. If you’re new to Reason, go checkout their docs and if you’re looking for more details on how to create a simple todo list, checkout this tutorial. Here we go…

Our root.re is our starting point to render the App component in the DOM element with has the “index” id. (which you can find in the index.html file)

ReactDOMRe.renderToElementWithId <App /> "index";

Our app.re is a stateless component. It enhances the application with ApolloProvider. We pass him a graphQL client and it will make it available for us in any component through the context.

let component = ReasonReact.statelessComponent "App";

let make _children => {
  ...component, /* Extend default */
  render: fun _self => {
    <ApolloProvider client=ApolloClient.instance> 
      <DataTodoContainer /> 
    </ApolloProvider>
  }
};

Here’s our apolloClient.re where the ApolloClient is created :

/* Js typed Object */
type networkInterface = Js.t {. uri : string}; 
/* Js typed Object, holding the above networkInterface */
type apolloClient = Js.t {. networkInterface : string};
/* Import ApolloClient from 'react-apollo'. [@@bs.new] is BuckleScript syntax to tell it needs a new instance. We name it apollo_client and type it as apolloClient (declared above) */
external apollo_client : apolloClient => string =
  "ApolloClient" [@@bs.new] [@@bs.module "react-apollo"];
/* Import createNetworkInterface from 'react-apollo' */
external create_network_interface : networkInterface => string =
  "createNetworkInterface" [@@bs.module "react-apollo"];
/* Create the networkInterface. Types are inferred */
let networkInterfaceInstance = {"uri": "http://localhost:3010/graphql"};
/* Create the apolloClientConfig. Types are again inferred */
let apolloClientConfig = {"networkInterface": create_network_interface networkInterfaceInstance};
/* Thanks to the [@@bs.new] it will create a new ApolloClient instance with the configuration passed as parameter. The instance is available in other files through ApolloClient.instance */
let instance = apollo_client apolloClientConfig;

In the dataTodoContainer.re, we create our queries and mutations and enhance the todoContainer.re with them.

/* we give the query (under the hood, it's a string) an opaque type. This way nobody can accidentally use it as a string  */
type query;

/*
annotate the function with [@bs] so that it's statically verified to be fully applied at each callsite. Better perf from fewer curryings. See more info on `[@bs]` in the BS manual
*/
type gql = (string => query) [@bs];
/* We import the default export of 'graphql-tag' */
external gql : gql = "graphql-tag" [@@bs.module];

We need to use the graphql HoC with and without configuration as 2nd parameter. To do so, we assign the graphql import from ‘react-apollo’ to a graphql function, typed to only receive a query. And we assign it to graphqlWithConfig, typed to be called with a query and a configuration parameter.

/* 
HoC signature, takes a ReactClass as param and returns a ReactClass */
type wrapper = (ReasonReact.reactClass => ReasonReact.reactClass) [@bs];
type graphqlConfig;
/* 
Define the configuration parameter as an Object, where only the name is required 
*/
external graphqlConfig :
  name::string =>
  alias::string? =>
  skip::Js.boolean? =>
  skip__func::'skipFunc? =>
  unit =>
  graphqlConfig =
  "" [@@bs.obj];
/* HoC that takes a query as parameter */
type graphql = (query => wrapper) [@bs];
/* HoC that takes a query and a configuration Object as parameter */
type graphqlWithConfig = (query => graphqlConfig => wrapper) [@bs];
/* 
We import graphql from 'react-apollo twice and assign it to a method with a specific signature'
*/
external graphql : graphql = "graphql" [@@bs.module "react-apollo"];
external graphqlWithConfig : graphqlWithConfig = "graphql" [@@bs.module "react-apollo"];

Here’s an example of a query

let todos_query =
  gql {|
  query getAllTodos {
    todos {
      id
      title
      active
    }
  }
|} [@bs];

And a mutation

let add_todo_mutation =
  gql
  {|
  mutation addTodo($title: String!, $active: Boolean!) {
    addTodo(
        title: $title,
        active: $active
    ) {
      id
      title
      active
    }
  }
|}
  [@bs];

We now start chaining our queries and mutations with our HoC’s:

/* 
Call the graphqlWithConfig method with the mutation and the configuration object, with a name 
*/
let addTodoWrapper = graphqlWithConfig add_todo_mutation (graphqlConfig name::"addTodoMutation" ()) [@bs];
/*
The wrapper is called on the TodoContainer to enhance it with new props. (TodoContainer is a simple component coming from another file)
*/
let wrappedAddTodoComponent: ReasonReact.reactClass =
  addTodoWrapper TodoContainer.jsComponent [@bs];

We do the same for the query

let queryWrapper = graphqlWithConfig todos_query (graphqlConfig name::"todosQuery"()) [@bs];

let wrappedQueryTodosComponent: ReasonReact.reactClass =
  queryWrapper wrappedAddTodoComponent [@bs];

We now export this, but the annoying part is that the graphql wrapper expects a ReactJS class, but remember that a ReasonReact class isn’t a ReactJS class, so you need to convert it to JS: Reason → JS

let make children =>
  ReasonReact.wrapJsForReason
    reactClass::wrappedDeleteTodoMutationComponent props::(Js.Obj.empty ()) children;

and then back to Reason: JS → Reason

let jsComponent = ReasonReact.wrapReasonForJs ::component (fun props => {
       make
       addTodoMutation::props##addTodoMutation
       todosQuery::props##todosQuery
       [||];
}

Our component is now fully enhanced with all queries and mutations we need. We can access them in the props of the enhanced component

let make ::todosQuery ::addTodoMutation _children => {
  ...component,
  render: fun _self => {
    <div>
     /* 
      todos are available like so: todosQuery##todos,
      and the mutation can be called with variabled like that
      let mutation = {
           "variables": {
              "title": text,
              "active": Js.true_
          }
      };
      addTodoMutation mutation
      */
</div>
  }
};

Conclusion

Reason integrates really well with GraphQL and the typings Reason offers us has a lot of benefits over any JS library. In the future we will be able to cut out the Reason → JS → JS → Reason and make it even easier.

Come join the Reason community on Discord and contribute to this great tech!


If you’re passionate about Front End development, check out the JavaScript Works job-board here!
@gregoirevda

Originally published on blog.hackages.io

Related Issues

viebel / klipse-clj
viebel / klipse-clj
  • Open
  • 0
  • 0
  • Intermediate
  • Clojure
viebel / klipse
  • Open
  • 0
  • 0
  • Intermediate
  • Clojure
viebel / klipse
  • 1
  • 0
  • Intermediate
  • Clojure
viebel / klipse
  • Open
  • 0
  • 0
  • Intermediate
  • Clojure
  • $80
viebel / klipse
  • Open
  • 0
  • 0
  • Advanced
  • Clojure
  • $80
viebel / klipse
  • Started
  • 0
  • 2
  • Advanced
  • Clojure
  • $180
viebel / klipse
  • Started
  • 0
  • 1
  • Intermediate
  • Clojure
viebel / klipse
  • Started
  • 0
  • 3
  • Intermediate
  • Clojure
  • $80
viebel / klipse
  • 1
  • 0
  • Advanced
  • Clojure
  • $300

Get hired!

Sign up now and apply for roles at companies that interest you.

Engineers who find a new job through JavaScript Works average a 15% increase in salary.

Start with GithubStart with Stack OverflowStart with Email