A simple email forwarding service built with Next.js that sends emails via Gmail API. Supports plain text, HTML templates, and automatic object formatting.
- 📧 Send emails via Gmail API
- 🎨 Support for plain text, HTML, and object-based emails
- 🔄 Automatic field name formatting (e.g.,
full_name→ "Full Name") - 📝 Template support with data substitution
- 🔐 OAuth 2.0 authentication with refresh tokens
- 🔒 API key authentication for secure access
- ⚡ Rate limiting to prevent abuse
- 🌐 CORS enabled for cross-origin requests
-
Google Cloud Console Setup:
- Create a project in Google Cloud Console
- Enable Gmail API
- Create OAuth 2.0 Client ID credentials
- Add authorized redirect URI:
http://localhost:3000/api/auth/google/callback
-
Environment Variables: Create a
.envfile in the root directory:# Google OAuth credentials GMAIL_API_KEY=your-client-id.apps.googleusercontent.com GMAIL_SECRET=your-client-secret GMAIL_REFRESH_TOKEN=your-refresh-token GMAIL_SENDER_EMAIL=your-email@gmail.com GMAIL_SENDER_NAME=Your Name # API Security (optional but recommended for production) # If not set, API will be open (development mode) # Generate a strong random string: openssl rand -hex 32 API_KEY=your-secret-api-key-here
-
Install dependencies:
pnpm install
-
Get Refresh Token:
- Start the development server:
pnpm dev - Open in browser:
http://localhost:3000/api/auth/google - Authorize the application
- Copy the
refresh_tokenfrom the response - Add it to your
.envfile asGMAIL_REFRESH_TOKEN
- Start the development server:
-
Run the development server:
pnpm dev
Sends an email via Gmail API.
Security:
- API Key (optional): If
API_KEYis set in environment variables, you must provide it in:- Header:
X-API-Key: your-api-key - Or:
Authorization: Bearer your-api-key
- Header:
- Rate Limiting:
- With valid API key: 10 requests per minute
- Without API key: 5 requests per minute
- Rate limit information is included in response headers (not request):
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset
Request Headers:
Content-Type: application/json(required)X-API-Key: your-api-key(required ifAPI_KEYenv is set)- Or
Authorization: Bearer your-api-key(alternative to X-API-Key)
Request Body:
to(required): Email address or array of addressessubject(required): Email subjecthtml(optional): HTML content or object to formattext(optional): Plain text content or object to formatfields(optional): Object with key-value pairs (auto-formatted)cc(optional): CC recipientsbcc(optional): BCC recipientssenderEmail(optional): Override sender email from envsenderName(optional): Override sender name from envrefreshToken(optional): Override refresh token from env
{
"to": "recipient@example.com",
"subject": "Hello",
"text": "This is a plain text email."
}{
"to": "recipient@example.com",
"subject": "Welcome",
"html": "<h1>Welcome!</h1><p>This is an HTML email.</p>"
}The service automatically detects objects and formats them as a nice HTML table with plain text fallback.
{
"to": "recipient@example.com",
"subject": "Customer Information",
"fields": {
"full_name": "John Doe",
"customer-email": "john@doe.com",
"company": "John's Company",
"phone_number": "+1 234 567 8900"
}
}Result:
- Field names are automatically formatted:
full_name→ "Full Name",customer-email→ "Customer Email" - HTML: Formatted as a table
- Text: Formatted as key-value pairs
You can also pass objects directly to text or html fields:
{
"to": "recipient@example.com",
"subject": "Order Details",
"html": {
"order_id": "12345",
"total_amount": "$99.99",
"order_date": "2024-01-15"
}
}{
"to": "recipient@example.com",
"subject": "Welcome {{name}}!",
"template": {
"html": "<h1>Hello {{name}}!</h1><p>Your email is {{email}}</p>",
"text": "Hello {{name}}! Your email is {{email}}"
},
"data": {
"name": "John Doe",
"email": "john@example.com"
}
}{
"to": ["recipient1@example.com", "recipient2@example.com"],
"cc": "cc@example.com",
"bcc": "bcc@example.com",
"subject": "Team Update",
"html": "<h1>Team Update</h1><p>This is an update for the team.</p>"
}GET http://localhost:3000/api/auth/google
Open this URL in your browser, authorize, and copy the refresh_token from the response.
POST http://localhost:3000/api/sendmail
Headers:
Content-Type: application/json
Body:
{
"to": "recipient@example.com",
"subject": "Test Email",
"text": "This is a test email."
}POST http://localhost:3000/api/sendmail
Headers:
Content-Type: application/json
X-API-Key: your-api-key-here
Body:
{
"to": "recipient@example.com",
"subject": "Test HTML Email",
"html": "<h1>Hello!</h1><p>This is an <strong>HTML</strong> email.</p>"
}POST http://localhost:3000/api/sendmail
Headers:
Content-Type: application/json
X-API-Key: your-api-key-here
Body:
{
"to": "recipient@example.com",
"subject": "Customer Information",
"fields": {
"full_name": "John Doe",
"customer-email": "john@doe.com",
"company": "John's Company",
"phone_number": "+1 234 567 8900"
}
}POST http://localhost:3000/api/sendmail
Headers:
Content-Type: application/json
X-API-Key: your-api-key-here
Body:
{
"to": "recipient@example.com",
"subject": "Order #{{orderId}}",
"template": "<html><body><h1>Order #{{orderId}}</h1><p>Total: ${{total}}</p></body></html>",
"data": {
"orderId": "12345",
"total": "99.99"
}
}{
"success": true,
"messageId": "18cxxxxxxxxxxxxxxxxxxxxx",
"message": "Email sent successfully"
}{
"error": "Field \"to\" is required"
}The service automatically formats field names:
full_name→ "Full Name"customer-email→ "Customer Email"phone_number→ "Phone Number"order_date→ "Order Date"
Works with both underscores (_) and hyphens (-).
The service automatically detects content type:
- String with HTML tags → Treated as HTML
- Plain string → Treated as text
- Object → Auto-formatted as HTML table + plain text
-
Generate a strong API key:
openssl rand -hex 32
-
Add to environment variables:
API_KEY=your-generated-key-here
-
Use in requests:
- Header:
X-API-Key: your-api-key - Or:
Authorization: Bearer your-api-key
- Header:
-
Development Mode:
- If
API_KEYis not set, the API is open (for development only) - Never deploy to production without setting
API_KEY
- If
- With API Key: 10 requests per minute
- Without API Key: 5 requests per minute
- Rate limits are per API key or IP address
- Rate limit info is included in response headers
- Always set
API_KEYin production - Use HTTPS in production (Vercel provides this automatically)
- Keep your API key secret - never commit it to git
- Rotate API keys periodically
- Monitor rate limits using response headers
- Set
API_KEYin your.envfile, or - Provide API key in
X-API-Keyheader orAuthorization: Bearer <token>
- Check that the API key in your request matches
API_KEYin environment variables - Ensure there are no extra spaces or characters
- Wait for the rate limit window to reset (check
Retry-Afterheader) - Use a valid API key to get higher rate limits (10/min vs 5/min)
- Consider implementing request queuing on the client side
This usually means:
- Redirect URI mismatch - ensure it matches exactly in Google Cloud Console
- Refresh token expired or revoked - get a new one via
/api/auth/google - Client ID mismatch - ensure
GMAIL_API_KEYmatches the one used for authorization
Add GMAIL_REFRESH_TOKEN to your .env file or pass it in the request body.
Add GMAIL_SENDER_EMAIL to your .env file or pass it in the request body.
mail-service/
├── app/
│ ├── api/
│ │ ├── auth/
│ │ │ └── google/
│ │ │ ├── route.ts # OAuth initiation
│ │ │ └── callback/
│ │ │ └── route.ts # OAuth callback
│ │ └── sendmail/
│ │ └── route.ts # Email sending endpoint
│ └── ...
├── src/
│ └── lib/
│ ├── auth.ts # API key authentication
│ ├── cors.ts # CORS utility
│ ├── email-parser.ts # Content parsing logic
│ ├── google.ts # Gmail API integration
│ └── rate-limit.ts # Rate limiting logic
└── ...
-
Push to GitHub:
git init git add . git commit -m "Initial commit" git remote add origin your-repo-url git push -u origin main
-
Import to Vercel:
- Go to Vercel
- Import your GitHub repository
- Add environment variables:
GMAIL_API_KEYGMAIL_SECRETGMAIL_REFRESH_TOKENGMAIL_SENDER_EMAILGMAIL_SENDER_NAMEAPI_KEY(generate a strong key:openssl rand -hex 32)
-
Update Redirect URI:
- In Google Cloud Console, add your production callback URL:
https://your-domain.vercel.app/api/auth/google/callback
- In Google Cloud Console, add your production callback URL:
-
Deploy:
- Vercel will automatically deploy on push
- Your API will be available at
https://your-domain.vercel.app/api/sendmail