Building with Email/Password authentication 

Although Gadget provides a default email/password authentication method, there is a lot of flexibility to configure or build on top of the user emails sent through emails in the action context.

Custom email templates 

Whether you want to customize the copy and style of the emails you send on the sendVerifyEmail/sendResetPassword action or send any other transactional/marketing emails within your app, you can easily do so.

  1. Create a constant value representing the desired custom email template (CustomTemplate) and declare an expression containing an HTML 5 document.

  2. Structure this document however you want your email copy and style to be.

  3. Now to render your custom template, when sending an email (via emails.sendMail()) within the html parameter you will pass the custom template like below:

JavaScript
1export async function onSuccess({ params, record, logger, api, emails }) {
2 if (
3 !record.emailVerified &&
4 record.emailVerificationToken &&
5 params.user?.emailVerificationCode
6 ) {
7 const url = new URL("/verify-email", Config.appUrl);
8 url.searchParams.append("code", params.user?.emailVerificationCode);
9
10 // create your custom email template
11 const CustomTemplate = `
12 <!DOCTYPE html>
13 <html lang="en">
14 <head>
15 <meta charset="UTF-8">
16 <meta name="viewport" content="width=device-width, initial-scale=1.0">
17 <title>Email Verification</title>
18 </head>
19 <body style="font-family: Arial, sans-serif; background-color: #f7f7f7; margin: 0; padding: 50px;">
20 <div style="max-width: 600px; margin: 0 auto; background-color: #ffffff;">
21 <h1 style="font-size: 24px; margin-bottom: 20px;">Email Verification</h1>
22 <p>Click the button below to verify your email:</p>
23 <a href="<%= url %>" style="color: #ffffff; background-color: #3498db;">Click to Verify Email</a>
24 </div>
25 </body>
26 </html>`;
27
28 await emails.sendMail({
29 to: record.email,
30 subject: `Verify your email with ${Config.appName}`,
31 // Pass your custom template
32 // The default template is an EJS string
33 html: DefaultEmailTemplates.renderEmailTemplate(CustomTemplate, {
34 url: url.toString(),
35 }),
36 });
37 }
38}

Important to note when passing the CustomTemplate through emails.sendMail():

  • There is no text parameter to pass as that is replaced by the content within your CustomTemplate

Email parameters 

The sendMail function takes in a MailData object. The MailData object has the following params:

NameTypeDescription
tostring | Address | Address[]The address(es) that the email will be sent to. The to param can be an email address or Address object, or an array of either. For example: [email protected] or { address: "[email protected]", name: "John Smith" }
fromstring | AddressThe from address displayed to users as to who the email came from. This param is optional. If not provided, the from address will be set to noreply@{your-app-slug}.gadget.app.
senderstring | AddressThe sender address used to identify the agent responsible for the actual transmission of the email. Can only be set when using a custom transport.
htmlstringThe HTML body of the email.
attachmentsAttachment[]An array of files to be attached to the email. The attachment is an object with either a path param which represents the string path to the file, or a content param which represents the Buffer or string raw data of the file, and an optional filename param which represents the name of the file. If a filename is not provided, the original name of the file will be used.

When using GadgetMailer, users are restricted from sending emails from anything other than their app's approved subdomain such as: { anything }@your-app-slug.gadget.app. No restrictions are set when users are using their own custom transport.

Adding attachments to emails with GadgetMailer 

Let's say we have a model called csvFile with a field link which is a file type and we want to dynamically create files to store in our model. To attach a generated csv file to an email we can do the following:

example of sending an email with an attachment in an action
JavaScript
1import { Base64 } from "base64-string";
2import { DefaultEmailTemplates } from "gadget-server";
3
4export async function onSuccess({ params, record, logger, api, session, emails }) {
5 // 1. Create CSV content
6 const csvContent = "name,age\njohn,32";
7
8 // 2. Encode the CSV content
9 const enc = new Base64();
10 const b64 = enc.urlEncode(csvContent);
11
12 // 3. Set the file name
13 const csvFileName = `test.csv`;
14
15 // 4. Create the file
16 const file = await api.csvFile.create({
17 link: {
18 base64: b64,
19 fileName: csvFileName,
20 },
21 });
22
23 const CustomTemplate = `
24 <!DOCTYPE html>
25 <html lang="en">
26 <head>
27 <meta charset="UTF-8">
28 <meta name="viewport" content="width=device-width, initial-scale=1.0">
29 <title>Email with CSV</title>
30 </head>
31 <body style="font-family: Arial, sans-serif; background-color: #f7f7f7; margin: 0; padding: 50px;">
32 <div style="max-width: 600px; margin: 0 auto; background-color: #ffffff;">
33 <h1 style="font-size: 24px; margin-bottom: 20px;">Sending a CSV</h1>
34 <p>See attachment</p>
35 </div>
36 </body>
37 </html>`;
38
39 // 5. Send the email with the attachment
40 await emails.sendMail({
42 subject: `Welcome`,
43 html: DefaultEmailTemplates.renderEmailTemplate(CustomTemplate),
44 attachments: [
45 {
46 filename: csvFileName,
47 path: file.link.url,
48 },
49 ],
50 });
51}

We can also fetch the file from a URL and pass it in as an attachment:

example of sending an email with attachment content in an action
JavaScript
1import pdf from "pdf-parse";
2import { DefaultEmailTemplates } from "gadget-server";
3
4export async function onSuccess({ params, record, logger, api, session, emails }) {
5 // 1. Fetch the PDF file from a URL
6 const url = "https://example.com/example.pdf";
7
8 async function getPdfBuffer(url) {
9 try {
10 const response = await fetch(url);
11 if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
12 const arrayBuffer = await response.arrayBuffer();
13 return Buffer.from(arrayBuffer);
14 } catch (error) {
15 console.error("Error fetching PDF:", error);
16 throw error;
17 }
18 }
19
20 const pdfBuffer = await getPdfBuffer();
21
22 const CustomTemplate = `
23 <!DOCTYPE html>
24 <html lang="en">
25 <head>
26 <meta charset="UTF-8">
27 <meta name="viewport" content="width=device-width, initial-scale=1.0">
28 <title>Email with PDF</title>
29 </head>
30 <body style="font-family: Arial, sans-serif; background-color: #f7f7f7; margin: 0; padding: 50px;">
31 <div style="max-width: 600px; margin: 0 auto; background-color: #ffffff;">
32 <h1 style="font-size: 24px; margin-bottom: 20px;">Sending a PDF</h1>
33 <p>See attachment</p>
34 </div>
35 </body>
36 </html>`;
37
38 // 2. Send the email with the attachment
39 await emails.sendMail({
41 subject: `Welcome`,
42 html: DefaultEmailTemplates.renderEmailTemplate(CustomTemplate),
43 attachments: [
44 {
45 filename: "myPdf.pdf",
46 content: pdfBuffer,
47 },
48 ],
49 });
50}

Setting up an external transporter 

If you decide to use an external email transport like Amazon SES, SendGrid, etc, you do have the ability to configure that within Gadget.

  1. Create a boot file and declare a value representing the new transport. With this value you'll run emails.setTransport(), for those familiar with Nodemailer this is exactly how createTransport() operates

  2. The parameters passed through setTransport() will be entirely dependent upon the type of transport you wish to use with your app. Refer to the Nodemailer or external transport service documentation for more details on parameters passed.

  3. For example if we wanted to use Mandrill (Mailchimp's transactional email service) as our email transporter we would do the following below:

JavaScript
1import { Server, emails } from "gadget-server";
2// import emails within the file
3
4// Create the transporter object
5const transporter = {
6 host: "smtp.mandrillapp.com",
7 port: 587,
8 auth: {
9 user: "YOUR_MANDRILL_USERNAME",
10 pass: "YOUR_MANDRILL_API_KEY",
11 },
12};
13
14// Set the transporter to the new configuration
15emails.setTransport(transporter);

In summary to configure an external transporter:

  1. Create a boot file

  2. Declare the transporter (transport) along with any parameters in a separate code file or within the action.

  3. Use emails.setTransport() and pass the transport value.