Nuxt server plugin for sending email.
Supports:
- nodemailer (local & most prod env's)
- worker-mailer (Cloudflare).
Exclude worker-mailer stuff if you're never deploying to Cloudflare.
Nuxt server plugin for sending email.
Supports:
Exclude worker-mailer stuff if you're never deploying to Cloudflare.
| NUXT_MAIL_SMTP = 'smtp.gmail.com' | |
| NUXT_MAIL_PORT = 465 | |
| NUXT_MAIL_USERNAME = '[email protected]' | |
| NUXT_MAIL_PASSWORD = 'xxxx xxxx xxxx xxxx' | |
| NUXT_MAIL_TARGET = '[email protected]' | |
| NUXT_MAIL_USE_WMAILER = false # set true when deploying to cloudflare |
| // FILE: server/api/mailer/index.post.ts | |
| // For now, direct custom type import | |
| import { Mail } from '~~/types/mail' | |
| export default defineEventHandler(async (event) => { | |
| try { | |
| if ( process.env.NUXT_MAIL_TARGET | |
| && process.env.NUXT_MAIL_USERNAME | |
| && process.env.NUXT_MAIL_PASSWORD | |
| && process.env.NUXT_MAIL_SMTP | |
| && process.env.NUXT_MAIL_PORT) { | |
| const body = await readBody(event) | |
| const finalBody:Mail = { ...body, from: process.env.NUXT_MAIL_USERNAME, to: process.env.NUXT_MAIL_TARGET } | |
| // CLOUDFLARE (use Worker-Mailer) | |
| if (process.env.NUXT_MAIL_USE_WMAILER === 'true') { | |
| const { mail_WorkerMailer } = await import('~~/server/utils/mail/mail_workermailer') | |
| return await mail_WorkerMailer(finalBody) | |
| .then( (response) => { | |
| return { message: "Mail successfully sent" } | |
| }) | |
| .catch( (error) => { | |
| throw 'Mail failure: worker-mailer' | |
| }) | |
| // ALL OTHER CASES (use Nodemailer) | |
| } else { | |
| const { mail_NodeMailer } = await import('~~/server/utils/mail/mail_nodemailer') | |
| return await mail_NodeMailer(finalBody) | |
| .then( (response) => { | |
| return { message: "Mail successfully sent" } | |
| }) | |
| .catch( (error) => { | |
| throw 'Mail failure: nodemailer' | |
| }) | |
| } | |
| } else { | |
| throw 'Env values missing' | |
| } | |
| } catch (error) { | |
| console.error("Nuxt Server API mailer - Error:", error) | |
| throw createError({ statusCode: 400, statusMessage: 'Mail failure: see server logs' }) | |
| } | |
| }) |
| // FILE: types/mail.d.ts | |
| export type Mail = { | |
| from: string | |
| to: string | |
| replyTo: string | |
| reply: string | |
| subject: string | |
| text: string | |
| html: html | |
| } |
| // FILE: server/utils/mail/mail_nodemailer.ts | |
| // For now, direct custom type import | |
| import { Mail } from '~~/types/mail' | |
| export const mail_NodeMailer = async (payload:Mail) => { | |
| try { | |
| const nodemailer = await import('nodemailer') | |
| const transporter = nodemailer.createTransport({ | |
| service: 'gmail', | |
| auth: { | |
| user: process.env.NUXT_MAIL_USERNAME, | |
| pass: process.env.NUXT_MAIL_PASSWORD | |
| } | |
| }) | |
| return await transporter.sendMail(payload, (error, info) => { | |
| if (error) { | |
| console.error("ERROR - mail_NodeMailer", error) | |
| throw error | |
| } else { | |
| return info | |
| } | |
| }) | |
| } catch (error) { | |
| throw "Problem with sending nodemailer" | |
| } | |
| } |
| // FILE: server/utils/mail/mail_workermailer.ts | |
| // For now, direct custom type import | |
| import { Mail } from '~~/types/mail' | |
| export const mail_WorkerMailer = async (payload:Mail) => { | |
| try { | |
| if ( process.env.NUXT_MAIL_TARGET | |
| && process.env.NUXT_MAIL_USERNAME | |
| && process.env.NUXT_MAIL_PASSWORD | |
| && process.env.NUXT_MAIL_SMTP | |
| && process.env.NUXT_MAIL_PORT) { | |
| const { WorkerMailer } = await import('worker-mailer') | |
| const mailer = await WorkerMailer.connect({ | |
| credentials: { | |
| username: process.env.NUXT_MAIL_USERNAME, | |
| password: process.env.NUXT_MAIL_PASSWORD, | |
| }, | |
| authType: 'plain', | |
| host: process.env.NUXT_MAIL_SMTP, | |
| port: parseInt(process.env.NUXT_MAIL_PORT!), | |
| secure: true, | |
| }) | |
| return await mailer.send(payload) | |
| } else { | |
| throw 'Env variables not found' | |
| } | |
| } catch (error) { | |
| console.error("ERROR - mail_WorkerMailer", error) | |
| throw "Problem with sending worker-mailer" | |
| } | |
| } |
| // FILE: app/components/myvuecomponent.vue | |
| // ... | |
| // NOTE, gmail smtp: | |
| // FAILS: "John Doe <[email protected]>" | |
| // WORKS: "John Doe [email protected]" | |
| const fromFinal = `${event.data.name} ${event.data.email}` | |
| const payloadFinal:Partial<Mail> = { | |
| // For now, specifying different "reply" keys; relevant services ignore the other | |
| // Nodemailer | |
| replyTo: fromFinal, | |
| // Cloudflare/worker-mailer | |
| reply: fromFinal, | |
| subject: event.data.subject, | |
| text: event.data.message // In theory, could also do "html: htmlcontent" | |
| } | |
| await $fetch('/api/mailer', { | |
| method: 'POST', | |
| body: payloadFinal | |
| }) | |
| .then( (response) => { | |
| console.log("Mailer api response is:", response) | |
| }) | |
| .catch( (error:any) => { | |
| console.error("ERROR: Message submission", error) | |
| }) |
| // nuxt.config.ts | |
| //... | |
| modules: ['@nuxt/content', '@nuxt/ui', "@nuxt/image", '@pinia/nuxt', '@nuxthub/core' ], | |
| // ... |
| // ... | |
| "nodemailer": "^6.10.0", | |
| "worker-mailer": "^1.1.1" | |
| // ... |