All Articles

Creating a Reddit Clone Using React and GraphQL - 10

inner peace

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

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;

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;

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 }
);

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};
}
...

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

Then change the Register graphql query.

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

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;

}

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

Also, install the type support to it.

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

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 }),
...

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

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

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

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 amazing tutorial and I highly recommend you to check that out.

Main image credit