
This comprehensive guide will walk you through setting up email sending capabilities in a Spring Boot application using Azure Communication Services (ACS).
Table of Contents
- Azure Communication Services Setup
- Spring Boot Project Configuration
- Implementing Email Service
- Testing with Postman
- Best Practices and Considerations
1. Azure Communication Services Setup
Prerequisites
- An active Azure account
- Azure CLI installed (optional but recommended)
- Appropriate permissions to create resources in your Azure subscription
Step-by-Step Azure Configuration
- Create a Communication Services Resource
- Log in to the Azure Portal
- Click "Create a resource" and search for "Communication Services"
- Click "Create" and fill in:
- Subscription: Select your subscription
- Resource group: Create new or select existing
- Resource name: e.g., "MyEmailService"
- Data location: Select region closest to your users
- Click "Review + create" then "Create"
- Configure Email Domain
- Once deployed, go to your Communication Services resource
- Navigate to "Email" under "Services" in the left menu
- Click "Add domain"
- Choose between:
- Azure Managed Domain (quick start)
- Bring Your Own Domain (for production with custom domains)
- For Azure Managed Domain:
- Provide a subdomain name (e.g., "notifications")
- Click "Add domain"
- Verification may take up to 24 hours
- Get Connection String
- In your Communication Services resource
- Navigate to "Keys" under "Settings"
- Copy either "Primary connection string" or "Secondary connection string"
- Store this securely (we'll use it in Spring Boot)
- Verify Sender Address (Optional but Recommended)
- Under "Email" service, go to "Sender addresses"
- Click "Add sender address"
- Provide an email address you control
- Azure will send a verification email
- Follow the link to verify the address
2. Spring Boot Project Configuration
Prerequisites
- Java JDK 11 or later
- Maven or Gradle
- Spring Boot 2.7.x or 3.x
Project Setup
1. Create a new Spring Boot project
- Use start.spring.io with these dependencies:
- Spring Web
- Lombok (optional but recommended)
- Azure Communication Services Email (add manually)
2. Add Azure Communication Services Dependency
For Maven (pom.xml
):
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-communication-email</artifactId>
<version>1.0.0</version> <!-- Check for latest version -->
</dependency>
For Gradle (build.gradle
):
implementation 'com.azure:azure-communication-email:1.0.0'
Configure Application Properties
Add to application.properties
:properties
# Azure Communication Services
azure.communication.connection-string=your-connection-string
azure.communication.sender-email=donotreply@yourdomain.azurecomm.net
Or in application.yml
:
azure:
communication:
connection-string: "your-connection-string"
sender-email: "donotreply@yourdomain.azurecomm.net"
3. Implementing Email Service
Create Configuration Class
import com.azure.communication.email.EmailClient;
import com.azure.communication.email.EmailClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AzureEmailConfig {
@Value("${azure.communication.connection-string}")
private String connectionString;
@Bean
public EmailClient emailClient() {
return new EmailClientBuilder()
.connectionString(connectionString)
.buildClient();
}
}
Create Email Service Class
import com.azure.communication.email.models.*;
import com.azure.core.util.polling.PollResponse;
import com.azure.core.util.polling.SyncPoller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class EmailService {
private final EmailClient emailClient;
@Value("${azure.communication.sender-email}")
private String senderEmail;
public EmailService(EmailClient emailClient) {
this.emailClient = emailClient;
}
public String sendEmail(String to, String subject, String htmlContent, String plainTextContent) {
try {
EmailMessage message = new EmailMessage()
.setSenderAddress(senderEmail)
.setToRecipients(to)
.setSubject(subject)
.setBody(new EmailBody()
.setHtml(htmlContent)
.setPlainText(plainTextContent));
SyncPoller<EmailSendResult, EmailSendResult> poller = emailClient.beginSend(message, null);
PollResponse<EmailSendResult> pollResponse = poller.waitForCompletion();
if (poller.getFinalResult().getStatus() == EmailSendStatus.SUCCEEDED) {
log.info("Email sent successfully to {}", to);
return "Email sent successfully";
} else {
log.error("Failed to send email to {}", to);
return "Failed to send email";
}
} catch (Exception ex) {
log.error("Error sending email: {}", ex.getMessage());
throw new RuntimeException("Error sending email", ex);
}
}
public String sendEmailWithAttachment(String to, String subject, String htmlContent,
String plainTextContent, byte[] attachment,
String attachmentName, String contentType) {
try {
EmailAttachment emailAttachment = new EmailAttachment(attachmentName, contentType, attachment);
EmailMessage message = new EmailMessage()
.setSenderAddress(senderEmail)
.setToRecipients(to)
.setSubject(subject)
.setBody(new EmailBody()
.setHtml(htmlContent)
.setPlainText(plainTextContent))
.setAttachments(emailAttachment);
SyncPoller<EmailSendResult, EmailSendResult> poller = emailClient.beginSend(message, null);
poller.waitForCompletion();
if (poller.getFinalResult().getStatus() == EmailSendStatus.SUCCEEDED) {
log.info("Email with attachment sent successfully to {}", to);
return "Email with attachment sent successfully";
} else {
log.error("Failed to send email with attachment to {}", to);
return "Failed to send email with attachment";
}
} catch (Exception ex) {
log.error("Error sending email with attachment: {}", ex.getMessage());
throw new RuntimeException("Error sending email with attachment", ex);
}
}
}
Create REST Controller
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@RestController
@RequestMapping("/api/email")
public class EmailController {
private final EmailService emailService;
public EmailController(EmailService emailService) {
this.emailService = emailService;
}
@PostMapping("/send")
public ResponseEntity<String> sendEmail(
@RequestParam String to,
@RequestParam String subject,
@RequestParam String htmlContent,
@RequestParam(required = false) String plainTextContent) {
String response = emailService.sendEmail(
to,
subject,
htmlContent,
plainTextContent != null ? plainTextContent : "");
return ResponseEntity.ok(response);
}
@PostMapping("/send-with-attachment")
public ResponseEntity<String> sendEmailWithAttachment(
@RequestParam String to,
@RequestParam String subject,
@RequestParam String htmlContent,
@RequestParam(required = false) String plainTextContent,
@RequestParam MultipartFile attachment) throws IOException {
String response = emailService.sendEmailWithAttachment(
to,
subject,
htmlContent,
plainTextContent != null ? plainTextContent : "",
attachment.getBytes(),
attachment.getOriginalFilename(),
attachment.getContentType());
return ResponseEntity.ok(response);
}
}
Exception Handling (Optional but Recommended)
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String handleRuntimeException(RuntimeException ex) {
return "Error occurred: " + ex.getMessage();
}
}
4. Testing with Postman
Prerequisites
- Postman installed
- Spring Boot application running
Testing the Basic Email Endpoint
- Create a new POST request
- URL:
http://localhost:8080/api/email/send
- Select "form-data" in the Body tab
- URL:
- Add parameters:
to
: recipient@example.comsubject
: Test Email from AzurehtmlContent
:<h1>Hello from Azure!</h1><p>This is a test email.</p>
plainTextContent
: Hello from Azure! This is a test email.
- Send the request
- Expected response: "Email sent successfully"
- Check recipient's inbox for the email
Testing Email with Attachment
- Create a new POST request
- URL:
http://localhost:8080/api/email/send-with-attachment
- Select "form-data" in the Body tab
- URL:
- Add parameters:
to
: recipient@example.comsubject
: Test Email with AttachmenthtmlContent
:<h1>Hello!</h1><p>Please see attached file.</p>
plainTextContent
: Hello! Please see attached file.attachment
: Select a file to upload
- Send the request
- Expected response: "Email with attachment sent successfully"
- Check recipient's inbox for the email with attachment
Testing Edge Cases
- Invalid Email Address
- Send to: "invalid-email"
- Expected: Error response with validation message
- Missing Required Fields
- Omit the
subject
field - Expected: 400 Bad Request with details of missing fields
- Omit the
- Large Attachments
- Try sending a file > 10MB (Azure limit)
- Expected: Error response about size limitation
5. Best Practices and Considerations
Performance Optimization
- Async Processing: Consider making the email sending asynchronous using
@Async
- Batching: For multiple emails, use batch processing
- Connection Pooling: Azure client handles this internally
Security Considerations
- Secure Connection Strings: Never hardcode in source; use Azure Key Vault
- Input Validation: Validate all email addresses and content
- Rate Limiting: Implement to prevent abuse of your email service
Monitoring and Logging
- Azure Monitor: Set up alerts for email delivery failures
- Application Insights: Track email sending metrics
- Logging: As shown in the service, log successes and failures
Cost Optimization
- Free Tier: Azure offers free emails per month
- Monitoring Usage: Track your email usage to avoid surprises
- Choosing Right SKU: Select appropriate pricing tier based on volume
Production Readiness
- Custom Domain: For production, use your own domain instead of Azure subdomain
- DKIM/DMARC/SPF: Configure these for better email deliverability
- Feedback Loop: Set up bounce and complaint handling
Conclusion
This guide provided a comprehensive walkthrough of setting up email sending capabilities with Spring Boot and Azure Communication Services. You've learned how to:
- Configure Azure Communication Services for email
- Set up a Spring Boot project with Azure SDK
- Implement email sending with and without attachments
- Test the service thoroughly with Postman
- Apply best practices for production readiness
With this implementation, you can now reliably send transactional emails from your Spring Boot applications while leveraging Azure's scalable infrastructure.