I use form to verify the email with emailjs, sending the email value to next.js API. In the API route.js I create and hash token, store it in database and make a confirmation link(so far the console.logs are giving my that everything is ok) and then in the sendEmail (which I use in another form and it works)function I receive: "Email send failed: undefined
portfolio3d-nextjs-app-1 | Failed to send email: Error: Failed to send email
portfolio3d-nextjs-app-1 | at sendEmail (webpack-internal:".
Another info I use Docker image to run my project.
Can someone give me advice how to solve this problem?
'use client';
import React from 'react';
import { useForm } from 'react-hook-form';
import { Toaster, toast } from 'sonner';
import { MailCheck, MailX } from 'lucide-react';
export default function EmailConfirmationForm() {
const { register, handleSubmit, formState: { errors }, reset } = useForm();
// Send confirmation email
const handleSendConfirmationEmail = async (email) => {
try {
const response = await fetch(`/api/sendConfirmationEmail?email=${email}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email }),
});
if (!response.ok) throw new Error('Failed to send confirmation email');
toast.success('Confirmation email sent!', {
duration: 5000,
icon: <MailCheck />,
style: {
backgroundColor: '#1B1B1B',
border: 'none',
},
});
return true;
} catch (error) {
toast.error('Failed to send confirmation email.', {
duration: 5000,
icon: <MailX />,
style: {
backgroundColor: '#1B1B1B',
border: 'none',
},
});
return false;
}
};
const onSubmit = async (data) => {
try {
await handleSendConfirmationEmail(data.email);
reset();
} catch (error) {
console.error('Failed to send confirmation email:', error);
}
};
return (
<>
<Toaster position="bottom-left" richColors />
<form onSubmit={handleSubmit(onSubmit)} className="max-w-md w-full flex flex-col items-center justify-center space-y-4">
{/* Email input */}
<label htmlFor="email" className="self-start">Email Confirmation</label>
<input
id="email"
type="email"
placeholder="Email Confirmation"
{...register("email", {
required: 'This field is required',
pattern: { value: /^S+@S+$/i, message: 'Invalid email format' }
})}
className="w-full p-2 rounded-md shadow-lg text-foreground focus:outline-none focus:ring-2 focus:ring-accent/50 custom-bg"
aria-label="Email"
/>
{errors.email && <span className="inline-block self-start text-red-500">{errors.email.message}</span>}
<input
value="Cast your message!"
className="px-10 py-4 rounded-md shadow-lg bg-background border border-accent/30 hover:shadow-glass-sm backdrop-blur-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent/50 cursor-pointer capitalize"
type="submit"
/>
</form>
</>
);
}
import { NextResponse } from 'next/server';
import { sendEmail } from '@/app/../../service/service.email';
import redisClient, { connectRedis } from '@/app/../../service/redisClient';
import { generateToken, hashToken } from '@/app/../../service/tokenService';
import { rateLimiter } from '@/app/../../service/rateLimiter';
export async function POST(req) {
const { searchParams } = new URL(req.url);
const email = searchParams.get('email');
const ip = req.headers.get('x-forwarded-for') || req.socket.remoteAddress || req.ip;
// if (!rateLimiter(ip)) {
// return NextResponse.json({ message: 'Too many requests, please try again later.' }, { status: 429 });
// }
await connectRedis();
const token = generateToken();
const hashedToken = await hashToken(token);
const expiresAt = Date.now() + 3600 * 1000;
try {
await redisClient.setEx(`confirm_tokens:${hashedToken}`, 3600, JSON.stringify({ email, expiresAt }));
const storedToken = await redisClient.get(`confirm_tokens:${hashedToken}`);
console.log('Token stored in Redis:', storedToken); // Log to verify storage
} catch (redisError) {
console.error('Error saving token in Redis:', redisError);
return NextResponse.json({ message: 'Error saving confirmation token to Redis.' }, { status: 500 });
}
const confirmationLink = `${process.env.NEXT_PUBLIC_APP_URL}/api/confirmEmail?token=${token}&email=${email}`;
console.log(`Confirmation link: ${confirmationLink}`);
try {
const templateParams = {
to: email,
from_name: 'Email Confirmation',
reply_to: email,
message: `Please confirm your email by clicking the link: ${confirmationLink}`,
};
await sendEmail(templateParams);
return NextResponse.json({ message: 'Confirmation email sent!' }, { status: 200 });
} catch (error) {
console.error('Failed to send email:', error);
return NextResponse.json({ message: 'Failed to send confirmation email.' }, { status: 500 });
}
}
import emailjs from '@emailjs/browser';
export const sendEmail = async (params) => {
try {
const response = await emailjs.send(
process.env.NEXT_PUBLIC_SERVICE_ID,
process.env.NEXT_PUBLIC_TEMPLATE_ID,
params,
{
publicKey: process.env.NEXT_PUBLIC_PUBLIC_KEY,
limitRate: {
throttle: 5000,
},
}
);
console.log('Email sent successfully:', response);
return response;
} catch (error) {
console.error('Email send failed:', error.text);
if (error.response) {
console.error('Error response from email service:', error.response);
}
throw new Error(error.text || 'Failed to send email');
}
};
2
Answers
I think you are using this node package on server side ‘@emailjs/browser’. while this is used for client side only.
please use this package https://www.npmjs.com/package/@emailjs/nodejs on server side code.
EmailJs expects a specific format. Try updating the key value pairs as follows:
change
"to"
to"to_email"
and set"reply_to"
to"to_name"
.If this doesnt work, make sure your environment variables are properly set in docker and also consider adding logging to help debug errors.