← Back to all posts
Kirim Email Transaksional dari NestJS dengan Mailanvil API
NestJS adalah framework Node.js yang populer untuk aplikasi enterprise — terutama fintech, SaaS, dan platform yang butuh struktur kuat. Tapi untuk email transaksional, banyak dev masih pakai SMTP manual atau provider USD mahal.
Tutorial ini cover: setup NestJS + Mailanvil API kirim email OTP, invoice, dan welcome email.
Kenapa Mailanvil untuk NestJS?
| Fitur | Mailanvil | SendGrid | Mailgun |
|---|---|---|---|
| Harga 10K/bulan | Rp 149rb | $20 (~Rp 320rb) | $35 (~Rp 560rb) |
| Bayar pakai | QRIS, GoPay | Kartu kredit | Kartu kredit |
| Latency dari Jakarta | ~10ms (CF edge) | ~180ms (US relay) | ~200ms (US relay) |
| MCP server | Native | Community | Community |
| Docs Bahasa | Ya | Tidak | Tidak |
Setup Project
# Buat project baru
npx @nestjs/cli new mailanvil-email-app
cd mailanvil-email-app
# Install dependency
npm install axios
Buat file src/mailanvil/mailanvil.service.ts:
import { Injectable } from '@nestjs/common';
import axios from 'axios';
@Injectable()
export class MailanvilService {
private readonly apiUrl = 'https://api.mailanvil.com/v1/send';
private readonly apiKey = process.env.MAILANVIL_API_KEY;
async sendEmail(to: string, subject: string, html: string) {
const response = await axios.post(
this.apiUrl,
{
from: '[email protected]',
to,
subject,
html,
},
{
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
}
);
return response.data;
}
}
Contoh: OTP Email
// src/auth/otp.service.ts
import { Injectable } from '@nestjs/common';
import { MailanvilService } from '../mailanvil/mailanvil.service';
@Injectable()
export class OtpService {
constructor(private readonly mailanvil: MailanvilService) {}
async sendOtp(email: string, code: string) {
const html = `
<div style="font-family: Arial, sans-serif; max-width: 400px; margin: 0 auto;">
<h2>Kode Verifikasi Anda</h2>
<p>Kode OTP Anda: <strong style="font-size: 24px; color: #ff801f;">${code}</strong></p>
<p>Kode ini berlaku selama 5 menit. Jangan bagikan kode ini kepada siapa pun.</p>
</div>
`;
return this.mailanvil.sendEmail(email, 'Kode Verifikasi', html);
}
}
Contoh: Invoice Email
// src/billing/invoice.service.ts
import { Injectable } from '@nestjs/common';
import { MailanvilService } from '../mailanvil/mailanvil.service';
@Injectable()
export class InvoiceService {
constructor(private readonly mailanvil: MailanvilService) {}
async sendInvoice(email: string, invoice: {
id: string;
amount: number;
dueDate: string;
}) {
const html = `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2>Invoice #${invoice.id}</h2>
<table style="width: 100%; border-collapse: collapse;">
<tr>
<td style="padding: 8px; border-bottom: 1px solid #eee;">Total</td>
<td style="padding: 8px; border-bottom: 1px solid #eee; text-align: right;">
Rp ${invoice.amount.toLocaleString('id-ID')}
</td>
</tr>
<tr>
<td style="padding: 8px; border-bottom: 1px solid #eee;">Jatuh Tempo</td>
<td style="padding: 8px; border-bottom: 1px solid #eee; text-align: right;">
${invoice.dueDate}
</td>
</tr>
</table>
<p style="margin-top: 20px;">
<a href="https://yourapp.com/invoice/${invoice.id}"
style="background: #ff801f; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px;">
Bayar Sekarang
</a>
</p>
</div>
`;
return this.mailanvil.sendEmail(email, `Invoice #${invoice.id}`, html);
}
}
Contoh: Welcome Email
// src/user/welcome.service.ts
import { Injectable } from '@nestjs/common';
import { MailanvilService } from '../mailanvil/mailanvil.service';
@Injectable()
export class WelcomeService {
constructor(private readonly mailanvil: MailanvilService) {}
async sendWelcome(email: string, name: string) {
const html = `
<div style="font-family: Arial, sans-serif; max-width: 500px; margin: 0 auto;">
<h2>Selamat Datang, ${name}! 🎉</h2>
<p>Terima kasih telah bergabung dengan platform kami.</p>
<p>Berikut yang bisa kamu lakukan selanjutnya:</p>
<ul>
<li>Lengkapi profil kamu</li>
<li>Eksplorasi fitur yang tersedia</li>
<li>Hubungi support jika butuh bantuan</li>
</ul>
<p>Salam,<br>Tim Kami</p>
</div>
`;
return this.mailanvil.sendEmail(email, 'Selamat Datang!', html);
}
}
Error Handling
// Tambahkan di MailanvilService
async sendEmail(to: string, subject: string, html: string) {
try {
const response = await axios.post(
this.apiUrl,
{ from: '[email protected]', to, subject, html },
{
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
}
);
return response.data;
} catch (error) {
if (error.response?.status === 429) {
// Rate limited — tunggu dan retry
await new Promise(r => setTimeout(r, 1000));
return this.sendEmail(to, subject, html);
}
throw error;
}
}
Environment Variables
# .env
MAILANVIL_API_KEY=your_api_key_here
Testing
# Jalankan app
npm run start:dev
# Test kirim email (dari terminal)
curl -X POST http://localhost:3000/auth/send-otp \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]"}'
Kenapa NestJS + MailAnvil?
- NestJS — struktur enterprise, dependency injection, TypeScript native
- Mailanvil — IDR pricing, QRIS/GoPay, CF edge latency ~10ms dari Jakarta
- MCP integration — AI coding agent bisa kirim email langsung dari IDE
- Bahasa Indonesia — docs dan support dalam Bahasa
Selanjutnya
- Webhook untuk notifikasi delivery status
- Template engine (Handlebars/MJML) untuk email kompleks
- Retry queue dengan Bull/BullMQ untuk reliability
MailAnvil API v1. Docs: docs.mailanvil.com. Pricing: mailanvil.com/pricing.