Flink plugin that makes it possible to send email
npm install @flink-app/email-pluginA Flink plugin that provides multi-provider email sending capabilities with support for SendGrid, SMTP, and Flowmailer.
Install the plugin to your Flink app project:
``bash`
npm install @flink-app/email-plugin
The email plugin supports three different email providers through a unified client interface:
Configure the plugin with SendGrid for API-based email sending:
`typescript
import { FlinkApp, FlinkContext } from "@flink-app/flink";
import { emailPlugin, sendgridClient } from "@flink-app/email-plugin";
function start() {
new FlinkApp
name: "My app",
plugins: [
emailPlugin({
client: new sendgridClient({
apiKey: process.env.SENDGRID_API_KEY!
})
})
],
}).start();
}
`
SendGrid Client Options:
`typescript`
interface sendgridClientOptions {
apiKey: string; // Your SendGrid API key
}
Configure the plugin with any SMTP server:
`typescript
import { emailPlugin, smtpClient } from "@flink-app/email-plugin";
function start() {
new FlinkApp
name: "My app",
plugins: [
emailPlugin({
client: new smtpClient({
host: "smtp.example.com",
port: 587,
secure: false,
auth: {
user: "username",
pass: "password"
}
})
})
],
}).start();
}
`
SMTP Client Options:
`typescript`
interface smtpClientOptions {
host: string; // SMTP server hostname
port: number; // SMTP server port (usually 587 or 465)
secure: boolean; // true for port 465, false for other ports
auth?: {
user: string; // SMTP username
pass: string; // SMTP password
};
}
Configure the plugin with Flowmailer:
`typescript
import { emailPlugin, flowMailerClient } from "@flink-app/email-plugin";
function start() {
new FlinkApp
name: "My app",
plugins: [
emailPlugin({
client: new flowMailerClient({
client_id: process.env.FLOWMAILER_CLIENT_ID!,
client_secrect: process.env.FLOWMAILER_CLIENT_SECRET!,
account_id: process.env.FLOWMAILER_ACCOUNT_ID!
})
})
],
}).start();
}
`
Flowmailer Client Options:
`typescript`
interface flowmailerClientOptions {
client_id: string; // Flowmailer OAuth client ID
client_secrect: string; // Flowmailer OAuth client secret
account_id: string; // Flowmailer account ID
}
Add the plugin context to your app's context type (usually in Ctx.ts):
`typescript
import { FlinkContext } from "@flink-app/flink";
import { emailPluginContext } from "@flink-app/email-plugin";
export interface Ctx extends FlinkContext
// Your other context properties
}
`
Plugin Context Interface:
`typescript`
interface emailPluginContext {
emailPlugin: {
client: client; // Unified email client interface
};
}
Send a simple text email:
`typescript
import { Handler } from "@flink-app/flink";
import { Ctx } from "../Ctx";
const SendWelcomeEmail: Handler
await ctx.plugins.emailPlugin.client.send({
from: "noreply@example.com",
to: ["user@example.com"],
subject: "Welcome!",
text: "Welcome to our service!"
});
return { data: { success: true } };
};
export default SendWelcomeEmail;
`
Send an HTML email:
` This is an HTML emailtypescript`
await ctx.plugins.emailPlugin.client.send({
from: "noreply@example.com",
to: ["user@example.com"],
subject: "Newsletter",
html: "Hello
});
`typescript`
await ctx.plugins.emailPlugin.client.send({
from: "noreply@example.com",
to: ["user1@example.com", "user2@example.com", "user3@example.com"],
subject: "Team Update",
text: "Important team announcement"
});
` Your support ticket has been received.typescript`
await ctx.plugins.emailPlugin.client.send({
from: "noreply@example.com",
to: ["user@example.com"],
replyTo: "support@example.com",
bcc: ["admin@example.com"],
subject: "Support Ticket",
html: "
});
` Please find your invoice attached.typescript`
await ctx.plugins.emailPlugin.client.send({
from: "noreply@example.com",
to: ["user@example.com"],
subject: "Invoice",
html: "
attachments: [
{
filename: "invoice.pdf",
content: Buffer.from("..."), // File content as Buffer or string
contentType: "application/pdf"
},
{
filename: "logo.png",
path: "/path/to/logo.png" // Or provide a file path
}
]
});
The generic email type used by SMTP and basic implementations:
`typescript`
type email = {
from: string; // Sender email address
to: string[]; // Array of recipient email addresses
replyTo?: string; // Optional reply-to address
bcc?: string[]; // Optional blind carbon copy recipients
subject: string; // Email subject line
attachments?: Attachment[]; // Optional file attachments
} & ({ text: string } | { html: string }); // Either text or html is required
`typescript`
interface Attachment {
content?: string | Buffer | Readable; // File content
filename?: string | false; // Filename to display
path?: string | Url; // Path to file on disk
href?: string; // URL to fetch file from
httpHeaders?: string; // HTTP headers for href requests
contentType?: string; // MIME type
contentDisposition?: 'attachment' | 'inline'; // How to display
cid?: string; // Content ID for inline images
encoding?: string; // Encoding (e.g., 'base64')
headers?: { [key: string]: string | string[] }; // Custom headers
raw?: string | Buffer; // Raw MIME node content
}
SendGrid-specific email format (similar to generic but with SendGrid-specific attachment structure):
`typescript`
type emailSendgrid = {
from: string;
to: string[];
replyTo?: string;
bcc?: string[];
subject: string;
attachments?: {
content: string; // Base64 encoded string
filename: string;
type?: string;
disposition?: string;
}[];
} & ({ text: string } | { html: string });
Flowmailer-specific email format:
`typescript`
type emailFlowmailer = {
from: string;
to: string[];
replyTo?: string;
bcc?: string[];
subject: string;
text?: string;
html?: string;
attachments?: {
content: string; // Base64 encoded content
contentType: string; // MIME type
filename: string;
}[];
};
All email clients implement this unified interface:
`typescript`
interface client {
send(email: email | emailSendgrid | emailFlowmailer): Promise
}
The send method returns:true
- - Email sent successfullyfalse
- - Email sending failed (errors are logged to console)
Here's a complete example of a handler that sends different types of emails:
`typescript
import { Handler } from "@flink-app/flink";
import { Ctx } from "../Ctx";
interface SendEmailRequest {
type: "welcome" | "invoice" | "notification";
recipientEmail: string;
userName: string;
}
const SendEmail: Handler
const { type, recipientEmail, userName } = req.body;
try {
switch (type) {
case "welcome":
await ctx.plugins.emailPlugin.client.send({
from: "welcome@example.com",
to: [recipientEmail],
subject: Welcome ${userName}!,
html:
We're excited to have you on board.
});
break; case "invoice":
await ctx.plugins.emailPlugin.client.send({
from: "billing@example.com",
to: [recipientEmail],
replyTo: "support@example.com",
subject: "Your Invoice",
html:
Dear ${userName}, your invoice is attached.
,
attachments: [
{
filename: "invoice.pdf",
content: Buffer.from("..."), // Your PDF content
contentType: "application/pdf"
}
]
});
break; case "notification":
await ctx.plugins.emailPlugin.client.send({
from: "notifications@example.com",
to: [recipientEmail],
subject: "New Notification",
text:
Hi ${userName}, you have a new notification.
});
break;
} return { data: { success: true } };
} catch (error) {
console.error("Failed to send email:", error);
return { data: { success: false } };
}
};
export default SendEmail;
`Error Handling
All email clients handle errors internally and return
false on failure. Errors are logged to the console with JSON.stringify(ex). For production use, you may want to implement additional error logging or monitoring.`typescript
const success = await ctx.plugins.emailPlugin.client.send({
from: "noreply@example.com",
to: ["user@example.com"],
subject: "Test",
text: "Test email"
});if (!success) {
// Handle email sending failure
console.log("Failed to send email");
}
`Notes
- Either
text or html` must be provided (not both required, but at least one)