DEV Community

Cover image for Creating a Reddit Clone Using React and GraphQL - 10
Rasika Gayan Gunarathna
Rasika Gayan Gunarathna

Posted on • Updated on • Originally published at rasikag.com

Creating a Reddit Clone Using React and GraphQL - 10

This post originally posted on my blog site.

Now we are adding email functions to the server. For this, we use NodeMailer. Use the below command to add it.


yarn add nodemailer
yarn add -D @types/nodemailer // this is for TS support

Enter fullscreen mode Exit fullscreen mode

We can grab the code from there example and paste it into a file. Now we are creating forgotPassword mutation. Before that, we need to add the email field to User entity.


@Field()
@Property({ type: "text", unique: true })
email!: string;

Enter fullscreen mode Exit fullscreen mode

Then we need to run the migration command to update the database. Then once we run the application we will get this error.


alter table "user" add column "email" text not null;

Enter fullscreen mode Exit fullscreen mode

This is because we are trying to add a not null column, but we have some users without an email address. For now, let’s delete all users.

Now we need to update user registration mutation because now email is a mandatory field. Add it to query builder also.

Now we are changing login mutation to match with this new change.

First, change in fineOne method that checks does the user passed username or email.

const user = await em.findOne(
  User,
  usernameOrEmail.includes("@")
    ? { email: usernameOrEmail }
    : { username: usernameOrEmail }
);
Enter fullscreen mode Exit fullscreen mode

Now there is a scenario that user can have @ in there username. Let’s handle that. With that validation, we create a separate util file called validateRegister . Then use that util function in register mutation.

...
const errors = validateRegister(options);
if (errors) {
  return {errors};
}
...

Enter fullscreen mode Exit fullscreen mode

Here you will see that we are returning the error array as an object. The returned object is matching with the return type.

Let’s change the front-end code to match with this back-end code.

We change the Login graphql query to get the usernameOrEmail first.


mutation Login($usernameOrEmail: String!, $password: String!) {
  login(usernameOrEmail: $usernameOrEmail, password: $password) {
... // rest of code is same

Enter fullscreen mode Exit fullscreen mode

Then change the Register graphql query.


mutation Register($options: UsernamePasswordInput!) {
register(options: $options){
... //rest of code is same

Enter fullscreen mode Exit fullscreen mode

Then add the email input field in to Register.tsx page.

After all these changes we are coming back to send emails for users that forgot password.

In the user.ts file inside the resolvers folder, we are adding forgotPassword mutation.


@Mutation(() => Boolean)
async forgotPassword(
  @Arg("email") email: string,
  @Ctx() { em }: RedditDbContext
) {
  const user = await em.findOne(User, {email});
  if (!user) {
    return true;
  }
  const token = "asdasdsadassadsadsadsadsad";
  await sendEmail(email,
    '<a href="http://localhost:3001/reset-password/${token}">click here to reset password</a>');

  return true;

}

Enter fullscreen mode Exit fullscreen mode

Within there, we first check that user email exists, if so we create a token and attached it to the reset-password link. We use uuid package for creating a unique user token to attach to the URL.


yarn add uuid ioredis

Enter fullscreen mode Exit fullscreen mode

Also, install the type support to it.


yarn add -D @types/uuid @types/ioredis

Enter fullscreen mode Exit fullscreen mode

Now we use ioredis and let’s make relevant changes in the index.ts file.

Also, we are passing redis in to context that later we can use it in the resolver. So now we need to add that to RedditDbContext type.

Then create a Redis object and use it in the RedisStore .


// inside the index.ts file
const redis = new Redis();
// use this redis object inside the RedisStore
...
store: new RedisStore({ client: redis, disableTouch: true }),
...

Enter fullscreen mode Exit fullscreen mode

Then inside the forgotPassword mutation use this redis object. There are a few things happening here.

First, we create a toke using uuid. Then we store this in Redis. After that, we set this token in the URL.

const token = v4();
await redis.set(
  FORGET_PASSWORD_PREFIX + token,
  user.id,
  "ex",
  1000 * 60 * 60 * 24 * 3
);
Enter fullscreen mode Exit fullscreen mode

I will wrap up this post from here. 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. This is an amazing tutorial and I highly recommend you to check that out.

Main image credit

Discussion (0)