Building the New facebook.com with React, GraphQL and Relay

Building the New facebook.com with React, GraphQL and Relay

Open source projects like React, GraphQL and Relay are powering more and more Facebook services. In this session, we'll discuss how we use the latest features of these technologies, like React Suspense, to help deliver a high quality, modern web experience at Facebook.

Open source projects like React, GraphQL and Relay are powering more and more Facebook services. In this session, we'll discuss how we use the latest features of these technologies, like React Suspense, to help deliver a high quality, modern web experience at Facebook.

Video

How to Build a Web App with GraphQL and React

How to Build a Web App with GraphQL and React

In this tutorial, we’ll learn to build a web application with React and GraphQL. We’ll consume an API available from graphql-pokemon and serve it from this link which allows you to get information about Pokémon.

In this tutorial, we’ll learn to build a web application with React and GraphQL. We’ll consume an API available from graphql-pokemon and serve it from this link, which allows you to get information about Pokémon.

GraphQL is a query language for APIs and a runtime for fulfilling those queries created by Facebook. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.

In this tutorial, we’ll only learn the front end of a GraphQL application that makes use of Apollo for fetching data from a ready GraphQL API hosted on the web.

Let’s get started with the prerequisites!

Prerequisites

There are a few prerequisites for this tutorial:

  • recent versions of Node.js and npm installed on your system
  • knowledge of JavaScript/ES6
  • familiarity with React

If you don’t have Node and npm installed on your development machine, you can simply download the binaries for your system from the official website. You can also use NVM, a POSIX-compliant bash script to manage multiple active Node.js versions.

Installing create-react-app

Let’s install the create-react-app tool that allows you to quickly initialize and work with React projects.

Open a new terminal and run the following command:

npm install -g create-react-app

Note: You may need to use sudo before your command in Linux and macOS or use a command prompt with administrator rights if you get EACCESS errors when installing the package globally on your machine. You can also simply fix your npm permissions.

At the time of writing, this installs create-react-app v3.1.1.

Creating a React Project

Now we’re ready to create our React project.

Go back to your terminal and run the following command:

create-react-app react-pokemon

Next, navigate into your project’s folder and start the local development server:

cd react-pokemon
npm start

Go to http://localhost:3000 in your web browser to see your app up and running.

This is a screenshot of the app at this point:

Installing Apollo Client

Apollo Client is a complete data management solution that’s commonly used with React, but can be used with any other library or framework.

Apollo provides intelligent caching that enables it to be a single source of truth for the local and remote data in your application.

You’ll need to install the following packages in your React project to work with Apollo:

  • graphql: the JavaScript reference implementation for GraphQL
  • apollo-client: a fully-featured caching GraphQL client with integrations for React, Angular, and more
  • apollo-cache-inmemory: the recommended cache implementation for Apollo Client 2.0
  • apollo-link-http: the most common Apollo Link, a system of modular components for GraphQL networking
  • react-apollo: this package allows you to fetch data from your GraphQL server and use it in building complex and reactive UIs using the React framework
  • graphql-tag: this package provides helpful utilities for parsing GraphQL queries such as gql tag.

Open a new terminal and navigate to your project’s folder, then run the following commands:

npm install graphql --save
npm install apollo-client --save
npm install apollo-cache-inmemory --save
npm install apollo-link-http --save
npm install react-apollo --save
npm install graphql-tag --save

Now that we’ve installed the necessary packages, we need to create an instance of ApolloClient.

Open the src/index.js file and add the following code:

import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';

const cache = new InMemoryCache();
const link = new HttpLink({
  uri: 'https://graphql-pokemon.now.sh/'
})

const client = new ApolloClient({
  cache,
  link
})

We first create an instance of InMemoryCache, then an instance of HttpLink and we pass in our GraphQL API URI. Next, we create an instance of ApolloClient and we provide the cache and link instances.

Connect the Apollo Client to React Components

After creating the instance of ApolloClient, we need to connect it to our React component(s).

We’ll use the new Apollo hooks, which allows us to easily bind GraphQL operations to our UI.

We can connect Apollo Client to our React app by simply wrapping the root App component with the ApolloProvider component — which is exported from the @apollo/react-hooks package — and passing the client instance via the client prop.

The ApolloProvider component is similar to React’s Context provider. It wraps your React app and places the client in the context, which enables you to access it from anywhere in your app.

Now let’s import the ApolloProvider component in our src/index.js file and wrap the App component as follows:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import { ApolloProvider } from '@apollo/react-hooks';

const cache = new InMemoryCache();
const link = new HttpLink({
  uri: 'https://graphql-pokemon.now.sh/'
})

const client = new ApolloClient({
  cache,
  link
})

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

serviceWorker.unregister();

Fetching Data with Queries

After adding and configuring the Apollo client in our application, let’s fetch some Pokémon data and render it in our component.

Open the src/App.js file and start by adding the following imports:

import { useQuery } from '@apollo/react-hooks';
import gql from "graphql-tag";

We imported the useQuery hook from the @apollo/react-hooks package. It’s a React Hook that fetches a GraphQL query and exposes the result so you can render your UI based on the data it returns. We also imported the gql tag, which allows us to parse GraphQL queries.

The useQuery hook is built on top of React’s Hooks API to fetch and load data from GraphQL queries into the UI of our applications. It exposes error, loading and data properties through a result object, used to populate and render our component.

After the imports, define the following GraphQL query:

const GET_POKEMON_INFO = gql`
{
    pokemons(first: 150) {
      id
      number
      name,
      image,
      evolutions {
        id,
        number,
        name,
        image
      }
    }
  }

This query will allow us to fetch the first 150 Pokémon with their ID, number, name, image, and evolutions.

Next, let’s run the query against our GraphQL API by calling the useQuery hook with our GET_POKEMON_INFO query:

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

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error</p>;

We use object destructuring to get the data, loading and error parts from the returned value of the useQuery hook.

If loading is true, this means data is still being fetched so we simply render the Loading… message code to indicate to users that data is still loading.

If an error occurs during the query, the error variable will have a value of true. In this case, we simply render an Error message.

Next, if data is done loading successfully, we render the list of Pokémon:

return (
  <React.Fragment>
    <h1>Pokémons</h1>

    <p>
      <a href="https://en.wikipedia.org/wiki/List_of_Pok%C3%A9mon">
        The Pokémon franchise
      </a>{" "}
      revolves around 832 fictional species of collectible monsters, each having
      unique designs and skills. Conceived by Satoshi Tajiri in early 1989,
      Pokémon are creatures that inhabit the fictional Pokémon World. This is
      the list of the first 150 Pokémon as they appear in Pokémon Stadium,
      starting with Bulbasaur in the top left corner and ending with Mewtwo in
      the bottom right corner.
    </p>
    <div className="container">
      {data &&
        data.pokemons &&
        data.pokemons.map((pokemon, index) => (
          <div key={index} className="card">
            <img src={pokemon.image} />
            <div class="card-body">
              <h3>{pokemon.name}</h3>
              <p>
                {pokemon.evolutions && pokemon.evolutions.length !== 0 && (
                  <p>
                    {" "}
                    Evolutions:
                    {pokemon.evolutions.map((e, indx) => {
                      return <p key={indx}> {e.name} </p>;
                    })}
                  </p>
                )}
              </p>
            </div>
          </div>
        ))}
    </div>
  </React.Fragment>
);

Styling the App

After fetching and rendering data in our React application, let’s add a bit of styling to the interface.

Open the public/index.html file and add a <link> tag to the Kalam font:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
    <link href="https://fonts.googleapis.com/css?family=Kalam" rel="stylesheet">

Next, open the src/index.css file and add the following CSS styles:

body {
  margin: 5px;
  font-family: 'kalam';
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.container {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    grid-gap: 19px;
}

.container > .card img {
    max-width: 100%;
}

This is a screenshot of our application at this stage:

Building and Hosting the Application

Now that we’ve created our application, we can build the production bundles using the following command:

npm run build

The command will produce a minified and optimized production bundle in the build folder that you can upload to your server.

We’ll be using ZEIT Now for hosting the application.

ZEIT Now is a cloud platform for websites and serverless functions that you can use to deploy your projects to a .now.sh or personal domain.

Head back to your terminal and execute the following command to install Now CLI:

npm  install -g now

Next, navigate to the build folder and run the now command:

cd build
now

That’s it! Your application will be uploaded to the hosting server. You can see the app live at https://build-cy9hwmjpe.now.sh.

You can also find the source code of this application in this GitHub repository.

Conclusion

In this tutorial, we built a React app that fetches the first 150 Pokémon, as they appear in Pokémon Stadium, and render them. We used the Apollo client with React hooks to fetch and manage data in our app.

Make CRUD simple with Nodejs, GraphQL, and React

Make CRUD simple with Nodejs, GraphQL, and React

It's great because the developer can use a simple, strongly typed language to define ... Create the GraphQL Server for Your Node.js App ...... User Authentication · Build a Simple Web App with Express, React, and GraphQL

GraphQL reduces the complexity of building APIs by abstracting all requests to a single endpoint. Unlike traditional REST APIs, it is declarative; whatever is requested is returned.

Of course, not all projects require GraphQL — it is merely a tool to consolidate data. It has well-defined schema, so we know for sure we won’t overfetch. But if we already have a stable RESTful API system where we rely on data from a single data source, we don’t need GraphQL.

For instance, let’s assume we are creating a blog for ourselves and we decide to store, retrieve, and communicate to data in a single MongoDB database. In this case, we are not doing anything architecturally complex, and we don’t need GraphQL.

On the other hand, let’s imagine we have a full-fledged product that relies on data from multiple sources (e.g., MongoDB, MySQL, Postgres, and other APIs). In this case, we should go for GraphQL.

For example, if we’re designing a portfolio site for ourselves and we want data from social media and GitHub (to show contributions), and we also have our own database to maintain a blog, we can use GraphQL to write the business logic and schema. It will consolidate data as a single source of truth.

Once we have the resolver functions to dispatch the right data to the front end, we will easily be able to manage data within a single source. In this article, we’re going to implement simple end-to-end CRUD operations with GraphQL.

CRUD with graphql-server

Setting up our server

We are going to spin off a simple GraphQL server using express-graphql and get it connected to a MySQL database. The source code and the MySQL files are in this repository.

A GraphQL server is built on top of schema and resolvers. As a first step, we build a schema (defining types, queries, mutations, and subscriptions). This schema describes the whole app structure.

Secondly, for the stuff defined in the schema, we’re building respective resolvers to compute and dispatch data. A resolver maps actions with functions; for each query declared in typedef, we create a resolver to return data.

Finally, we complete server settings by defining an endpoint and passing configurations. We initialize /graphql as the endpoint for our app. To the graphqlHTTP middleware, we pass the built schema and root resolver.

Along with the schema and root resolver, we’re enabling the GraphiQL playground. GraphiQL is an interactive in-browser GraphQL IDE that helps us play around with the GraphQL queries we build.

var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');

var schema = buildSchema( type Query { hello: String });

var root = {
hello: () => "World"
};

var app = express();

app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));

app.listen(4000);

console.log('Running a GraphQL API server at localhost:4000/graphql');

Once the server is good to go, running the app with node index.js will start the server on http://localhost:4000/graphql. We can query for hello and get the string “World” as a response.

Connecting the database

I’m going to establish the connection with the MySQL database as shown below:

var mysql = require('mysql');

app.use((req, res, next) => {
req.mysqlDb = mysql.createConnection({
host : 'localhost',
user : 'root',
password : '',
database : 'userapp'
});
req.mysqlDb.connect();
next();
});

We can connect multiple databases/sources and get them consolidated in the resolvers. I’m connecting to a single MySQL database here. The database dump I’ve used for this article is in the GitHub repository.

Reading and writing data with GraphQL

We use queries and mutations to read and modify data in data-sources. In this example, I’ve defined a generic queryDB function to help query the database.

Queries

All the SELECT statements (or read operations) to list and view data goes into the type Querytypedef. We have two queries defined here: one to list all the users in the database, and another to view a single user by id.

  1. Listing data: To list users, we’re defining a GraphQL schema object type called User, which represents what we can fetch or expect from the getUsers query. We then define the getUsers query to return an array of users.
  2. Viewing a single record: To view a single record, we’re taking id as an argument with the getUserInfo query we have defined. It queries for that particular id in the database and returns the data to the front end.

Now that we have put together the queries to fetch all records and to view record by id, when we try to query for users from GraphiQL, it will list an array of users on the screen! 

var schema = buildSchema(  type User {     id: String     name: String     job_title: String     email: String   }   type Query {     getUsers: [User],     getUserInfo(id: Int) : User   });

const queryDB = (req, sql, args) => new Promise((resolve, reject) => {
req.mysqlDb.query(sql, args, (err, rows) => {
if (err)
return reject(err);
rows.changedRows || rows.affectedRows || rows.insertId ? resolve(true) : resolve(rows);
});
});

var root = {
getUsers: (args, req) => queryDB(req, "select * from users").then(data => data),
getUserInfo: (args, req) => queryDB(req, "select * from users where id = ?", [args.id]).then(data => data[0])
};

Mutations

The write operations for the database — CREATE, UPDATE, DELETE — are generally defined under mutations. The mutations are executed in a sequential manner by the GraphQL engine. Queries are executed parallelly.

  1. Creating data: We have defined a mutation, createUser, that takes the specified arguments to create data in the MySQL database.
  2. Updating or deleting data: Similar to viewing a record, update (updateUserInfo) and delete (deleteUser) take id as a param and modify the database.

The functions resolve with a boolean to indicate whether the change happened or not.

var schema = buildSchema(  type Mutation {     updateUserInfo(id: Int, name: String, email: String, job_title: String): Boolean     createUser(name: String, email: String, job_title: String): Boolean     deleteUser(id: Int): Boolean   });

var root = {
updateUserInfo: (args, req) => queryDB(req, "update users SET ? where id = ?", [args, args.id]).then(data => data),
createUser: (args, req) => queryDB(req, "insert into users SET ?", args).then(data => data),
deleteUser: (args, req) => queryDB(req, "delete from users where id = ?", [args.id]).then(data => data)
};

Now that we have set and sorted the server side of things, let’s try and connect the back end to our React app.

CRUD with graphql-client

Once we have the server in place, creating client logic to display and mutate data is easy. Apollo Client helps in state management and caching. It is also highly abstracted and quick: all of the logic for retrieving your data, tracking loading and error states, and updating UI is encapsulated by the useQueryHook.

Connecting to graphql-server

I have created a CRA boilerplate and have installed GraphQL, apollo-boost, and @apollo/react-hooks. We initialize Apollo Client and get it hooked to React.

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

const client = new ApolloClient({
uri: 'http://localhost:4000/graphql'
});

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

Reading and mutating data

I have managed all the GraphQL queries in the Queries folder of my source code. I’m going to request data from the server with the useQuery Hook, which is built on top of the React Hooks API. It helps in bringing in data into the UI.

GraphQL queries are generally wrapped in the gql function. gql helps convert query string into a query document. Here’s how we define queries in our app.

import { gql } from 'apollo-boost';

export const GET_USERS = gql { getUsers { id, name, job_title, email } };

export const VIEW_USERS = gql query ($id: Int){ getUserInfo(id: $id) { id, name, job_title, email } };

export const ADD_USER = gql mutation($name: String, $email: String, $job_title: String) { createUser (name: $name, email: $email, job_title: $job_title) };

export const EDIT_USER = gql mutation($id: Int, $name: String, $email: String, $job_title: String) { updateUserInfo (id: $id, name: $name, email: $email, job_title: $job_title) };

export const DELETE_USER = gql mutation($id: Int) { deleteUser(id: $id) }

Once ApolloProvider is set, we can request data from our GraphQL server. We pass the query we are trying to make to the useQuery Hook, and it will provide the result for us.

I’ve made two queries, with and without arguments, to show how we should be handling queries and mutations in the front end. useQuery tracks error and loading states for us and will be reflected in the associated object. Once the server sends the result, it will be reflected by the data property.

import React from 'react';
import { useQuery } from '@apollo/react-hooks';
import { GET_USERS, VIEW_USERS } from "./Queries";
import { Card, CardBody, CardHeader, CardSubtitle, Spinner } from 'reactstrap';

function App() {
const getAllUsers = useQuery(GET_USERS);
const userInfo = useQuery(VIEW_USERS, { variables: { id: 1 }});
if (getAllUsers.loading || userInfo.loading) return <Spinner color="dark" />;
if (getAllUsers.error || userInfo.error) return <React.Fragment>Error :(</React.Fragment>;

return (
<div className="container">
<Card>
<CardHeader>Query - Displaying all data</CardHeader>
<CardBody>
<pre>
{JSON.stringify(getAllUsers.data, null, 2)}
</pre>
</CardBody>
</Card>
<Card>
<CardHeader>Query - Displaying data with args</CardHeader>
<CardBody>
<CardSubtitle>Viewing a user by id</CardSubtitle>
<pre>
{JSON.stringify(userInfo.data, null, 2)}
</pre>
</CardBody>
</Card>
</div>
)
}

export default App;

Similar to querying, mutations will use the same useQuery Hook and will pass data as variables into the query.

const deleteMutation = useQuery(DELETE_USER, { variables: { id: 8 }});
const editMutation = useQuery(EDIT_USER, { variables: { id: 9, name: "Username", email: "email", job_title: "job" }});
const createMutation = useQuery(ADD_USER, { variables: { name: "Username", email: "email", job_title: "job" }});
Conclusion

We just did end-to-end CRUD operations with GraphQL. On the client side, reading and mutating data has become very simple after the introduction of React Hooks. Apollo Client also provides provisions for authentication, better error handling, caching, and optimistic UI.

Subscriptions is another interesting concept in GraphQL. With this application as boilerplate, we can keep experimenting with other concepts like these!

Thanks for reading. If you liked this post, share it with all of your programming buddies!

Originally published on blog.logrocket.com