Fast, Reliable Email Notifications with Quarkus
How to build a non-blocking, templated, PostgreSQL-powered email system Java developers can trust.
In almost any serious enterprise application, sending emails for alerts, confirmations, or digest summaries is a common requirement. Quarkus, with its reactive and imperative capabilities, provides robust support for email functionalities. This article guides you through setting up an email and notification system using Quarkus, focusing on templating, queuing outbound messages, integrating with SMTP servers, and managing recipients from a PostgreSQL database.
You will build with me today:
Setting up the Quarkus Mailer extension.
Configuring SMTP for email delivery.
Creating email templates with Qute.
Implementing a queuing mechanism for outbound emails.
Integrating PostgreSQL to manage email recipients.
Testing email functionalities during development.
1. Setting Up the Quarkus Project
Start by creating a new Quarkus project with the necessary extensions:
mvn io.quarkus.platform:quarkus-maven-plugin:3.21.2:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=mailer-app \
-Dextensions='rest,mailer,qute,jdbc-postgresql,hibernate-orm-panache,smallrye-reactive-messaging' \
-DnoCode
cd mailer-app
This command sets up a Quarkus project with REST, Mailer, Qute (templating), PostgreSQL, Hibernate ORM with Panache, and Reactive Messaging extensions.
2. Configuring SMTP for Email Delivery
Configure your SMTP settings in application.properties
:
quarkus.mailer.from=your-email@example.com
quarkus.mailer.host=smtp.example.com
quarkus.mailer.port=587
quarkus.mailer.username=your-email@example.com
quarkus.mailer.password=your-email-password
quarkus.mailer.start-tls=REQUIRED
quarkus.mailer.mock=false
Replace the placeholders with your actual SMTP server details. Setting quarkus.mailer.mock=false
ensures that emails are sent to the specified SMTP server.
3. Creating Email Templates with Qute
Qute is Quarkus's templating engine, allowing you to create dynamic email content. Create a template file at src/main/resources/templates/alert-email.html
:
<!DOCTYPE html>
<html>
<head>
<title>{subject}</title>
</head>
<body>
<p>Hello {recipientName},</p>
<p>{message}</p>
<p>Regards,<br/>Your Company</p>
</body>
</html>
To use this template in your code:
@Inject
@Location("alert-email")
MailTemplate alertEmail;
public void sendAlertEmail(String recipientEmail, String recipientName, String message) {
alertEmail.to(recipientEmail)
.subject("Alert Notification")
.data("recipientName", recipientName)
.data("message", message)
.send()
.subscribe().with(
success -> System.out.println("Email sent successfully."),
failure -> System.err.println("Failed to send email: " + failure)
);
}
Ensure that alert-email.html
is located in the src/main/resources/templates
directory.
4. Implementing a Queuing Mechanism for Outbound Emails
To handle high volumes of emails or to decouple email sending from the main application flow, implement a queuing mechanism using Quarkus's reactive messaging.
Configure the messaging channel in application.properties
:
mp.messaging.outgoing.email-out.connector=smallrye-in-memory
Define a producer to send messages to the queue:
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.reactive.messaging.Channel;
import org.eclipse.microprofile.reactive.messaging.Emitter;
@ApplicationScoped
public class EmailQueueProducer {
@Inject
@Channel("email-out")
Emitter<EmailMessage> emailEmitter;
public void queueEmail(String recipientEmail, String recipientName, String message) {
EmailMessage email = new EmailMessage(recipientEmail, recipientName, message);
emailEmitter.send(email);
}
}
Create a consumer to process the queued emails:
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.reactive.messaging.Incoming;
@ApplicationScoped
public class EmailQueueConsumer {
@Inject
EmailService emailService;
@Incoming("email-out")
public void processEmail(EmailMessage email) {
emailService.sendAlertEmail(email.getRecipientEmail(), email.getRecipientName(), email.getMessage());
}
}
Define the EmailMessage
class:
public class EmailMessage {
private String recipientEmail;
private String recipientName;
private String message;
// Constructors, getters, and setters
}
This setup allows you to queue emails and process them asynchronously, improving application responsiveness.
5. Integrating PostgreSQL to Manage Email Recipients
To manage email recipients dynamically, integrate a PostgreSQL database using Hibernate ORM with Panache.
5.1. Configuring the Data Source
Update your application.properties
to configure the PostgreSQL data source:
quarkus.datasource.db-kind=postgresql
#quarkus.datasource.username=your_db_username
#quarkus.datasource.password=your_db_password
#quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/your_db_name
quarkus.hibernate-orm.database.generation=drop-and-create
Replace your_db_username
, your_db_password
, and your_db_name
with your actual database credentials.
What you have learned
In this little guide, you built a full-featured email and notification system using Quarkus. You configured the Quarkus Mailer extension to send emails through an SMTP server, used Qute for templated email content, implemented a reactive messaging queue to decouple email processing, and integrated PostgreSQL to manage recipients through the EmailRecipient
entity.
By combining these technologies, you get a scalable, maintainable system that supports real-world requirements like confirmation emails, alerts, and periodic digests; all built with modern, cloud-native patterns.
Further Learning Resources
Let this be your starting point and extend it with digest batching, retry logic, or admin UIs as needed. Quarkus gives you the building blocks to deliver fast. Let me know if you want me to cover specific things and, I’d happily do so.