Blog

How to engage GraphQL, .Net and React together – Part IV: React, GraphQL and the amazing Apollo Client

Ulrike Kulzer

Hello! 🙂 I’m Ulrike, a media informatics student in my master’s degree and currently an intern at Centigrade. During my internship one of my tasks was finishing the VedaVersum app together with Mikhail. We wanted to create the application using .NET, GraphQL and React as a knowledge base where team members can log in and write, edit and delete articles (see first blog article). In this part of the series, I’d like to share my approach to link the frontend with the backend with you and briefly report how I fared as a React and GraphQL newbie.

Review: .NET, GraphQL und MongoDB

Review: .NET, GraphQL and MongoDB

In the first blog article, Mikhail explained how to create a GraphQL API with .Net. He chose Hot Chocolate from ChilliCream as GraphQL server to support it. Banana Cake Pop, a tool for checking GraphQL queries provided by ChilliCream, will also be of use later, when we test our queries in isolation from the frontend. In the follow-up article, we focused entirely on backend authentication using GitLab as an example, and in the latest article we added MongoDB to the backend for ensuring data persistence.

In this article, we will write GraphQL queries in JavaScript and integrate them in our React frontend using Apollo Client Library.

An important note about the code on Github: While we are writing this series of articles for our blog, we have been working continuously on VedaVersum. Accordingly, as it is usually the case when developing software ?, there are always major and minor restructurings and renames. For example, the VedaVersumCard has now become the VedaVersumArticle.

Frontend with React, Tailwind CSS and Apollo Client

We use the following technologies for the implementation of the frontend:

Logos React, tailwind, apollo

As described in detail in the first blog article, we chose React because it is more lightweight (User Interface Library) than Angular (User Interface Framework).

Tailwind CSS is a CSS framework that follows the “utility first” approach. A blog article from frontstuff.io inspired us to look into this topic. We chose TailwindCSS for the following reasons: We didn’t want anything heavyweight like Bootstrap for our small application. In addition, the requirements for our application and therefore its design and layout are continuously revised. With Tailwind CSS it is possible to implement these changes quickly right in HTML.
Since a detailed discussion of the framework and “utility first” would go beyond the scope of this blog article, I’ll only briefly discuss it for completeness.

Apollo Client is an open-source JavaScript library for data management with GraphQL. We chose this library for our project because Apollo Client is widely used, well documented and handles a lot of data request logic on its own. For example, errors in data retrieval don’t have to be implemented manually. Apollo Client sends them along directly and we only have to take care of what should happen in case of an error.

VedaVersum – A React application communicating with the GraphQL backend via Apollo Client

After reviewing the requirements for the VedaVersum app for the publication of this article, the first “Minimum Viable Product” of the application contains the following features:

  • On the home page, a list of all existing articles is shown to the users. As a user I can filter this list by the articles I have created. For this we need GraphQL queries.
  • I can create articles, delete articles created by me and edit articles written by me as well as articles written by other users. For this we need GraphQL mutations.
  • If other users create new articles or edit existing articles while I am logged in, I will be notified by the system. For this we need a GraphQL subscription.

Initializing Apollo Client in React

The Apollo Client Docs describe very well how to integrate Apollo Client into a React project. However, since we implemented authentication in the previous articles, we need a bit more code for the initialization than the “Get started” from the docs. Accordingly, we outsourced this code to a separate file called “ApolloSetup.ts”. We import the Apollo client created in “ApolloSetup.ts” into the “index.tsx” file and put it around our application. The framework then looks like this:

ApolloSetup.ts

export const apolloClient = new ApolloClient({
  ...
});

 


index.tsx

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);

root.render(
  <ApolloProvider client={apolloClient}>
    ...
  </ApolloProvider>,
);


The basic implementations of GraphQL Queries, Mutations and Subscriptions are very similar to each other. Hence, in the following section I will describe GraphQL queries a bit more in-depth and keep the explanation short in the other sections.

How to write GraphQL queries and use them in React via Apollo Client

GraphQL queries are utilized to read data from the database, which is why we use them often in our application. On the one hand, these queries can be called directly, for example to request all the items stored in the database. On the other hand, we can pass them parameters, e.g. to filter all stored articles by the articles of the logged-in user.

Writing GraphQL queries in JavaScript

To define a query as a GraphQL query in JavaScript, you have to use the keyword gql with backticks ( ` ` ). Between the backticks you write the query and store the entire expression in a constant:

export const ALL_ARTICLES_QUERY = gql`
   ...
`;

The GraphQL query itself looks like this: Introduced by the keyword query and the name of the query, you put the database query in curly brackets. It is important that the GraphQL query is named the same as the variable in which the result of the query is stored in the backend. Otherwise, the result cannot be assigned to the database query, and you will get an error message. How exactly to write a GraphQL query is described very well in the GraphQL docs.

The completed query, which reads all information of all articles stored in the database, looks like this:

export const ALL_ARTICLES_QUERY = gql`
  query GetAllArticles {
    allArticles {
      id
      title
      content
      created
      userCreated
      relatedArticleIds
      userUpdated
      updatedAt
      accessCounter
    }
  }
`;

To make sure that the GraphQL query itself is executable and that the backend returns the desired result, we can test the query in Banana Cake Pop without the frontend. To do this, we simply copy the pure GraphQL query from the code and paste it into Banana Cake Pop (see also first blog article). If there is a syntax error in the query, the program fortunately points it out immediately. If the query is correct, Banana Cake Pop shows us the result from the backend:

Banana Cake Pop Ergebnis Backend

Using GraphQL queries with Apollo Client in React

Now, to execute the GraphQL query in our React application, we first need to import the React hook useQuery from Apollo Client. Then we create a constant where the result of our GraphQL Query will be stored. We know that database requests cannot always be successfully resolved, for example if the server where the data is stored is currently unavailable. Likewise, it may take longer to process the query. To enable good usability, we want to cover both cases, and therefore choose the following notation:

const {
    error: errorAllArticles,
    data: allArticlesData,
    loading: loadingAllArticles,
  } = ...

If the query is successfully resolved, the data is stored in the data variable (here renamed to “allArticlesData”). Later we can process the result in the JavaScript code or check it in the HTML section and display the corresponding UI component.

But back to the query – to call the query, we now assign our constant the following expression as “value”:

const { ... } = useQuery(ALL_ARTICLES_QUERY);

In round brackets we provide useQuery with the parameters the query needs – in this case only the name of the GraphQL query we want to execute. The GraphQL query must be imported for this.

In general, you can pass other parameters to useQuery besides the name. These parameters are “collected” in an object and sent with the name of the GraphQL query separated by a comma. An example of this is fetchPolicy, whose value specifies whether and how data is cached. For GraphQL queries that expect parameters, the variables are also listed here. But we’ll take a closer look at that below:

Writing GraphQL queries with parameters in JavaScript

GraphQL queries can expect parameters. We can take advantage of this e.g. if we want to read an article. Users do not always take the detour via the start page but save an article as a bookmark in the browser for example. In this case we don’t want to request all articles from the database but get directly only the information of the selected article. For this we write a GraphQL query that gets the ID of an article and then returns all database entries for exactly this article – provided the article exists. The whole query looks like this in the end:

export const ARTICLE_BY_ID_QUERY = gql`
  query GetArticle($articleId: String!) {
    article(articleId: $articleId) {
      # database table fields
    }
  }
`;

The GraphQL query expects an article ID of the type string, that is passed to the backend in the next line. It is important to note that the names of the parameters received by the GraphQL query must start with a dollar sign and are passed on in the same way.

For testing, we can also execute this GraphQL query in Banana Cake Pop with the article ID of one of our saved articles. We don’t have to pass the article ID to the GraphQL query, but give it directly to the backend function:

Query With Parameters

To successfully execute this GraphQL query in the frontend, we need to add an object with the field “variables” to the parameters of useQuery. This field also gets an object where we would list all the parameters if there were more than one. It is important that the fields of the “variables” object are named the same as the parameters the query expects. If not, GraphQL cannot process the values. In this case, we only have the article ID as a parameter, which we previously stored as “currentArticleId”. From the useQuery structure, the rest remains the same:

const { error, data, loading } = useQuery(ARTICLE_BY_ID_QUERY, {
    variables: { articleId: currentArticleId },
  });

How to write GraphQL mutations and use them in React via Apollo Client 

GraphQL mutations are used to add, modify or delete data in the database. Since our users can do just that, we need a mutation for each action. The mutations have different parameters, but otherwise have the same structure. We take a look at this by the “UpdateArticle” mutation.

Writing GraphQL mutations in JavaScript

The basic structure of a GraphQL mutation in JavaScript is the same as for a GraphQL query:

export const UPDATE_ARTICLE_MUTATION = gql`
  mutation UpdateArticle($articleId: String!, $articleTitle: String!, $articleContent: String!) {
    articleAction(action: UPDATE, articleId: $articleId, title: $articleTitle, content: $articleContent) {
      # database table fields
    }
  }
`;

There are two small differences: Since we have created a mutation, we also use mutation as a keyword instead of query. Furthermore, we have written a function in the backend that handles all article mutations (create – “CREATE”, edit – “UPDATE”, delete – “DELETE”). Accordingly, when calling this function, we need to pass the “action” as an additional parameter – for “UpdateArticle”, of course “UPDATE”.

Mutations can be tested in Banana Cake Pop like GraphQL queries with parameters:

Mutation Test Banana Cake Pop

Using GraphQL mutations with Apollo Client in React

GraphQL mutations are included in React again via a React hook from Apollo Client, this time useMutation. The basic structure is the same as a query. But in detail they differ:

const [updateArticle] = useMutation(UPDATE_ARTICLE_MUTATION, {
    variables: { articleId: articleData?.id, articleTitle: title, articleContent: content },
    onError: error => {
      // error handling
    },
    onCompleted: data => {
      // actions when successful
    },
  });

The various responses (error, data, loading) are not specified in the constant, but in the mutation itself. They are listed after the name of the mutation and the parameters as onError or onCompleted with the corresponding statements.

Later in the code the mutation can be called like a normal function:

if (editorSettings.type === 'create') {
   insertArticle();
} else if (editorSettings.type === 'edit') {
   updateArticle();
}

How to write GraphQL subscriptions and use them in React via Apollo Client

GraphQL Subscriptions are used when a client needs to receive messages from the server in real time. As Mikhail mentioned in the first blog article, subscriptions are one of THE features of GraphQL. What I’ve written so far about GraphQL queries and mutations at first sounds a lot like standard REST GET and POST commands. GraphQL implements them with normal HTTP methods. However, subscriptions are a different story: Even if you don’t notice anything on the surface, for subscriptions GraphQL works with websockets. That’s why we must integrate the corresponding library for Apollo Client into our project later on. The use case for a subscription at VedaVersum are the notifications that should be triggered on the server side: If someone creates a new article or edits an existing article, all users that are logged in get a notification and can view the corresponding articles directly.

Writing GraphQL subscriptions in JavaScript

A GraphQL subscription in JavaScript is structured in the same way as a GraphQL query without parameters:

export const ARTICLE_CHANGED_SUBSCRIPTION = gql`
  subscription OnArticleChanged {
    articleChanged {
      action
      vedaVersumArticle {
        # database table fields
      }
    }
  }
`;

As the keyword here, we write subscription instead of query.

Testing a subscription in Banana Cake Pop is a bit more complicated than testing a query or mutation:

  1. We start the subscription in a tab. We can see if the subscription is active by the button we use to execute GraphQL statements. Where before it said “Run” with an arrow icon, now it should say “Cancel” and show a loading circle.
  2. We switch to a second tab, authenticate and edit an article.
  3. We go back to the first tab. There we should see in the right field the changed article with the “action” that was executed.

GraphQL Subscriptions mit Apollo Client in React verwenden

Using GraphQL subscriptions with Apollo Client in React

Before we can use our subscription in React, we first need a subscription library. Even though it is not recommended in the Apollo Docs, we need to take the deprecated subscription-transport-ws library. The reason for this is that Hot Chocolate, our GraphQL server, does not yet support the successor library graphql-ws. How exactly to integrate the library into your project is described in detail at the end of the Apollo Docs entry.

If you have successfully set up the library, you can use the React hook useSubscription in the code just like useQuery without parameters:

const { data: subscriptionData } = useSubscription(ARTICLE_CHANGED_SUBSCRIPTION);

Now the client gets a notification from the server every time a new article is created, or an already existing article is edited or deleted. So far, so good. Only this is of no use to us yet. The idea was that users get a notification when the article table in the database has changed. To achieve this, we need to use the React hook useEffect to monitor the data that the subscription sends (here “subscriptionData”). As soon as they change, i.e. the client receives a “new” changed article from the server, the statements inside the useEffect hook are executed. In the code, it looks like this (simplified):

useEffect(() => {
    if (subscriptionData) {
      // instructions what to do every time
      // a new change is sent by the server
    }
  }, [subscriptionData]);

In the dependency array at the end, we will later write all variables or functions on which the statements inside the curly brackets depend. Now we just program the user notifications in the if statement, then this feature is ready, too 🙂

Conclusion and outlook

In this blog article, I told you about my approach and experience on how to connect a React frontend to a GraphQL backend using Apollo Client. We have learned what GraphQL queries, mutations, and subscriptions are used for and how to implement them in JavaScript in the React frontend. This also included how to call these GraphQL queries using Apollo Client in React and how to process their result in the code.

Personally, I learned a lot in the project because React, GraphQL, and “utility-first” are all areas I’ve had little to no exposure to. In the following section, I give a brief conclusion for each technology used.

I have found React to be not particularly beginner-friendly – in part because I originally came from Vue.js, which is very different from React. As a result, I had problems understanding the concepts compared to getting started (in parallel) with Angular. Also, it was sometimes difficult to find solutions to problems or error messages because many forum posts, etc. were still written using the older class syntax instead of the new function syntax.

I could only make friends with Tailwind CSS to a limited extent. I understand the idea of writing less CSS declarations, which then also repeat less often in the stylesheets. However, if you solve the complete styling via the utility classes, you get very quickly a lot of class names, which in my opinion make the code confusing. For example, the following line in the code describes the styling for a button:

className="hover:cursor-pointer outline outline-4 outline-transparent text-white text-base text-center rounded-lg font-white bg-primary py-2 px-3 mr-4 hover:outline-primary-light active:bg-primary-dark disabled:bg-primary-dark disabled:outline-transparent disabled:cursor-auto"

Using Apollo Client was pretty relaxed, since the library catches and processes everything (load status, errors, etc.), so we could continue working directly with the responses of the queries. In addition, you can define many settings, e.g. how caching is done, in a way that suits the project best. Another big plus is that even beginners can integrate Apollo Client into projects very quickly and work with it directly.

With GraphQL it was very interesting to use something other than SQL queries. Especially because GraphQL queries can be a lot of typing, but you can see exactly what you get back from the query as result. In this project, we unfortunately couldn’t present all features of GraphQL, e.g. server-side data transformations like unit conversion, but it was still exciting to deal with it.

So, now we have a runnable web app, with minimal but working features. To use the application internally, all that’s really missing is a deployment. We decided to deploy VedaVersum to GitLab pages for now. Our DevOps engineers will show you how to do that in the next article of this series ?

Thanks for reading and happy coding!

Want to know more about our services, products or our UX process?
We are looking forward to hearing from you.

Client Relationship Manager
+49 681 959 3110

Before sending your request, please confirm that we may contact you by clicking in the checkbox above.