#Nodemailer integration
1 messages · Page 1 of 1 (latest)
Hi @onyx notch. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
Nodemailer needs to run in Convex’s Node.js runtime, and there are a couple of common pitfalls that match what you’re seeing.
From the docs and Discord threads, here’s what is supported and how to structure it:
-
Put Nodemailer in a dedicated Node action file
-
Create a file in
convex/that contains only actions and starts with:"use node"; import { action } from "./_generated/server"; import nodemailer from "nodemailer"; export const sendEmail = action({ args: { // your args here }, handler: async (ctx, args) => { // use nodemailer here }, }); -
Files with
"use node"are bundled for the Node.js runtime and are the only place you can safely use Node‑specific libraries like Nodemailer. [Actions runtime; Node runtime]
-
-
Do not import Node files from non‑Node files
-
Any file without
"use node"must not import from a file with"use node". That will drag Node‑only imports into the Convex runtime and cause “missing node” /node:fs/node:async_hooksstyle errors. [Node runtime; ESLint rule] -
Instead, call the Node action via
ctx.runActionfrom a normal action / httpAction:// convex/http.ts (no "use node") import { httpAction } from "./_generated/server"; import { internal } from "./_generated/api";
-
export const sendEmailHttp = httpAction(async (ctx, request) => {
// parse body, etc.
await ctx.runAction(internal.mail.sendEmail, { /* args */ });
return new Response(null, { status: 204 });
});
```
HTTP actions run in the Convex runtime but **can call actions** (including Node actions). [[HTTP limits](<https://docs.convex.dev/functions/http-actions#limits>)]
-
If you see bundling errors about Node APIs even in a
"use node"file- That usually means some code that imports Nodemailer (or a helper that imports it) is still being pulled into a non‑Node file (e.g. shared
utils.tsimported from both sides). Make sure:- All Nodemailer imports live only in
"use node"files. - Any helper that touches Nodemailer also has
"use node"and is not imported from Convex‑runtime files. [Node runtime]
- All Nodemailer imports live only in
- That usually means some code that imports Nodemailer (or a helper that imports it) is still being pulled into a non‑Node file (e.g. shared
-
External packages (optional)
-
If Nodemailer’s dependencies cause bundling issues (dynamic imports, large size), you can mark it as an external package so it’s not bundled and is loaded at runtime in Node:
{ "node": { "externalPackages": ["nodemailer"] } }This only applies to Node actions. [External packages]
-