Sending email with JavaScript

JavaScript sends email from a server-side runtime using an SMTP client such as Nodemailer or an HTTPS request to an email API.

Browsers block raw TCP connections to mail servers. Putting an API key or SMTP password in frontend code also exposes your credentials to anyone who opens developer tools.

Why can't the browser send email?

Browsers do not expose raw TCP sockets to JavaScript, so client-side code cannot open the SMTP connection a mail server requires. Calling an HTTP email API directly from the browser also exposes your API key in the page source and network tab. The browser collects the form input and posts it to your backend; the backend sends the email.

How do you send from a Node backend with SMTP?

Nodemailer is the standard library for SMTP in Node. You configure a transport once, then call sendMail:

import nodemailer from "nodemailer";

const transporter = nodemailer.createTransport({
  host: "smtp.example.com",
  port: 587,
  secure: false,
  auth: {
    user: process.env.SMTP_USER,
    pass: process.env.SMTP_PASS,
  },
});

await transporter.sendMail({
  from: "you@yourdomain.com",
  to: "recipient@example.org",
  subject: "Your receipt",
  text: "Thanks for your order.",
  html: "<p>Thanks for your order.</p>",
});

This works well on a long-running server. Nodemailer also supports file attachments and local SMTP testing.

How do you send with an HTTP email API?

Your backend can also send email by posting JSON to an HTTP email API with fetch. Because this uses standard HTTPS instead of raw TCP sockets, it works on both long-running servers and serverless runtimes:

const res = await fetch("https://us1.platform.bird.com/v1/email/messages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BIRD_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    from: "onboarding@messagebird.dev",
    to: ["delivered@messagebird.dev"],
    subject: "Hello from JavaScript",
    html: "<p>It works.</p>",
  }),
});

if (!res.ok) {
  throw new Error(`Send failed: ${res.status}`);
}

Match the host to your workspace region (us1.platform.bird.com for bk_us1_ keys, eu1.platform.bird.com for bk_eu1_ keys). Keep the API key in a server-side environment variable so it never ships to the client.

Send it with Bird

To send from Node.js or TypeScript with typed responses and automatic retries, pass BIRD_API_KEY to the Bird TypeScript SDK:

import { BirdClient } from "@messagebird/sdk";

const bird = new BirdClient({ apiKey: process.env.BIRD_API_KEY! });

const { data, error } = await bird.email
  .send({
    from: "onboarding@messagebird.dev",
    to: ["delivered@messagebird.dev"],
    subject: "Hello from Node",
    html: "<p>It works.</p>",
  })
  .safe();

The onboarding@messagebird.dev sender and delivered@messagebird.dev sandbox recipient verify your setup before you configure a custom domain. If you build email bodies as React components, React Email renders the component to the HTML string passed in html.

Build on the same network.

A test API key is yours immediately. Production unlocks when you add a payment method and verify a sender.

Start building with Bird in minutes.