W
Wasp4mo ago
Kynetix

Setting up email sending functionality

I'm trying to set up a simple functionality for sending email notifications. Basically, from the frontend, I want to send a notification when an action occurs. My approach was to create an api function called sendNotifications(), add it to main.wasp, then call it from the frontend as await sendNotifications(to,from,subject,message). The thing is that I need to retrieve some data from the database to add to the body of the email in the backend, so the sendNotification() function should include calls to the db (either context.entity.TableName.... or by calling the 'operational' functions). Should the sendNotification() be in the operations file, or as an api call? Please show me an example of the best way to implement this
6 Replies
kapa.ai
kapa.ai4mo ago
Hi @Kynetix. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To implement email notification functionality in Wasp, the recommended approach is to use an Action (not an API route) for your sendNotification logic. Actions in Wasp are designed for server-side business logic, can access the database, and can be called directly from the frontend using the generated client API. This fits your use case perfectly: you want to trigger an email from the frontend, but the backend needs to fetch data from the database before sending the email. Here's how you can implement this: 1. Declare the Action in main.wasp
action sendNotification {
fn: import { sendNotification } from "@src/server/actions.js"
entities: [User, Notification] // Add any entities you need to access
}
action sendNotification {
fn: import { sendNotification } from "@src/server/actions.js"
entities: [User, Notification] // Add any entities you need to access
}
You can adjust the entities list to match the tables you need to access. 2. Implement the Action in NodeJS In src/server/actions.js:
import { emailSender } from "wasp/server/email";

export const sendNotification = async ({ to, subject, message }, context) => {
// Example: Fetch user data from the database
const user = await context.entities.User.findUnique({ where: { email: to } });

// You can use user data to customize the email body
const emailBody = `Hello ${user.name},\n\n${message}`;

// Send the email
const info = await emailSender.send({
to,
subject,
text: emailBody,
html: `<p>Hello ${user.name},</p><p>${message}</p>`,
// 'from' is optional if you set defaultFrom in main.wasp
});

return info;
};
import { emailSender } from "wasp/server/email";

export const sendNotification = async ({ to, subject, message }, context) => {
// Example: Fetch user data from the database
const user = await context.entities.User.findUnique({ where: { email: to } });

// You can use user data to customize the email body
const emailBody = `Hello ${user.name},\n\n${message}`;

// Send the email
const info = await emailSender.send({
to,
subject,
text: emailBody,
html: `<p>Hello ${user.name},</p><p>${message}</p>`,
// 'from' is optional if you set defaultFrom in main.wasp
});

return info;
};
You can access the database via context.entities as shown above. The emailSender.send method is the standard way to send emails in Wasp and supports all configured providers (see docs). 3. Call the Action from the Frontend In your React component:
import { sendNotification } from 'wasp/client/operations';

const handleSend = async () => {
await sendNotification({
to: 'user@example.com',
subject: 'Notification',
message: 'This is your notification!'
});
};
import { sendNotification } from 'wasp/client/operations';

const handleSend = async () => {
await sendNotification({
to: 'user@example.com',
subject: 'Notification',
message: 'This is your notification!'
});
};
4. Configure the Email Provider In your main.wasp file, set up the email provider (e.g., Dummy for development, SMTP, Mailgun, or SendGrid for production):
app Example {
...
emailSender: {
provider: SMTP, // or Mailgun, SendGrid, Dummy
defaultFrom: {
name: "MyApp",
email: "no-reply@myapp.com"
}
}
}
app Example {
...
emailSender: {
provider: SMTP, // or Mailgun, SendGrid, Dummy
defaultFrom: {
name: "MyApp",
email: "no-reply@myapp.com"
}
}
}
And set the required environment variables for your provider (see docs). --- Summary:
- Use an Action for your notification logic, not an API route. - Actions can access the database and be called from the frontend. - Use emailSender.send to send emails from your Action. - Configure your email provider in main.wasp and .env.server. For more details, see the official docs on Working with Actions and Sending Emails. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @Kynetix, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
Kynetix
KynetixOP4mo ago
@kapa.ai I noticed the actions file is a js file, not ts. Does it have to be a js file?
kapa.ai
kapa.ai4mo ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- No, your actions file does not have to be a JavaScript (.js) file—you can use TypeScript (.ts) as well. Wasp supports both JavaScript and TypeScript for implementing actions, and you can freely choose between them or even mix them within your project. The documentation and community discussions confirm that both file types are supported, and examples are provided for both .js and .ts files. You just need to ensure that your import path in the Wasp file matches the actual file extension you are using (e.g., @src/actions.ts or @src/actions.jsDocs: Creating a New Action, Discord: Typescript vs Javascript. So, you can implement your actions in TypeScript if you prefer. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @Kynetix, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
Kynetix
KynetixOP4mo ago
@kapa.ai I tried your sample above, but the frontend function call is asking for the context parameter, which is a parameter in the action definition. Should the context parameter be passed from the frontend, or is it available somehow via the backend?
kapa.ai
kapa.ai4mo ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- You should not pass the context parameter from the frontend. In Wasp, when you call an action from the frontend, Wasp automatically injects the context on the backend for you. The context contains things like the current user and access to entities, and is only available on the server side. If you are seeing a TypeScript or IDE error asking for the context parameter when calling the action from the frontend, it usually means you are either: - Importing the action incorrectly (e.g., importing the server implementation directly instead of the generated client operation), or - Calling the action in a context where Wasp is not handling the call (e.g., from another server function, not from the frontend). Correct usage: - From the frontend, import the action from wasp/client/operations and call it with only the arguments you defined (not the context). - The context is only passed manually when you are calling the action from another server-side function (e.g., from another action or a job) Docs: Implementing Actions, Discord: Action calling action. Example: Frontend (React component):
import { sendNotification } from 'wasp/client/operations';

await sendNotification({ to, subject, message }); // No context argument here!
import { sendNotification } from 'wasp/client/operations';

await sendNotification({ to, subject, message }); // No context argument here!
Server (action implementation):
export const sendNotification = async ({ to, subject, message }, context) => {
// context is injected by Wasp when called from the frontend
};
export const sendNotification = async ({ to, subject, message }, context) => {
// context is injected by Wasp when called from the frontend
};
If you are calling an action from another action or from a server-side job, then you must pass the context manually Discord: Action calling action.
In summary:
Do not pass context from the frontend. Wasp handles it for you when the action is called from the client. Only pass context manually when calling actions from other server-side code.
---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @Kynetix, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
Kynetix
KynetixOP4mo ago
Thanks, I was importing from wasp/server/operations instead of wasp/client/operations. Works great now, thanks.

Did you find this page helpful?