DEV Community

Discussion on: How to implement email functionality with Node.js, React.js, Nodemailer, and OAuth2

Collapse
 
oicydwa profile image
Chris

This is the best tutorial I have found for reactjs and nodemailer. Thank you for this.

I went through the comments and I saw that you did a followup for someone with react-hook-form. I also am using react-hook-form, and it helped me adapt to my code. However, I am attempting to use my form to send files through nodemailer. No matter where I look, I cannot find any information on getting the file data from react-hook-form into nodemailer. Would you happen to have any insight on this?

Collapse
 
jlong4223 profile image
Jared Long • Edited

Hey not sure if you've already figured out the react portion of this question but with react-hook-form and files, all you'd have to do is add an input like this to your form:

<input type="file" {...register("file")} />
Enter fullscreen mode Exit fullscreen mode

If you throw a console.log of the data in your onSubmit, you should see the file info there. I'll follow up later on the nodemailer side!

Collapse
 
oicydwa profile image
Chris • Edited

I have that part figured out already. I can get the data to my API, but what I can't figure out is how to make the 'file' attach with nodemailer. In my browser I see the file being sent like this:
"documentsUpload: FileList
0: File {name: 'Email Attachment test.txt', lastModified: 1647400270450, lastModifiedDate: Tue Mar 15 2022 22:11:10 GMT-0500 (Central Daylight Time), webkitRelativePath: '', size: 20, …}
length: 1"
But the data sent is blank:
"documentsUpload: { '0': {} }"
And nothing comes through on the email attachments.

Thread Thread
 
jlong4223 profile image
Jared Long • Edited

Yeah I'm actually running into the same thing. I can see the file updating when I log it but when I send it, it goes through blank. Not super sure why that's happening yet. I'm going to try and see If throwing together a custom onChange for this file input helps. Not sure what's happening in the background of react-hook-form with sending files.

Thread Thread
 
oicydwa profile image
Chris • Edited

I actually got this figured out LATE last night. It was NOT a simple solution and was very picky.

Thread Thread
 
jlong4223 profile image
Jared Long

That’s awesome! You mind sharing? Really curious to see what you came up with

Thread Thread
 
oicydwa profile image
Chris • Edited

This works with multi-file attachments too.

<Input
   type="file"
   ref={register}
   multiple
   {...register('file')}
   name={'file'}
   onChange={handleChange}
/>
Enter fullscreen mode Exit fullscreen mode

My handleChange()

const handleChange = async (e) => {
    setValue('file', e.target.value.files, {shouldDirty: true});
  }
Enter fullscreen mode Exit fullscreen mode

So, in my API calling method I did:

const fileUpload = require("express-fileupload"); // I don't know why
  app.use(express.urlencoded({ extended: true })); // But this is required. I dropped it
  app.use(fileUpload({
    createParentPath: true                        // and it stopped working.
  }));

 const sendEmail = async (data) => {
    let form = document.querySelector('form');
    const formData = new FormData(form);

    const {file} = data;

    for (let [index, value] of Object.entries(file)){
      formData.append(value.name, file[index]);
    }

    axios({
      method: 'post',
      url: 'http://localhost:3001/send',
      data: formData,
    });
  }
Enter fullscreen mode Exit fullscreen mode

and on the backend:

app.post('/send', async (req, res) => {

  const docs = Object.keys(req.files).map(key => {
    return {
      filename: req.files[key].name,
      content: req.files[key].data
    }
  });

  let mailOptions = {
    from: 'whoever@whereever.net',
    to: 'to-mail@mail.go',
    subject: `Message from: Whoever`,
    html: '<html></html>',
    attachments: docs // <--- All you need here!!
  };

  transporter.sendMail(mailOptions, function (err, data) {
    if (err) {
      res.json({
        status: 'fail',
      });
    } else {
      console.log('== Message Sent ==');
      res.json({
        status: 'success',
      });
    }
  });
});
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
jlong4223 profile image
Jared Long

Nice! Solid work. Glad you figured it out and thanks for sharing

Thread Thread
 
oicydwa profile image
Chris

I edited it a dozen times to make sure I had it just right, for posterity. I really hope this helps someone later, because it was a pain to figure out, and searching yielded bits and pieces, but NOTHING on the backend portion.

Thread Thread
 
jlong4223 profile image
Jared Long

Yeah it definitely will. I had a hard time finding quality full stack nodemailer stuff before putting this article together with bits and pieces that I found that worked. What really surprised me is how you were able to receive the file data on the api without needing a middleware package like multer. I’ll for sure be trying this.

Thread Thread
 
oicydwa profile image
Chris

I had to update. I took a part out I thought was no longer needed, and I was wrong. I guess there is your middleware you were talking about.

Thread Thread
 
vipcode786 profile image
VipCode786

Thanks @chris and @jlong4223 for sharing this information.

Collapse
 
jlong4223 profile image
Jared Long • Edited

Hey, really happy to hear that this helped you! This gets a little more complex and I'll definitely expand on this specific question with react-hook-form when I get a little more time.

Until then, these resources may help:

You will most likely have to use FormData() to handling attaching a file on the frontend.

Didn't test these comments but this stackoverflow article about FormData & react looks promising on how to do it with a handleChange or handleSubmit function.

On the backend, you will will have to at least use a library like multer to handle those file requests.

I know Vue is very different but I have a Vue.js frontend project where I sent a picture file with FormData():
github.com/jlong4223/the-office-cl...

This is another API of mine where I use multer to handle file requests. In this case, pictures:
github.com/jlong4223/node-cloudina...

I will get back to this, but until this hopefully these help a little 😅