Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shaneosullivan/993d09a73bd8f5895e7513a49ac8d914 to your computer and use it in GitHub Desktop.
Save shaneosullivan/993d09a73bd8f5895e7513a49ac8d914 to your computer and use it in GitHub Desktop.
Forward an email with SendGrid and Formidable
import fs from "fs";
// You can use "formidable-serverless" if in a serverless environment like
// AWS Lamba or Google Cloud Functions
import formidable from "formidable";
import sendgridMail from "@sendgrid/mail";
import { AttachmentData } from "@sendgrid/helpers/classes/attachment";
sendgridMail.setApiKey(process.env.SENDGRID_API_KEY);
// See https://www.npmjs.com/package/formidable
interface FormidableFile {
// The size of the uploaded file in bytes.
// If the file is still being uploaded (see `'fileBegin'` event),
// this property says how many bytes of the file have been written to disk yet.
size: number;
// The path this file is being written to. You can modify this in the `'fileBegin'` event in
// case you are unhappy with the way formidable generates a temporary path for your files.
path: string;
// The name this file had according to the uploading client.
name: string | null;
// The mime type of this file, according to the uploading client.
type: string | null;
// A Date object (or `null`) containing the time this file was last written to.
// Mostly here for compatibility with the [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/).
lastModifiedDate: Date | null;
// If `options.hash` calculation was set, you can read the hex digest out of this var.
hash: string | "sha1" | "md5" | "sha256" | null;
}
// Hook this up to your inbound API however you like, e.g.
// using Express
function handleRequest(req, res) {
const form = new formidable.IncomingForm();
form.uploadDir = "/tmp/";
form.keepExtensions = true;
form.type = "multipart";
form.multiples = false;
form.parse(req, async (_err: any, fields, files) => {
handleFormidableResult(fields, files).then(() => {
// Send whatever you want
res.status(200);
res.json({ success: true });
res.end();
});
});
}
async function handleFormidableResult(fields, files) {
const { to, subject, from, html } = fields;
const fileKeys = Object.keys(files);
let attachments: Array<AttachmentData> = null;
let cleanupPromises = null;
if (fileKeys.length > 0) {
const filesInfo = fileKeys.map((key) => files[key]) as Array<
FormidableFile
>;
const attachmentPromises = filesInfo.map((fileInfo) => {
return new Promise((resolve, reject) => {
fs.readFile(fileInfo.path, (err, data) => {
if (err) {
reject(err);
return;
}
const attachment: AttachmentData = {
// Encode the buffer as a base64 encoded string
content: data.toString("base64"),
filename: fileInfo.name,
type: fileInfo.type,
disposition: "attachment",
contentId: fileInfo.hash,
};
resolve(attachment);
});
});
});
// Feel free to do better error handling, where if one file fails to
// read then you still attach others. Keeping it simple here.
attachments = (await Promise.all(attachmentPromises)) as Array<
AttachmentData
>;
// Delete all temp files.
cleanupPromises = filesInfo.map((fileInfo) => {
return new Promise((resolve, reject) => {
fs.unlink(fileInfo.path, () => {
resolve(null);
});
});
});
}
const emailBody = html || fields.text;
const message = {
from: "no-reply@example.com",
to,
subject,
html: emailBody,
envelope: {
from: "no-reply@example.com",
to,
},
attachments,
};
try {
await sendgridMail.send(message);
} catch (err) {
console.error("Sending email failed with error", err, " message ", message);
} finally {
if (cleanupPromises) {
await Promise.all(cleanupPromises);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment