Graphql Client with React & Apollo
React JS, Apollo Graphql Client
Last time we had seen together how to do a Nodejs graphql server using Apollo :
We continue our trip inside graphql and this time we will focus on client.
We will use :
- React JS
- Apollo Graphql Client (hooks version)
- ant design to have beautiful UI
Project init
Create project
npx create-react-app react-graphql-clientnpm install apollo-boost @apollo/react-hooks graphqlnpm install antd
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import './index.css';
import 'antd/dist/antd.css';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
Apollo Client Init
import React from 'react';
import ApolloClient from 'apollo-boost';
import { ApolloProvider } from '@apollo/react-hooks';
const localGraphQL = "http://localhost:5000/graphql";
const client = new ApolloClient({
uri: localGraphQL
});
function App() {
return (
<ApolloProvider client={client}>
<div>
App
</div>
</ApolloProvider>
);
}
export default App;
User list (query all)
View
import React from 'react';
import { useQuery } from '@apollo/react-hooks';
import {
List,
Button,
} from 'antd';
import {
UserQueries,
}from '../graphql';
const {
GET_USERS,
GET_USERS_SKIP,
GET_USERS_INCLUDE,
} = UserQueries;
const UsersListView = () => {
const {
loading,
error,
data,
refetch,
} = useQuery(GET_USERS, {
// pollInterval: 500,
// fetchPolicy: 'cache-and-network'
});
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
if (!data || !data.users) return <p>Empty :(</p>;
return (
<>
<List
bordered
dataSource={data.users}
renderItem={user => {
const {
firstName,
lastName,
email
} = user;
return (
<List.Item>
<List.Item.Meta
avatar={null}
title={`${firstName} ${lastName}`}
description={email}
/>
</List.Item>
)
}}
/>
<Button
type="primary"
onClick={() => refetch()}
style={{ margin: '1rem' }}
>
Refetch!
</Button>
</>)
};
export default UsersListView;
Query
import { gql } from 'apollo-boost';const GET_USERS = gql`
{
users {
id
firstName
lastName
birthday
phone
username
posts {
createdAt
text
user
}
}
}
`;
User details (query by email)
View
import React from 'react';
import { useQuery } from '@apollo/react-hooks';
import { Descriptions } from 'antd';
import {
UserQueries,
}from '../graphql';
const {
GET_USER,
} = UserQueries;
const UserDetailsView = ({
email,
}) => {
const {
loading,
error,
data,
} = useQuery(GET_USER, {
variables: { email },
});
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
if (!data || !data.user) return <p>Empty :(</p>;
const {
firstName,
lastName,
username,
} = data.user;
return (
<div>
<Descriptions title="User Details">
<Descriptions.Item label="username">{username}</Descriptions.Item>
<Descriptions.Item label="First Name">{firstName}</Descriptions.Item>
<Descriptions.Item label="Last Name">{lastName}</Descriptions.Item>
<Descriptions.Item label="Birthday">22/02/1299</Descriptions.Item>
</Descriptions>
</div>);
};
export default UserDetailsView;
Query
const GET_USER = gql`
query User($email: String!) {
user(email: $email) {
id
firstName
lastName
birthday
email
phone
username
password
posts {
createdAt
text
user
}
}
}
`;
User add form (mutation)
View
import React, { useState } from 'react';
import { useMutation } from '@apollo/react-hooks';
import {
Form,
Input,
Button,
} from 'antd';
import {
UserMutations,
}from '../graphql';
const {
ADD_USER,
} = UserMutations;
const UserAddView = () => {
const [
addUser,
{loading}
] = useMutation(ADD_USER, { errorPolicy: 'all' });
const [data, setData ] = useState('');
const [error, setError ] = useState('');
const handleSubmit = async (values) => {
try {
const { data } = addUser({
variables: {
...values,
}})
setData(data)
}catch (e) {
setError(e)
}
}
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
const onFinish = values => {
handleSubmit(values);
};
return (
<Form
name="basic"
onFinish={onFinish}
>
<Form.Item
label="First Name"
name="firstName"
rules={[{ required: true, message: 'Please input your first name!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="Last Name"
name="lastName"
rules={[{ required: true, message: 'Please input your last name!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="Email"
name="email"
rules={[{ required: true, message: 'Please input your email!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="Username"
name="username"
rules={[{ required: true, message: 'Please input your username!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="Password"
name="password"
rules={[{ required: true, message: 'Please input your password!' }]}
>
<Input.Password />
</Form.Item>
<Form.Item >
<Button
type="primary"
htmlType="submit"
>
Submit
</Button>
</Form.Item>
</Form>
)
};
export default UserAddView;
Mutation
import { gql } from 'apollo-boost';
const ADD_USER = gql`
mutation AddUser (
$firstName: String!
$lastName: String!
$phone: String
$email: String!
$username: String!
$password: String!
) {
addUser(firstName: $firstName, lastName: $lastName, phone: $phone, email: $email, username: $username, password: $password) {
id
firstName
lastName
birthday
phone
email
username
}
}
`;
App
import React from 'react';
import ApolloClient from 'apollo-boost';
import { ApolloProvider } from '@apollo/react-hooks';
import {
Divider,
} from 'antd';
import {
UsersListView,
UserDetailsView,
UserAddView,
} from './components';
const localGraphQL = "http://localhost:5000/graphql";
const client = new ApolloClient({
uri: localGraphQL
});
function App() {
return (
<ApolloProvider client={client}>
<div>
<Divider plain>User List</Divider>
<UsersListView />
<Divider plain>User Details</Divider>
<UserDetailsView
email="alberteinsten@gmail.com"
/>
<Divider plain>Add new user</Divider>
<UserAddView />
</div>
</ApolloProvider>
);
}
export default App;
Graphql directives
1. skip
View
const {
loading,
error,
data,
refetch,
} = useQuery(GET_USERS_SKIP, {
variables: {
skipEmail: true,
},
});
Query
const GET_USERS_SKIP = gql`
query Users($skipEmail: Boolean!) {
users{
id
firstName
lastName
birthday
email @skip(if: $skipEmail)
phone
username
posts {
createdAt
text
user
}
}
}
`;
skip : GraphQL execution skips the field email if skipEmail = true by not calling the resolver.
2. include
View
const {
loading,
error,
data,
refetch,
} = useQuery(GET_USERS_INCLUDE, {
variables: {
includeEmail: false,
},
});
Query
const GET_USERS_INCLUDE = gql`
query Users($includeEmail: Boolean!) {
users{
id
firstName
lastName
birthday
email @include(if: $includeEmail)
phone
username
posts {
createdAt
text
user
}
}
}
`;
include : Calls resolver for annotated field if true.
Graphql Fragment
Let’s look at these queries :
const GET_USERS = gql`
{
users {
id
firstName
lastName
birthday
phone
username
posts {
createdAt
text
user
}
}
}
`;const GET_USER = gql`
query User($email: String!) {
user(email: $email) {
id
firstName
lastName
birthday
phone
username
password
posts {
createdAt
text
user
}
}
}
`;
What’s wrong ?
Each time we need user informations we duplicate this part :
id
firstName
lastName
birthday
email
phone
username
password
To avoid duplication we can use Fragment.
Fragment definition :
const USER_PROFILE = gql`
fragment UserProfile on UserType {
id
firstName
lastName
birthday
email
phone
username
}
`;
UserType is the schema defined in graphql server :
type UserType {
id: ID!
firstName: String! @upper
lastName: String! @upper
birthday: DateTime
phone: String
email: String!
username: String!
password: String!
isConnected: Boolean
numberOfFollowers: Int
login: String @deprecated(reason: "Use \`username\`.")
posts: [PostType]
}
Fragment calls :
const GET_USERS = gql`
{
users {
...UserProfile
posts {
createdAt
text
user
}
}
}
${USER_PROFILE}
`;
const GET_USER = gql`
query User($email: String!) {
user(email: $email) {
...UserProfile
posts {
createdAt
text
user
}
}
}
${USER_PROFILE}
`;
Project source code
Thank you for reading my story.
You can find me at :
Twitter : https://twitter.com/b_k_hela
Github : https://github.com/helabenkhalfallah