Building a Hacker News Clone with GraphQL, Hasura and React - Part 2
Tags: graphql, tutorial, react
Published: 2019-12-06 13:24:14

---
title: Building a Hacker News Clone with GraphQL, Hasura and React - Part 2
published: true
date: 2019-09-03 12:45:06 UTC
tags: graphql, tutorial, reactjs
canonical_url: https://blog.hasura.io/hackernews-tutorial-using-graphql-react-hasura-part2/
---

![](https://blog.hasura.io/content/images/2019/09/Hackernews-clone--1--1.png)

_This tutorial was written by_ [Abhijeet Singh](https://github.com/abhi40308) _and published as part of  the [Hasura Technical Writer Program](https://blog.hasura.io/the-hasura-technical-writer-program/) - an initiative that supports authors who write guides and tutorials for the open source Hasura GraphQL Engine._

In [**part-1**](https://blog.hasura.io/hackernews-tutorial-using-graphql-react-hasura-part1/) of this series, we setup our backend and Auth0. In this part, we will setup our React app and connect it to our backend.

## React App Setup

We will start with user authentication. We will be using JWT (JSON  web tokens) for authentication. Let’s first create some basic header in our react app for showing login button.

Replace the contents of `styles/index.css` file as shown:

{% gist https://gist.github.com/abhi40308/fc5d0bfae8821dd5a0bbf579791d0544.js %}

These styles will be used throughout our app so you don’t have to worry about the styling.

### Setup Apollo GraphQL Client

Replace contents of `App.js` to use Apollo GraphQL client as shown below. ([See apollo github repository for more help](https://github.com/apollographql/react-apollo)).

{% gist https://gist.github.com/abhi40308/aad7e57db58891031d50ef35cf21ade9.js %}

In `line 15` change the `uri` to your GraphQL Endpoint on Hasura, which you can find on hasura console (remember where you created tables). Here we have imported the `header` component which we will implement now.

### Create header component and use react-router

We will use [`react-router`](https://reacttraining.com/react-router/web/guides/quick-start) to implement single-page application behaviour. Install `react-router` using:

```
$ npm install react-router-dom
```

> _React  Router, and dynamic, client-side routing, allows us to build a  single-page web application with navigation without the page refreshing  as the user navigates. React Router uses component structure to call  components, which display the appropriate information._

> _By  preventing a page refresh, and using Router or Link, the flash of a  white screen or blank page is prevented. This is one increasingly common  way of having a more seamless user experience. ([source](https://medium.com/@marcellamaki/a-brief-overview-of-react-router-and-client-side-routing-70eb420e8cde))_

For using `react-router` in our app, we have to wrap whole app in `BrowserRouter` It is a context provider for routing, which provides several `props` necessary for routing (like `match`, `location`, `history`). See [this](https://reactjs.org/docs/context.html) if you are unfamiliar with context. Replace  the contents of index.js:

{% gist https://gist.github.com/abhi40308/7d142b7f69be5faa7d15c5a077eca4fb.js %}

Next, we will create a `header` component for navigation within app. Create a `header.js` file in `components` directory. The contents of `header.js` should be:

{% gist https://gist.github.com/abhi40308/a102eb32b99fdc5860b10ead8a676499.js %}

Here we are creating a navbar similar to HackerNews navbar. Later we will add some routes to it for navigation. That’s it! We have  successfully created a header navbar and used `react-routes` in our app.

![](https://blog.hasura.io/content/images/2019/08/hacker_news_margin.png)<figcaption>Right now your app should look like this</figcaption>

## 

### Auth0 JWT integration with React App

Follow along with [Auth0-react-quickstart](https://auth0.com/docs/quickstart/spa/react/01-login#configure-auth0) guide as reference to include Auth0 in react app. Configure Auth0 client by setting `Allowed Callback URLs`, `Allowed Web Origins`, `Allowed Logout URLs` to [http://localhost:3000](http://localhost:3000) and add the custom API if you haven’t done already. Now install `auth0-spa-js` :

`$ npm install @auth0/auth0-spa-js`

Now we will include `react-auth0-wrapper` in our app, which is a set of custom [react-hooks](https://reactjs.org/docs/hooks-intro.html) that enable you to work with the Auth0 SDK. Create a new directory `src/auth` and add file `react-auth0-wrapper.js` populate it with code from [here](https://auth0.com/docs/quickstart/spa/react/01-login#install-the-auth0-react-wrapper).

Now add another file as `auth/auth_config.json` in `src/auth` . Populate `auth_config.json` with following code (change the values accordingly):

{% gist https://gist.github.com/abhi40308/8882bd82c757444233b97912cb946aa3.js %}

Now we are ready to include login functionality in our react app. Basically, we will be including a `login` button in header. This button will lead to login through Auth0 with redirect to our `localhost` once login/signup is completed. At the same time login/signup data will be updated in our `users` table in hasura backend due to the Auth0 `rules` we added earlier. Once the login is done, we will get the `accessToken` in JWT format using functions provided by Auth0 SDK in `App.js` . This `accessToken` will then be used as a authorization header in apollo client queries to  backend, thus every query which goes to backend will have authorization  header.

Firstly, change the contents of index.js to the following:

{% gist https://gist.github.com/abhi40308/9cf917ae180479230be534b72d783cb6.js %}

Here, we are using the `Auth0Provider` which is a context provider for Auth0 client. Any children components will now have access to the Auth0 client.

Having provided the Auth0 client to our app, we now replace the contents of `components/header.js` file to the following:

{% gist https://gist.github.com/abhi40308/6a09324b1455c4d203ce336f32eb1860.js %}

We are using `useState` hook(line 20) to set initial `accessToken` value to empty string. If the user is logged in, the token is fetched from the Aut0 SDK client using `getTokenSilently()` (line 31). Notice that this function returns a `Promise` and is asynchronous. This function attempts to return the current  access token. If the token is invalid, the token is refreshed silently  before being returned from the function. If the`try` block successfully gets executed, `accessToken` value is set to the JWT access-token from Auth0 (line 32).

The component re-renders when we get `accessToken` value. Thus after the async function has finished executing, we store the value of `accessToken` in state. The component re-renders and apollo-client gets the token value, thus re-rendering the whole `ApolloProvider` (context-provider) with new token value and the authentication header.

Once we have accessToken, we will use this to make requests to our backend using apollo client. See [apollo-docs](https://www.apollographql.com/docs/react/recipes/authentication/#header) for apollo authentication using headers. Basically, we are passing the `accessToken` as authorization header(line 50), in our apollo queries. This client is then used inside the `ApolloProvider` (context provider) to provide the child elements access to apollo client created here.

Now,  you should be able to login and logout of our app. Clear cache and  login. You must be asked to give access to your auth0 tenant by our  hasura backend. Give the access and you’re good to go.

![](https://blog.hasura.io/content/images/2019/08/auth0_1.png)

> Note : If you are facing errors, remember to not keep any dependency on `react-apollo` . `@apollo/react-hooks` must be used instead.

![](https://blog.hasura.io/content/images/2019/08/HN_heading_2.png)<figcaption>Now, our app looks like this</figcaption>

## Implementing Post List and Upvote/Points(realtime updates of upvotes)

We will implement a list of post and upvote post button. Create a new component `components/PostList.js` as :

{% gist https://gist.github.com/abhi40308/9a4d277523f7d1971c718e67a0d4e145.js %}

`POSTS_LIST` query(line 10) is being used to fetch details from `post` table in our database. We can fetch post description, url, id, user  details who created the post, and also the number of upvotes/points in  that post all in just one query. This is due to the object and array  relationships we created on our `post` table earlier. `useQuery` (line 31) is a custom [apollo-client react hook](https://www.apollographql.com/docs/react/api/react-hooks/). We get the query data in `data` object (line 31) which is then passed as a prop to the `Post` component, which we will implement now.

Create a new component `components/Post.js` as:

{% gist https://gist.github.com/abhi40308/2e9e99bdeecbeb3339e123dc2ca27a83.js %}

Two  things are happening here: we are displaying the post information using props passed by parent component, and we are writing a GraphQL mutation o upvote posts.

Firstly, we are getting post information in `props` (line 18). This is used to display post `description`, `url` , `points count` and `created_at` .

Secondly, we are making a mutation `UPVOTE_POST` using `useMutation` hook. For upvoting a post, we need current logged in user’s `userId` and `postId` . `userId` is present in `user` object we get from `useAuth0` hook (line19), which is being stored in `loggedUserId` variable (line 23). Once we get `userId` we will use it as variable in our mutation. See [apollo mutation](https://www.apollographql.com/docs/react/essentials/mutations/) if you are unfamiliar with passing variables in mutation. The other variable in `useMutation` hook, `refetchQueries` is used to refetch the specified query after the mutation results have  arrived. Thus, upvotes/points will be recorded and shown in real-time.

The mutation will insert a row in our `point` table, recording the upvote. Now, the same user cannot vote the given post again as (`post_id` + `user_id`) is the unique key for our point table, which we set earlier.

Finally, we need to import `PostList` component in `App.js`. Change your `App.js` file to make the following changes:

{% gist https://gist.github.com/abhi40308/88dbc28a8295f4eb08c0cbc213d55243.js %}

`Switch` is a part of react-router which is used to match components with their paths.

![](https://blog.hasura.io/content/images/2019/08/react_now.png)<figcaption>Now, our react-app looks like this</figcaption>

Try upvoting posts, and see the real-time updates in upvotes, thanks to `refetchQueries` . We haven’t yet implemented the user-profile and create-post  functionality, so the submit button and user-profile links do not work.  Next we will implement the same.

## Implementing Submit Post functionality

Create a new file as `components/SecuredRoute.js` as:

{% gist https://gist.github.com/abhi40308/84155ad7baf02ec5961b8fb8c89db2d6.js %}

This  will help us to create some secured routes which can only be accessed  if the user is logged-in. We will use secured routes while routing.  Using secured route, if someone tries to access the url’s without  logging-in, user will be redirected to login automatically.

Now, let’s create component `components/NewPost.js` as:

{% gist https://gist.github.com/abhi40308/f25d8ba2f0e40b55e4d58068556afdf0.js %}

This component will be used to submit a post. We have a `form` which has two input fields for `description` and `url`. We are using react state to store values of `description`, `url` and `error` (if mutation is not successful). The mutation accepts three variables which are being passed using `variables` parameter (line 34). If the mutation succeeds, we are using `refetchQueries` to refetch `POSTS_LISTS` query, thus implementing real-time updates of the list of post on the  home page. If the mutation is successful, data in our backend postgres  database gets updated with the new post data.

Let’s include route to `new-post` in our `App.js` :

{% gist https://gist.github.com/abhi40308/ea654219f606f680df6ae00119cbc53a.js %}

It is a secured route, so if we try to access `localhost:3000/new-post` without logging-in, we will be redirect to login screen.

Now,  you can try submitting a post and see that the home page will be  updated with the latest post. In case of any error in mutation, the  error message will be shown on screen.

![](https://blog.hasura.io/content/images/2019/08/posting_hn.png)<figcaption><code><a href="http://localhost:3000/new-post">http://localhost:3000/new-post</a></code> should look like this</figcaption>

## Implementing User Profile

Let’s now implement user-profile. Create a new component `components/Profile.js` as :

{% gist https://gist.github.com/abhi40308/5d188d58c9b556dbcc00389ae295520e.js %}

We are getting `user_id` as props which will be used to query our backend database for user info, for the given `user_id`. The data is then rendered in `return()` . The props (`user_id`) here is being passed in form of url, and we are using `props.match.params.id` to get that prop. These props are provided by the react-router `BrowserRouter` context provider, which is included in our `index.js `file.

We need to now update `App.js` to redirect user to profile section:

{% gist https://gist.github.com/abhi40308/6669615742ecd98b9bb486b632e592b2.js %}

Here, we are passing `id` as props (`path={“/user/:id”}`), which is accessed via `props.match.params.id` . This is a react-router way of passing prop. See [this](https://reacttraining.com/react-router/web/example/url-params) example for more details.

Finally,  we have our app ready with user-profiles section. You can navigate to  user-profiles, create new posts and see real-time updates of new-posts,  upvote posts and see real-time updates of upvotes.

You should now have a working Hacker News clone. Incase you'd like to reference it, the final code for this app is hosted [here](https://github.com/abhi40308/hackernews-clone). Visit the live demo [here](https://hackernews-1919.herokuapp.com/).

## About the author 

[Abhijeet Signh](https://github.com/abhi40308) is final year UG student in Computer  Science and Engineering from IIIT Kalyani. He has done work in Full  Stack Development, Android, Deep Learning, Machine Learning and NLP. He  actively takes part in competitive programming contests and has interest  in solving algorithmic problems. He is a startup enthusiast and plays  table tennis and guitar in spare time.