SaltyCrane Blog — Notes on JavaScript and web development

How to set up a React Apollo client with a Graphcool GraphQL server

Graphcool is a service similar to Firebase except it is used to create GraphQL APIs instead of RESTful ones. Apollo Client is a GraphQL client (alternative to Relay) that can be used with React (and other frontend frameworks). Below is how to create a Graphcool GraphQL service and query it from a React frontend using Apollo Client. I also used Next.js to set up the React project. I am running Node 8.4.0 on macOS Sierra. The code for this example is on github: https://github.com/saltycrane/graphcool-apollo-example.

Jump to: Graphcool setup, Next.js setup, or Apollo setup.

Graphcool GraphQL server

Graphcool Apollo Quickstart: https://www.graph.cool/docs/quickstart/frontend/react/apollo-tijghei9go

  • Install the Graphcool command-line tool (step 2 in quickstart)
    $ npm install -g graphcool-framework 
    
    $ graphcool-framework --version
    graphcool-framework/0.11.5 (darwin-x64) node-v8.4.0 
    
  • Create a Graphcool service (step 3 in quickstart)
    $ cd /tmp
    $ mkdir graphcool-apollo-example
    $ cd graphcool-apollo-example 
    
    $ graphcool-framework init myserver
    Creating a new Graphcool service in myserver... ✔
    
    Written files:
    ├─ types.graphql
    ├─ src
    │  ├─ hello.js
    │  └─ hello.graphql
    ├─ graphcool.yml
    └─ package.json
    
    To get started, cd into the new directory:
      cd myserver
    
    To deploy your Graphcool service:
      graphcool deploy
    
    To start your local Graphcool cluster:
      graphcool local up
    
    To add facebook authentication to your service:
      graphcool add-template auth/facebook
    
    You can find further instructions in the graphcool.yml file,
    which is the central project configuration.
    
  • Add a Post type definition. (step 4 in quickstart) Edit myserver/types.graphql:
    type Post @model {
      id: ID! @isUnique    # read-only (managed by Graphcool)
      createdAt: DateTime! # read-only (managed by Graphcool)
      updatedAt: DateTime! # read-only (managed by Graphcool)
    
      description: String!
      imageUrl: String!
    }
  • Deploy the Graphcool server (step 5 in quickstart). After running the deploy command, it will ask to select a cluster, select a name, and create an account with Graphcool.
    $ cd myserver
    $ graphcool-framework deploy
    ? Please choose the cluster you want to deploy to
    
    shared-eu-west-1
    
    Auth URL: https://console.graph.cool/cli/auth?cliToken=xxxxxxxxxxxxxxxxxxxxxxxxx&authTrigger;=auth
    Authenticating... ✔
    
    Creating service myserver in cluster shared-eu-west-1... ✔
    Bundling functions... 2.3s
    Deploying... 1.3s
    
    Success! Created the following service:
    
    Types
    
      Post
       + A new type with the name `Post` is created.
       ├─ +  A new field with the name `createdAt` and type `DateTime!` is created.
       ├─ +  A new field with the name `updatedAt` and type `DateTime!` is created.
       ├─ +  A new field with the name `description` and type `String!` is created.
       └─ +  A new field with the name `imageUrl` and type `String!` is created.
    
    Resolver Functions
    
      hello
       + A new resolver function with the name `hello` is created.
    
    Permissions
    
      Wildcard Permission
       ? The wildcard permission for all operations is added.
    
    Here are your GraphQL Endpoints:
    
      Simple API:        https://api.graph.cool/simple/v1/cjc2uk4kx0vzo01603rkov391
      Relay API:         https://api.graph.cool/relay/v1/cjc2uk4kx0vzo01603rkov391
      Subscriptions API: wss://subscriptions.graph.cool/v1/cjc2uk4kx0vzo01603rkov391 
    
  • Run some queries in the Graphcool playground (step 6 in quickstart). Run the following command to open a new browser tab with the Graphcool playground:
    $ graphcool-framework playground 
    
    Run a query to create a post:
    mutation {
      createPost(
        description: "A rare look into the Graphcool office"
        imageUrl: "https://media2.giphy.com/media/xGWD6oKGmkp6E/200_s.gif"
      ) {
        id
      }
    }
    Run a query to retrieve all posts:
    query {
      allPosts {
        id
        description
        imageUrl
      }
    }
  • Done with Graphcool server. Also see https://console.graph.cool/server/schema/types

Next.js React frontend

Set up a React frontend using the Next.js framework. Next.js setup: https://github.com/zeit/next.js#setup

  • Create a myclient directory alongside the myserver directory:
    $ cd /tmp/graphcool-apollo-example
    $ mkdir myclient
    $ cd myclient 
    
  • Create myclient/package.json:
    {
      "dependencies": {
        "next": "4.2.1",
        "react": "16.2.0",
        "react-dom": "16.2.0"
      },
      "scripts": {
        "dev": "next",
        "build": "next build",
        "start": "next start"
      }
    }
  • Install Next.js and React:
    $ npm install
    npm WARN deprecated npmconf@2.1.2: this package has been reintegrated into npm and is now out of date with respect to npm
    npm WARN deprecated @semantic-release/last-release-npm@2.0.2: Use @semantic-release/npm instead
    
    > fsevents@1.1.3 install /private/tmp/graphcool-apollo-example/myclient/node_modules/fsevents
    > node install
    
    [fsevents] Success: "/private/tmp/graphcool-apollo-example/myclient/node_modules/fsevents/lib/binding/Release/node-v57-darwin-x64/fse.node" already installed
    Pass --update-binary to reinstall or --build-from-source to recompile
    
    > uglifyjs-webpack-plugin@0.4.6 postinstall /private/tmp/graphcool-apollo-example/myclient/node_modules/uglifyjs-webpack-plugin
    > node lib/post_install.js
    
    npm notice created a lockfile as package-lock.json. You should commit this file.
    npm WARN myclient No description
    npm WARN myclient No repository field.
    npm WARN myclient No license field.
    
    + react-dom@16.2.0
    + react@16.2.0
    + next@4.2.1
    added 838 packages in 20.012s
    
  • Create a Hello World page
    $ mkdir pages
    
    Create myclient/pages/index.js:
    const Home = () => <div>Hello World</div>;
    
    export default Home;
    
  • Run the Next.js dev server
    $ npm run dev 
    
    Go to http://localhost:3000 in the browser

Apollo Client

Set up Apollo Client to query the Graphcool server. Apollo Client setup: https://www.apollographql.com/docs/react/basics/setup.html

  • Install Apollo Client
    $ cd /tmp/graphcool-apollo-example/myclient 
    
    $ npm install apollo-client-preset react-apollo graphql-tag graphql 
    npm WARN apollo-link-http@1.3.2 requires a peer of graphql@^0.11.0 but none is installed. You must install peer dependencies yourself.
    npm WARN myclient No description
    npm WARN myclient No repository field.
    npm WARN myclient No license field.
    
    + react-apollo@2.0.4
    + graphql-tag@2.6.1
    + apollo-client-preset@1.0.6
    + graphql@0.12.3
    added 20 packages in 5.476s
    
  • Set up Apollo Client. Edit myclient/pages/index.js:
    import { InMemoryCache } from "apollo-cache-inmemory";
    import { ApolloClient } from "apollo-client";
    import { HttpLink } from "apollo-link-http";
    import { ApolloProvider } from "react-apollo";
    
    const client = new ApolloClient({
      link: new HttpLink({
        // Replace this with your Graphcool server URL
        uri: "https://api.graph.cool/simple/v1/cjc2uk4kx0vzo01603rkov391",
      }),
      cache: new InMemoryCache(),
    });
    
    const Home = () => <div>Hello World</div>;
    
    const App = () => (
      <ApolloProvider client={client}>
        <Home />
      </ApolloProvider>
    );
    
    export default App;
    
  • Try running the dev server
    $ npm run dev 
    
    Go to http://localhost:3000 in the browser
  • But, get this error:
    Error: fetch is not found globally and no fetcher passed, to fix pass a fetch for
          your environment like https://www.npmjs.com/package/node-fetch.
    
          For example:
            import fetch from 'node-fetch';
            import { createHttpLink } from 'apollo-link-http';
    
            const link = createHttpLink({ uri: '/graphql', fetch: fetch });
    
        at warnIfNoFetch (/private/tmp/graphcool-apollo-example/myclient/node_modules/apollo-link-http/lib/httpLink.js:72:15)
        at createHttpLink (/private/tmp/graphcool-apollo-example/myclient/node_modules/apollo-link-http/lib/httpLink.js:89:5)
        at new HttpLink (/private/tmp/graphcool-apollo-example/myclient/node_modules/apollo-link-http/lib/httpLink.js:159:34)
        at Object. (/private/tmp/graphcool-apollo-example/myclient/.next/dist/pages/index.js:25:9)
          at Module._compile (module.js:573:30)
          at Module._compile (/private/tmp/graphcool-apollo-example/myclient/node_modules/source-map-support/source-map-support.js:492:25)
          at Object.Module._extensions..js (module.js:584:10)
          at Module.load (module.js:507:32)
          at tryModuleLoad (module.js:470:12)
          at Function.Module._load (module.js:462:3)
  • Install node-fetch. This is needed because Next.js runs on a Node server in addition to the browser.
    $ npm install node-fetch
    npm WARN apollo-link-http@1.3.2 requires a peer of graphql@^0.11.0 but none is installed. You must install peer dependencies yourself.
    npm WARN myclient No description
    npm WARN myclient No repository field.
    npm WARN myclient No license field.
    
    + node-fetch@1.7.3
    updated 1 package in 4.028s
    
  • Update the code to use node-fetch as described in the error message:
    import { InMemoryCache } from "apollo-cache-inmemory";
    import { ApolloClient } from "apollo-client";
    import { createHttpLink } from "apollo-link-http";
    import gql from "graphql-tag";
    import fetch from "node-fetch";
    import { ApolloProvider } from "react-apollo";
    
    const client = new ApolloClient({
      link: createHttpLink({
        // Replace this with your Graphcool server URL
        uri: "https://api.graph.cool/simple/v1/cjc2uk4kx0vzo01603rkov391",
        fetch: fetch,
      }),
      cache: new InMemoryCache(),
    });
    
    class Home extends React.Component {
      componentDidMount() {
        client
          .query({
            query: gql`
              {
                allPosts {
                  id
                  description
                  imageUrl
                }
              }
            `,
          })
          .then(console.log);
      }
    
      render() {
        return <div>Look in the devtools console</div>;
      }
    }
    
    const App = () => (
      <ApolloProvider client={client}>
        <Home />
      </ApolloProvider>
    );
    
    export default App;
    
  • Try running the dev server again
    $ npm run dev 
    
    Go to http://localhost:3000 in the browser
  • It works. Open the browser devtools console and see the result of the query:
    {
      "data": {
        "allPosts": [
          {
            "id": "cjbffxjq5rrvd0192qmptpm2f",
            "description": "A rare look into the Graphcool office",
            "imageUrl": "https://media2.giphy.com/media/xGWD6oKGmkp6E/200_s.gif",
            "__typename": "Post"
          }
        ]
      },
      "loading": false,
      "networkStatus": 7,
      "stale": false
    }
  • Use the graphql higher-order component to make things nicer. Edit myclient/pages/index.js:
    import { InMemoryCache } from "apollo-cache-inmemory";
    import { ApolloClient } from "apollo-client";
    import { createHttpLink } from "apollo-link-http";
    import gql from "graphql-tag";
    import fetch from "node-fetch";
    import { ApolloProvider, graphql } from "react-apollo";
    
    const client = new ApolloClient({
      link: createHttpLink({
        // Replace this with your Graphcool server URL
        uri: "https://api.graph.cool/simple/v1/cjc2uk4kx0vzo01603rkov391",
        fetch: fetch,
      }),
      cache: new InMemoryCache(),
    });
    
    const MY_QUERY = gql`
      {
        allPosts {
          id
          description
          imageUrl
        }
      }
    `;
    
    const Home = ({ data }) => <pre>{JSON.stringify(data, null, 2)}</pre>;
    
    const HomeWithData = graphql(MY_QUERY)(Home);
    
    const App = () => (
      <ApolloProvider client={client}>
        <HomeWithData />
      </ApolloProvider>
    );
    
    export default App;
    
  • Run the dev server
    $ npm run dev 
    
    Go to http://localhost:3000 in the browser and see this result on the page:
    {
      "variables": {},
      "loading": false,
      "networkStatus": 7,
      "allPosts": [
        {
          "id": "cjbffxjq5rrvd0192qmptpm2f",
          "description": "A rare look into the Graphcool office",
          "imageUrl": "https://media2.giphy.com/media/xGWD6oKGmkp6E/200_s.gif",
          "__typename": "Post"
        }
      ]
    }

Comments