Apollo GraphQL: How to build a Full-stack app with React and Node Js

Ibrahima Ndaw - Apr 29 '20 - - Dev Community

Apollo Client is a complete state management library for JavaScript apps. It's a powerful tool since it can be used on both back-end and front-end side.
In this tutorial, we will use it on both by building first an Apollo GraphQL Server with Node JS and then consume the data on the client-side using React JS.

If you're new to GraphQl, this tutorial might help you. Otherwise, Let's get started.

Originally posted on my blog

Building the server with Apollo, Node, and GraphQl

In this guide, I will use the Github API to have data to show, and that operation will be done by the GraphQl server built with Apollo and Node JS.
And to do so, we need to run the following command on the terminal to set up a new Node JS project.

  yarn init
Enter fullscreen mode Exit fullscreen mode

Once the set up is done, we can now install the needed packages by running this command.

  yarn add apollo-server graphql axios
Enter fullscreen mode Exit fullscreen mode

Great, we have now all we need to build a server, let's create first a new file app.js in the root which will be the entry point of our server.

Next, we need to define a Graphql schema that mirrors the way our data should look like.

GraphQl Schema

A schema describes the shape of your data graph. It defines a set of types with fields that are populated from your back-end data stores. So, let's add a new schema in the app.js file.

  • app.js
const { ApolloServer, gql } = require("apollo-server")
const axios = require("axios")

const typeDefs = gql`
  type User {
    id: ID
    login: String
    avatar_url: String
  }

  type Query {
    users: [User]
  }
`
Enter fullscreen mode Exit fullscreen mode

As you can see we don't use all data provided by the Github API. We just need the id that will be used as a reference key on the React App, the login, and the avatar_url. We also have a query users that returns an array of users.

Now we have a GraphQL schema, it's time to build the corresponding resolvers to complete the query operation.

GraphQl resolvers

A resolver is a collection of functions that helps generating a response from a GraphQL query. So, let's add a new resolver in the app.js file.

  • app.js
const resolvers = {
  Query: {
    users: async () => {
      try {
        const users = await axios.get("https://api.github.com/users")
        return users.data.map(({ id, login, avatar_url }) => ({
          id,
          login,
          avatar_url,
        }))
      } catch (error) {
        throw error
      }
    },
  },
}
Enter fullscreen mode Exit fullscreen mode

A resolver has to match by name the appropriate schema. Therefore, here users refers to the users query defined in our schema. It's a function that fetches the data from the API with the help of axios and returns as expected the id, the login, and the avatar_url.
And that operation can take time to complete that's why async/await is used here to handle it consequently.

With that, we can now create the Apollo Server in the next section.

Creating the Apollo Server

If you remember, in the app.js file, we had imported ApolloServer from the apollo-server package, it a constructor that receives an object as an argument. And that object must contain the schema and the resolver to be able to create the server.

So, let's tweak app.js a bit with ApolloServer.

  • app.js
const server = new ApolloServer({
  typeDefs,
  resolvers,
})
//  typeDefs: typeDefs,
//  resolvers: resolvers
server.listen().then(({ url }) => console.log(`Server ready at ${url}`))
Enter fullscreen mode Exit fullscreen mode

Here, we pass as a parameter an object that holds the schema and the resolver to ApolloServer to create the server and then listens to it. With that in place, we have now a functional server to work with.

You can already play with it and send queries with the help of GraphQL playground by running this command:

  yarn start
Enter fullscreen mode Exit fullscreen mode

You can now preview it on http://localhost:400

  • The complete app.js file
const { ApolloServer, gql } = require("apollo-server")
const axios = require("axios")

const typeDefs = gql`
  type User {
    id: ID
    login: String
    avatar_url: String
  }

  type Query {
    users: [User]
  }
`

const resolvers = {
  Query: {
    users: async () => {
      try {
        const users = await axios.get("https://api.github.com/users")
        return users.data.map(({ id, login, avatar_url }) => ({
          id,
          login,
          avatar_url,
        }))
      } catch (error) {
        throw error
      }
    },
  },
}

const server = new ApolloServer({
  typeDefs,
  resolvers,
})

server.listen().then(({ url }) => console.log(`Server ready at ${url}`))
Enter fullscreen mode Exit fullscreen mode

A server alone does not do much, we need to add a start script in the package.json file to as you guessed start the server.

  • package.json
  // first add nodemon: yarn add nodemon --dev
  "scripts": {
    "start": "nodemon src/index.js"
  }
Enter fullscreen mode Exit fullscreen mode

With that, we have now a server to fetch data from the Github API, it's time to move to the client-side and consume the data.

Let's do it

yaay

Building the Client-side with React

The first thing we have to do is creating a fresh React App by running in the terminal the following command

npx create-react-app client-react-apollo
Enter fullscreen mode Exit fullscreen mode

Next, we need to install the Apollo and GraphQl packages.

  yarn add apollo-boost @apollo/react-hooks graphql
Enter fullscreen mode Exit fullscreen mode

Now, we can connect Apollo with our React App by updating the index.js file.

Connecting React to Apollo

  • index.js
import React from 'react';
import ReactDOM from 'react-dom';
import ApolloClient from 'apollo-boost'
import { ApolloProvider } from '@apollo/react-hooks';

import App from './App';
import './index.css';
import * as serviceWorker from './serviceWorker';

const client = new ApolloClient({
  uri: 'https://7sgx4.sse.codesandbox.io'
})


ReactDOM.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

serviceWorker.unregister();
Enter fullscreen mode Exit fullscreen mode

As you can see, we start by importing ApolloClient and ApolloProvider. The first helps us to inform Apollo about the URL to use when fetching data. And if no uri is passed to ApolloClient, it will take the current domain name plus /graphql.
The second is the Provider which expects to receive the client object to be able to connect Apollo to React.

That said, we can now create a component that shows the data.

Fetching the data

  • App.js
import React from "react"
import { useQuery } from "@apollo/react-hooks"
import gql from "graphql-tag"
import "./App.css"

const GET_USERS = gql`
  {
    users {
      id
      login
      avatar_url
    }
  }
`
Enter fullscreen mode Exit fullscreen mode

Here, we have a simple GraphQL query that fetches the data. That query will be passed later to useQuery to tell to Apollo which data to fetch.

  • App.js
const User = ({ user: { login, avatar_url } }) => (
  <div className="Card">
    <div>
      <img alt="avatar" className="Card--avatar" src={avatar_url} />
      <h1 className="Card--name">{login}</h1>
    </div>
    <a href={`https://github.com/${login}`} className="Card--link">
      See profile
    </a>
  </div>
)
Enter fullscreen mode Exit fullscreen mode

This presentational component will be used to display a user. It receives the data from the App component and displays it.

Showing the data

  • App.js
function App() {
  const { loading, error, data } = useQuery(GET_USERS)

  if (error) return <h1>Something went wrong!</h1>
  if (loading) return <h1>Loading...</h1>

  return (
    <main className="App">
      <h1>Github | Users</h1>
      {data.users.map(user => (
        <User key={user.id} user={user} />
      ))}
    </main>
  )
}

export default App
Enter fullscreen mode Exit fullscreen mode

The useQuery hook provided by Apollo receives the GraphQL query and returns three states: the loading, the error, and the data.
If the data are successfully fetched, we pass it to the User component otherwise we throw an error.

  • The complete App.js file
import React from "react"
import { useQuery } from "@apollo/react-hooks"
import gql from "graphql-tag"
import "./App.css"

const GET_USERS = gql`
  {
    users {
      id
      login
      avatar_url
    }
  }
`

const User = ({ user: { login, avatar_url } }) => (
  <div className="Card">
    <div>
      <img alt="avatar" className="Card--avatar" src={avatar_url} />
      <h1 className="Card--name">{login}</h1>
    </div>
    <a href={`https://github.com/${login}`} className="Card--link">
      See profile
    </a>
  </div>
)

function App() {
  const { loading, error, data } = useQuery(GET_USERS)

  if (error) return <h1>Something went wrong!</h1>
  if (loading) return <h1>Loading...</h1>

  return (
    <main className="App">
      <h1>Github | Users</h1>
      {data.users.map(user => (
        <User key={user.id} user={user} />
      ))}
    </main>
  )
}

export default App
Enter fullscreen mode Exit fullscreen mode

Great! with that, we have now done building a full-stack Apollo GraphQL app using React and Node JS.

Preview the Apollo GraphQL Server here

Preview the React App here

Find the source code here

Thanks for reading

congrats

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .