Send Emails Using a Dedicated Custom Email Sending Service
Create an email sending microservice (really just a template) to speed up sending emails in your new projects.
Start by creating a new email microservice that exposes a single endpoint api/Email/Send and accepts the following payload:
{
"to": "sample.user@example.com",
"subject": "Save Time on Future Projects",
"body": "Prepare to save some time.",
"isHtmlBody": false
}
Now, whenever you need to add email sending to a project you're working on, simply deploy this at a subdomain like emailservice.example.com
Create a new EmailService folder in your Infrastructure layer and add EmailServiceOptions.cs file that has a single BaseUrl property. In hindsight, I could've just used the primitive string type without the overhead of the Options pattern.
Then create the EmailRequest.cs file (you can use a record for immutability):
public class EmailRequest
{
public required string To { get; set; }
public required string Subject { get; set; }
public required string Body { get; set; }
public bool IsHtmlBody { get; set; }
}
Create the IEmailSender interface
public interface IEmailSender
{
Task SendEmailAsync(EmailRequest emailRequest);
}
Implement it
public class EmailSender(
IHttpClientFactory httpClientFactory,
ILogger<EmailSender> logger) : IEmailSender
{
private readonly HttpClient _httpClient = httpClientFactory.CreateClient(nameof(EmailSender));
public async Task SendEmailAsync(EmailRequest emailRequest)
{
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
Content = JsonContent.Create(emailRequest),
};
var response = await _httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
logger.LogInformation("Email sent successfully to {To}", emailRequest.To);
return;
}
var errorContent = await response.Content.ReadAsStringAsync();
logger.LogError("Failed to send email to {To}. Status code: {StatusCode}, Error: {Error}",
emailRequest.To, response.StatusCode, errorContent);
throw new InvalidOperationException(
$"Failed to send email. Status code: {response.StatusCode}, Error: {errorContent}");
}
}
And register the service in your service collection (i.e dependency injection container)
// Configure the EmailSender service
var emailUrl = configuration.GetValue<string>("EmailUrl")
?? throw new InvalidOperationException("EmailUrl configuration is missing.");
services.AddHttpClient(nameof(EmailSender), client =>
{
client.BaseAddress = new Uri(emailUrl);
client.DefaultRequestHeaders.Add("Accept", "application/json");
});
services.AddScoped<IEmailSender, EmailSender>();
services.AddHttpContextAccessor();
Add the email service configurations to your appsettings.json file (or whatever method you see fit for your use case)
"EmailUrl": "https://emailservice.example.com/api/Email/send"
And that is it. You can now inject the email service interface in your services and start sending emails in minutes.