Quarkus - Sending emails

This guide demonstrates how your Quarkus application can send emails using an SMTP server.

Prerequisites

To complete this guide, you need:

  • less than 15 minutes

  • The SMTP hostname, port and credentials, and an email address

  • an IDE

  • JDK 1.8+ installed with JAVA_HOME configured appropriately

  • Apache Maven 3.5.3+

  • GraalVM installed if you want to run in native mode.

Architecture

In this guide, we are going to see how you can send emails from a Quarkus application. It covers simple emails, attachments, inlined attachments, the reactive and imperative APIs…​

Creating the Maven Project

Create a new project with the following command:

mvn io.quarkus:quarkus-maven-plugin:0.17.0:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=sending-email-quickstart \
    -Dextensions="mailer"

If you already have an existing project, add the mailer extension:

mvn quarkus:add-extensions -Dextensions="mailer"

Configuring the mailer

The Quarkus mailer is using SMTP. In the src/main/resources/application.properties file, you need to configure the host, port, username, password as well as the other configuration aspect. Note that the password can also be configured using system properties and environment variables.

Here is an example using sendgrid:

quarkus.mailer.from=test@quarkus.io
quarkus.mailer.host=smtp.sendgrid.net
quarkus.mailer.port=465
quarkus.mailer.ssl=true
quarkus.mailer.username=....
quarkus.mailer.password=....

The following table lists the properties that can be configured:

Property Type Comment Default

from

String

the default from address, without, all emails will need an explicit value

mock

Boolean

if set to true, the emails are not sent, but printed on the console and collected in a MockMailbox bean.

false in PROD mode, true in DEV and TEST modes

bounce-address

String

the default bounce address

host

String

the SMTP server host name

mandatory

port

Integer

the SMTP server port

25

username

String

the username

password

String

the password

ssl

Boolean

Enables or disables SSL

false

trust-all

Boolean

Set whether to trust all certificates

false

max-pool-size

Integer

Configures the maximum number of open connections to the mail server

10

own-host-name

String

The hostname to be used for HELO/EHLO and the Message-ID

keep-alive

Boolean

Set if connection pool is enabled.

true

disable-esmtp

Boolean

Disable ESMTP

false

start-tls

NONE, OPTIONAL, REQUIRED

Set the TLS security mode

OPTIONAL

login

DISABLED, OPTIONAL, REQUIRED

Set the login mode for the connection

NONE

auth-methods

space-separated list

Set the allowed auth methods, accept all methods if not set

key-store

String

Path of the key store

key-store-password

String

Key store password

Sending simple emails

In a JAX-RS resource, or in a bean, you can inject the mailer as follows:

@Inject
Mailer mailer;

@Inject
ReactiveMailer reactiveMailer;

There are 2 APIs:

  • io.quarkus.mailer.Mailer provides the imperative (blocking and synchronous) API;

  • io.quarkus.mailer.ReactiveMailer provides the reactive (non-blocking and asynchronous) API

Note
The two APIs are equivalent feature-wise. Actually the Mailer implementation is built on top of the ReactiveMailer implementation.

To send a simple email, proceed as follows:

// Imperative API:
mailer.send(Mail.withText("to@acme.org", "A simple email from quarkus", "This is my body."));
// Reactive API:
CompletionStage<Void> stage = reactiveMailer.send(Mail.withText("to@acme.org", "A reactive email from quarkus", "This is my body."));

For example, you can use the Mailer in a JAX-RS endpoint as follows:

@GET
@Path("/simple")
public Response sendASimpleEmail() {
    mailer.send(Mail.withText("to@acme.org", "A simple email from quarkus", "This is my body"));
    return Response.accepted().build();
}

@GET
@Path("/async")
public CompletionStage<Response> sendASimpleEmailAsync() {
    return reactiveMailer.send(
            Mail.withText("to@acme.org", "A reactive email from quarkus", "This is my body"))
            .thenApply(x -> Response.accepted().build());
}

With such a JAX-RS resource, you can check that everything is working with:

curl http://localhost:8080/simple
curl http://localhost:8080/async

You can create new io.quarkus.mailer.Mail instances from the constructor or from the Mail.withText and Mail.withHtml helper methods. The Mail instance lets you add recipients (to, cc, or bcc), set the subject, headers, sender (from) address…​

You can also send several Mail objects in one call:

mailer.send(mail1, mail2, mail3);

Sending attachments

To send attachment, just use the addAttachment methods on the io.quarkus.mailer.Mail instance:

@GET
@Path("/attachment")
public Response sendEmailWithAttachment() {
    mailer.send(Mail.withText("to@acme.org", "An email from quarkus with attachment",
            "This is my body")
            .addAttachment("my-file.txt",
                "content of my file".getBytes(), "text/plain"));
    return Response.accepted().build();
}

Attachments can be created from raw bytes (as shown in the snippet) or files.

Sending HTML emails with inlined attachments

When sending HTML email, you can add inlined attachments. For example, you can send an image with your email, and this image will be displayed in the mail content.

@GET
@Path("/html")
public Response sendingHTML() {
    String body = "<strong>Hello!</strong>" + "\n" +
        "<p>Here is an image for you: <img src=\"cid:my-image@quarkus.io\"/></p>" +
        "<p>Regards</p>";
    mailer.send(Mail.withHtml("to@acme.org", "An email in HTML", body)
        .addInlineAttachment("quarkus-logo.png",
            new File("quarkus-logo.png"),
            "image/png", "<my-image@quarkus.io>"));
    return Response.accepted().build();
}

Note the content-id format and reference. By spec, when you create the inline attachment, the content-id must be structured as follows: <id@domain>. If you don’t wrap your content-id between <>, it is automatically wrapped for you. When you want to reference your attachment, for instance in the src attribute, use cid:id@domain (without the < and >).

Testing email sending

Because it is very inconvenient to send emails during development and testing, you can set the quarkus.mailer.mock boolean configuration to true to not actually send emails but print them on stdout and collect them in a MockMailbox bean instead. This is the default if you are running Quarkus in DEV or TEST mode.

You can then write tests to verify that your emails were sent, for example, by a REST endpoint:

class MailTest {

    private static final String TO = "foo@quarkus.io";

    @Inject
    MockMailbox mailbox;

    @BeforeEach
    void init() {
        mailbox.clear();
    }

    @Test
    void testTextMail() throws MessagingException, IOException {
        // call a REST endpoint that sends email
        given()
        .when()
        .get("/send-email")
        .then()
           .statusCode(200)
           .body(is("OK"));

        // verify that it was sent
        List<Mail> sent = mailbox.getMessagesSentTo(TO);
        assertThat(sent).hasSize(1);
        Mail actual = sent.get(0);
        assertThat(actual.getText()).contains("Wake up!");
        assertThat(actual.getSubject()).isEqualTo("Alarm!");

        assertThat(mailbox.getTotalMessagesSent()).isEqualTo(6);
    }
}

Using the underlying Vert.x Mail Client

The Quarkus Mailer is implemented on top of the Vert.x Mail Client, providing an asynchronous and non-blocking way to send emails. If you need fine control on how the mail is sent, for instance if you need to retrieve the message ids, you can inject the underlying client, and use it directly:

@Inject MailClient client;

Three API flavors are exposed:

  • the Axle client (io.vertx.axle.ext.mail.MailClient), using CompletionStage and Reactive Streams Publisher

  • the RX Java 2 client (io.vertx.reactivex.ext.mail.MailClient)

  • the bare client (io.vertx.ext.mail.MailClient)

Check the Using Vert.x guide for further details about these different APIs and how to select the most suitable for you.

The retrieved MailClient is configured using the configuration key presented above. You can also create your own instance, and pass your own configuration.

Conclusion

This guide has shown how you can send emails from a Quarkus application. The mailer extension works in JVM and native mode.