Based on your description, this is a classic Next.js environment variable scoping issue. The problem occurs because you’re likely trying to access MAILGUN_API_KEY in a client-side context, but Next.js environment variables have specific rules about where they can be accessed.
Understanding Next.js Environment Variable Rules
Next.js has two types of environment variables:
- Server-side only (default): Variables in
.env.localwithout theNEXT_PUBLIC_prefix - Client-side accessible: Variables prefixed with
NEXT_PUBLIC_
Since MAILGUN_API_KEY contains sensitive credentials, it should never be prefixed with NEXT_PUBLIC_ as this would expose it to the browser.
The Solution: Move Email Logic to API Routes
The best practice is to create a Next.js API route to handle email sending on the server-side where your environment variables are accessible:
Step 1: Create an API Route
Create /pages/api/send-email.ts (or /app/api/send-email/route.ts if using App Router):
typescript
// pages/api/send-email.ts
import { NextApiRequest, NextApiResponse } from ‘next’;
import EmailClient from “mailgun.js”;
import FormData from “form-data”;
const emailClient = new EmailClient(FormData);
const mailService = emailClient.client({
username: “api”,
key: process.env.MAILGUN_API_KEY!, // This works on server-side
url: “https://api.eu.mailgun.net/”,
});
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== ‘POST’) {
return res.status(405).json({ message: ‘Method not allowed’ });
}
const { recipient, title, content, htmlContent } = req.body;
try {
const emailData = {
from: “[email protected]”,
to: [recipient],
subject: title,
text: content,
html: htmlContent,
};
const result = await mailService.messages.create("mydomain.com", emailData);
res.status(200).json({ success: true, messageId: result.id });
} catch (error) {
console.error(‘Email sending failed:’, error);
res.status(500).json({ success: false, error: ‘Failed to send email’ });
}
}
Step 2: Update Your Client-Side Code
Now call your API route instead of directly using the email service:
typescript
// In your component or client-side code
const sendEmail = async (emailData: {
recipient: string;
title: string;
content?: string;
htmlContent?: string;
}) => {
const response = await fetch(‘/api/send-email’, {
method: ‘POST’,
headers: {
‘Content-Type’: ‘application/json’,
},
body: JSON.stringify(emailData),
});
if (!response.ok) {
throw new Error(‘Failed to send email’);
}
return response.json();
};
// Usage
await sendEmail({
recipient: userEmail,
title: “Account Created”,
content: “Thanks for joining us!”,
});
Additional Troubleshooting Steps
1. Verify Your Environment File
Ensure your .env.local file is in the root of your project and formatted correctly:
bash
MAILGUN_API_KEY=your-actual-api-key-here
No spaces around the equals sign, no quotes unless they’re part of the actual key.
2. Restart Your Development Server
After adding new environment variables, always restart your Next.js development server:
bash
npm run dev
or
yarn dev
3. Check Environment Variable Loading
Add a temporary server-side console log to verify the variable is loaded:
typescript
// In your API route
console.log(‘Mailgun API Key loaded:’, process.env.MAILGUN_API_KEY ? ‘YES’ : ‘NO’);
Why This Approach Works
- Security: Your API key stays on the server and never gets exposed to the client
- Reliability: Server-side environment variables are always accessible in API routes
- Scalability: You can add authentication, rate limiting, and other server-side logic
- Best Practice: This follows Next.js recommended patterns for handling sensitive operations
Important Note: If you’re deploying to production, make sure to add your environment variables to your hosting platform’s environment configuration (Vercel, Netlify, etc.).
This solution will resolve your 401 error and set you up with a robust, secure email sending system! ![]()