Mailgun
is a Vapor 4 service for the popular email sending API.
Sign up and set up a Mailgun account here. Make sure you get an API key and register a custom domain.
extension MailgunDomain {
static var myApp1: MailgunDomain { .init("mg.myapp1.com", .us) }
static var myApp2: MailgunDomain { .init("mg.myapp2.com", .eu) }
static var myApp3: MailgunDomain { .init("mg.myapp3.com", .us) }
static var myApp4: MailgunDomain { .init("mg.myapp4.com", .eu) }
}
In configure.swift
:
import Configuration
import Mailgun
public func configure(_ app: Application) async throws {
// Either set it directly
app.mailgun.configuration = .init(apiKey: "<api key>", defaultDomain: .myApp1)
// Or use Swift Configuration to read from environment variables or config files
let config = ConfigReader(providers: [
EnvironmentVariablesProvider(),
try await JSONProvider(filePath: "mailgun.config.json")
])
app.mailgun.configuration = try .init(config: config)
}
Note: If your private API key begins with
key-
, be sure to include it
The MailgunClient
is available on both Application
and Request
// Call it without arguments to use default domain
try await app.mailgun.client().send(...)
try await req.mailgunClient().send(...)
// or call it with domain
try await app.mailgun.client(.myApp1).send(...)
try await req.mailgunClient(.myApp1).send(...)
π‘ NOTE: All the examples below will be with
Request
, but you could do the same withApplication
as in example above.
import Mailgun
func routes(_ app: Application) throws {
app.post("mail") { req async throws -> ClientResponse in
let message = MailgunMessage(
from: "postmaster@example.com",
to: "example@gmail.com",
subject: "Newsletter",
text: "This is a newsletter",
html: "<h1>This is a newsletter</h1>"
)
return try await req.mailgunClient().send(message)
}
}
import Mailgun
func routes(_ app: Application) throws {
app.post("mail") { req async throws -> ClientResponse in
let fm = FileManager.default
guard let attachmentData = fm.contents(atPath: "/tmp/test.pdf") else {
throw Abort(.internalServerError)
}
let bytes: [UInt8] = Array(attachmentData)
var bytesBuffer = ByteBufferAllocator().buffer(capacity: bytes.count)
bytesBuffer.writeBytes(bytes)
let attachment = File.init(data: bytesBuffer, filename: "test.pdf")
let message = MailgunMessage(
from: "postmaster@example.com",
to: "example@gmail.com",
subject: "Newsletter",
text: "This is a newsletter",
html: "<h1>This is a newsletter</h1>",
attachments: [attachment]
)
return try await req.mailgunClient().send(message)
}
}
import Mailgun
func routes(_ app: Application) throws {
app.post("mail") { req async throws -> ClientResponse in
let message = MailgunTemplateMessage(
from: "postmaster@example.com",
to: "example@gmail.com",
subject: "Newsletter",
template: "my-template",
templateData: ["foo": "bar"]
)
return try await req.mailgunClient().send(message)
}
}
Using Vapor Leaf, you can easily setup your HTML Content.
First setup a leaf file in Resources/Views/Emails/my-email.leaf
<html>
<body>
<p>Hi #(name)</p>
</body>
</html>
With this, you can change the #(name)
with a variable from your Swift code; then when sending the email:
import Mailgun
func routes(_ app: Application) throws {
app.post("mail") { req async throws -> ClientResponse in
let content = try await req.view.render("Emails/my-email", ["name": "Bob"])
let message = MailgunMessage(
from: "postmaster@example.com",
to: "example@gmail.com",
subject: "Newsletter",
text: "",
html: content
)
return try await req.mailgunClient().send(message)
}
}
public func configure(_ app: Application) async throws {
// sets up a `catch_all` forward for the route listed
let routeSetup = MailgunRouteSetup(forwardURL: "http://example.com/mailgun/all", description: "A route for all emails")
try await app.mailgun.client().setup(forwarding: routeSetup)
}
import Mailgun
func routes(_ app: Application) throws {
let mailgunGroup = app.grouped("mailgun")
mailgunGroup.post("all") { req -> String in
do {
let incomingMail = try req.content.decode(MailgunIncomingMessage.self)
print("incomingMail: (incomingMail)")
return "Hello"
} catch {
throw Abort(.internalServerError, reason: "Could not decode incoming message")
}
}
}
import Mailgun
func routes(_ app: Application) throws {
let mailgunGroup = app.grouped("mailgun")
mailgunGroup.post("template") { req async throws -> ClientResponse in
let template = MailgunTemplate(name: "my-template", description: "api created :)", template: "<h1>Hello {{ name }}</h1>")
return try await req.mailgunClient().createTemplate(template)
}
}