Creating a Project with GraphQL and Understanding Fragments in GraphQL
If you are new to GraphQL, this post will help you start your first project. And if you have knowledge of GraphQL and want to practice fragments, this post is also for you.
Let’s get started! First, we need our React project and then install these dependencies
npm install @apollo/client
npm install graphql-tag babel-plugin-graphql-tag
Create the file .babelrc
{
"plugins": ["babel-plugin-graphql-tag"]
}
Now, we will be using the GitHub API, so we need to generate our token at this link: https://github.com/settings/personal-access-tokens/new
Now, in our root directory, we will create the apolloConfig.js
file where the configuration for our client will go.
import { ApolloClient, InMemoryCache, createHttpLink } from "@apollo/client";
const httpLink = createHttpLink({
uri: "https://api.github.com/graphql",
headers: {
Authorization:
"Bearer YOUR_GITHUB_TOKEN",
},
});
export const client = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
});
createHttpLink
: Imports the createHttpLink
function from @apollo/client
and uses it to configure an HTTP link. This link is used to make HTTP requests to the GitHub API.
httpLink
: Creates an httpLink
object with the GitHub API URL and an authorization header containing your GitHub token.
ApolloClient
: Imports from @apollo/client
and is used to create an instance of the Apollo Client.
client
: Creates an instance of the Apollo Client with the previously configured HTTP link (httpLink
) and an in-memory cache (InMemoryCache
) to store the results of queries.
In summary, these code lines set up the necessary configuration for the Apollo Client to perform queries against the GitHub API using GraphQL. Make sure to replace "YOUR_GITHUB_TOKEN"
with your actual GitHub personal access token.
Now, in our index.js
, we import our client to use it throughout the app
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { ApolloProvider } from "@apollo/client";
import { client } from "./apolloConfig";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</React.StrictMode>
);
We create the SearchResults
component, which will contain the query and be responsible for displaying it on the screen.
import React from "react";
import { useQuery } from "@apollo/client";
import { gql } from "graphql-tag";
import { REPOSITORY_FRAGMENT } from "./fragments";
import "./SearchResults.css";
const SEARCH_QUERY = gql`
query myOrgRepos($queryString: String!) {
search(query: $queryString, type: REPOSITORY, first: 10) {
repositoryCount
edges {
node {
id
name
owner {
login
}
}
}
}
}
`;
const SearchResults = ({ query }) => {
const { loading, error, data } = useQuery(SEARCH_QUERY, {
variables: { queryString: query },
});
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div className="search-results-container">
<h2 className="search-results-title">Search Results</h2>
<ul className="results-list">
{data.search.edges.map((result) => (
<li key={result.id} className="result-item">
<div className="result-details">
<strong>Repository:</strong> {result.node.name},
<strong>Owner:</strong> {result.node.owner.login}
</div>
</li>
))}
</ul>
</div>
);
};
export default SearchResults;
A GraphQL query named myOrgRepos
is defined, taking a variable named $queryString
of type String
. The query utilizes the search
operation in GraphQL to search for repositories on GitHub. The search type is specified as REPOSITORY
, and the results are limited to the first 10.
A functional component named SearchResults
is defined, which receives a query
property. Within the component, the useQuery
hook is employed to execute the SEARCH_QUERY
. The hook returns an object containing information about the query's state, such as loading
(loading), error
(error), and data
(obtained data).
This way, we obtain the data from the GitHub API
In GraphQL, a fragment is a way to reuse and structure specific fields within a query. A fragment allows you to define a set of fields that can be included in multiple queries without repeating the same field definition in each one. This helps keep the code cleaner, modular, and easy to maintain
So in this case, the repository fields, which are fundamental, might be reused in other queries. Therefore, it works better for me to have those fields in a fragment to reuse the code
For learning purposes, we will create a file called fragments.js
. Here, we will create a fragment named RepositoryInfo
, which will contain all the fields we need and will be reused
import { gql } from "graphql-tag";
export const REPOSITORY_FRAGMENT = gql`
fragment RepositoryInfo on Repository {
id
name
owner {
login
}
}
`;
Now, we need to import that fragment and use it as follows within our search query
const SEARCH_QUERY = gql`
query myOrgRepos($queryString: String!) {
search(query: $queryString, type: REPOSITORY, first: 10) {
repositoryCount
edges {
node {
...RepositoryInfo
}
}
}
}
${REPOSITORY_FRAGMENT}
`;
The complete code would look like this:
import React from "react";
import { useQuery } from "@apollo/client";
import { gql } from "graphql-tag";
import { REPOSITORY_FRAGMENT } from "./fragments";
import "./SearchResults.css";
const SEARCH_QUERY = gql`
query myOrgRepos($queryString: String!) {
search(query: $queryString, type: REPOSITORY, first: 10) {
repositoryCount
edges {
node {
...RepositoryInfo
}
}
}
}
${REPOSITORY_FRAGMENT}
`;
const SearchResults = ({ query }) => {
const { loading, error, data } = useQuery(SEARCH_QUERY, {
variables: { queryString: query },
});
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
console.log("data.search", data);
return (
<div className="search-results-container">
<h2 className="search-results-title">Search Results</h2>
<ul className="results-list">
{data.search.edges.map((result) => (
<li key={result.id} className="result-item">
<div className="result-details">
<strong>Repository:</strong> {result.node.name},
<strong>Owner:</strong> {result.node.owner.login}
</div>
</li>
))}
</ul>
</div>
);
};
export default SearchResults;
Now imagine you have another query elsewhere in the code, and you need the same repository fields, but also want to include the URL and description. For that, you could do this, using the fragment and simply adding the new fields.
const SEARCH_QUERY = gql`
query myOrgRepos($queryString: String!) {
search(query: $queryString, type: REPOSITORY, first: 10) {
repositoryCount
edges {
node {
... on Repository {
...RepositoryInfo
url
description
}
}
}
}
}
${REPOSITORY_FRAGMENT}
`;
Result
With this simple example, you can now create your first project using GraphQL and utilize fragments to reuse code. As always, the complete code is here