Creating a Reddit Clone Using React and GraphQL - 17
Tags: react, typescript, graphql, redditclone
Published: 2021-04-17 17:09:50

### This blog post originally posted on my blog site and you can find it [here](https://www.rasikag.com/posts/creating-a-reddit-clone-using-react-and-graphql-17).

From the last post, we are able to get posts successfully. It is better to show who wrote this post in the UI. So, let's change the post resolver. To do that we need to write a join query with `Post` and `User` entities.

Add the below code to `posts` resolver method to get the user data.

```javascript
// query parameters array
const replacement: any[] = [realLimitPlusOne];
if (cursor) {
  replacement.push(new Date(parseInt(cursor)));
}
// create a join query
const posts = await getConnection().query(
  // make attention on json_build_object method
  // this will form this result as expected return type
  `
    SELECT p.*,
    json_build_object(
    'id', u.id,
    'username', u.username,
    'email', u.email
    ) creator
    FROM post p
    INNER JOIN public.user u on u.id = p."creatorId"
    ${cursor ? ` WHERE  p."createdAt" < $2` : ""}
    ORDER BY p."createdAt" DESC
    LIMIT $1
  `,
  replacement
);
```

In the above code, any user can see any email address. So let's add a mask to the email field on `user` resolver. Only logged user can see own email address.

```javascript

@Resolver(User) // add the resolver type
export class UserResolver {
// add the field resolver
@FieldResolver(() => String)
email(@Root() user: User, @Ctx() { req }: RedditDbContext) {
// only logged user can see his/her email address.
if (req.session.userId === user.id) {
  return user.email;
}
return "";
}

```

Now, we are going to add the Upvote and Downvote functionality. Here we need to have `Many-to-Many` relationship with `User` and `Post` entities.

1. User can upvote or downvote many posts.
2. Post can have many votes from many users.

First, we are adding the `Upvote` entity.

```javascript
@ObjectType()
@Entity()
export class Upvote extends BaseEntity {
  @Column({ type: "int" })
  value: number;

  @PrimaryColumn()
  userId: number;

  @ManyToOne(() => User, (user) => user.upvotes)
  user: User;

  @PrimaryColumn()
  postId: number;

  @ManyToOne(() => Post, (post) => post.upvotes)
  post: Post;
}
```

Within this entity, you will that it has a relationship mappers to `User` and `Post` entities. So, now we need to add those mappers back to those entities.

```javascript

// inside the Post entity add below code
@OneToMany(() => Upvote, (upvote) => upvote.post)
upvotes: Upvote[];
// inside the User entity add below code
@OneToMany(() => Upvote, (upvote) => upvote.user)
upvotes: Upvote[];


```

Now, most importantly we need to update the entities in `index.ts `file.

```javascript

const conn = await createConnection({
  // ... removed for clarity
  entities: [Post, User, Upvote],
  // ... here we added Upvote entity
} as any);


```

Now we need to add the mutation to `post` resolver. Here we need to add a SQL transaction for this functionality. Because in here we need to update 2 tables. One is and a new record for `upvote` table. After that, we need to update the new count of upvotes in `post` table.

Here is `vote` mutation code.

```javascript
@Mutation(() => Boolean)
@UseMiddleware(isAuth)
async vote(
@Arg("postId", () => Int) postId: number,
@Arg("value", () => Int) value: number,
@Ctx() { req }: RedditDbContext
) {
  const isUpvote = value !== -1;
  const realValue = isUpvote ? 1 : -1;
  const { userId } = req.session;

  await Upvote.insert({
    userId,
    postId,
    value: realValue,
  });

  await getConnection().query(

    `
    START TRANSACTION;

    INSERT INTO upvote ("userId", "postId", value)

    VALUES (${userId}, ${postId}, ${realValue});

    UPDATE post p

    SET p.points = p.points + ${realValue}
    where p.id = ${postId};

    COMMIT;

    `
  );

  return true;

}

```

Now our backend code is complete for those two functionalities. Let’s change the front-end code.

First, we can change the `posts graphql query` to get the user name.

```javascript

// change the Posts query by adding this code lines
// inside the posts
creator {
  id
  username
}

```

Now run `yarn gen `command to generate the new changes for the `graphql.tsx` file.

Now add the below line to show the user name in the UI.

```javascript
<Box key={p.id} p={5} shadow="md" borderWidth="1px">
  <Heading fontSize="xl">{p.title}</Heading>
  // we can show the user name by adding below line.
  <Text>posted by {p.creator.username}</Text>
  <Text mt={4}>{p.textSnippet} </Text>
</Box>
```

And the last for this post we are making some changes to invalidate the cache when the user adds a new post. It will make sure that user will see new posts when the user redirects to the home page.

Let’s add this mutation to `createUrqlClient` method in the web app.

```javascript

Mutation: {
  // new mutation for create post
  createPost: (_result, args, cache, info) => {
    cache.invalidate("Query", "posts", {
    limit: 15,
  });
  },
// ... rest of the code

```

---

Thanks for reading this. If you have anything to ask regarding this please leave a comment here. Also, I wrote this according to my understanding. So if any point is wrong, don’t hesitate to correct me. I really appreciate you.
That’s for today friends. See you soon. Thank you.

**References:**

This article series based on the [Ben Award - Fullstack React GraphQL TypeScript Tutorial](https://www.youtube.com/watch?v=I6ypD7qv3Z8). This is amazing tutorial and I highly recommend you to check that out.

Main image [credit](https://www.pexels.com/photo/black-and-teal-mountain-540518/)