I have been working on a project where the user fills a form which includes his mobile number which we then use to pass it onto our express server using twilio API for messaging. My project is working fine on my local host but is showing error when I am uploading it to my github repository which in turn is connected to vercel for deployment.
Here is the code for the page where I am collecting data where I am handling request through the function handleSendSMS via axios library:
import React, { useState } from "react";
import formBG from "../assets/formBG.jpg";
import PoliceData from "../components/data";
import { useSupabase } from "../context/SupabaseContext";
import { useToast } from "@chakra-ui/react";
import axios from 'axios';
const NewVisit = () => {
const { handleSubmit, individual } = useSupabase();
const [ phoneNumber, setPhoneNumber ] = useState('');
const [form, setForm] = useState({
name: "",
age: "",
mobile: "",
email: "",
pstation: individual || "",
});
const handleChange = (e) => {
const { name, value } = e.target;
if (name === 'mobile') {
setPhoneNumber(value);
}
setForm({ ...form, [name]: value });
};
const toast = useToast()
const handleSendSMS = async () => {
try {
const response = await axios.get('http://localhost:4000/send-text', {
params: {
recipient: phoneNumber
},
});
console.log('SMS Sent:', response.data);
} catch (error) {
console.error('Error sending SMS:', error.response?.data || error.message);
}
};
const handleFormSubmit = async (e) => {
e.preventDefault();
toast.promise(handleSubmit(form), {
success: { title: "Visit Marked", description: "Looks great" },
error: { title: "Error", description: "Something wrong" },
loading: { title: "Marking Your Visit", description: "Please wait" },
});
handleSendSMS();
setForm({
name: "",
age: "",
email: "",
mobile: "",
pstation: individual || "",
});
};
return (
<div className="flex flex-col justify-center items-center">
<div className="lg:w-[50%] w-[90%] mx-auto mt-28">
<form
className="mt-12 flex flex-col px-2 gap-8 lg:mb-10 bg-slate-50 rounded-md shadow-md shadow-[#5e5d5d]"
style={{
background: `url(${formBG})`,
backgroundSize: "cover",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
}}
id="visitForm"
onSubmit={handleFormSubmit}
>
<label className="flex mt-4 flex-row justify-center items-center gap-[10%]">
<span className="w-[20%] font-bold">Full Name:</span>
<input
type="text"
required
name="name"
value={form.name}
onChange={handleChange}
className="bg-transparent border-[1px] border-black rounded-xl p-2"
/>
</label>
<label className="flex flex-row justify-center items-center gap-[10%]">
<span className="w-[20%] font-bold">Age:</span>
<input
type="number"
required
name="age"
value={form.age}
onChange={handleChange}
className="bg-transparent border-[1px] border-black rounded-xl p-2"
/>
</label>
<label className="flex flex-row justify-center items-center gap-[10%]">
<span className="w-[20%] font-bold">Mobile:</span>
<input
type="text"
required
name="mobile"
value={form.mobile}
onChange={handleChange}
className="bg-transparent border-[1px] border-black rounded-xl p-2"
/>
</label>
<label className="flex flex-row justify-center items-center gap-[10%]">
<span className="w-[20%] font-bold">Email:</span>
<input
type="text"
required
name="email"
value={form.email}
onChange={handleChange}
className="bg-transparent border-[1px] border-black rounded-xl p-2"
/>
</label>
<label className="flex flex-row justify-center items-center gap-[10%]">
<span className="w-[20%] font-bold">Police Station: </span>
<select
name="pstation"
form="visitForm"
value={individual}
onChange={handleChange}
disabled
className="bg-transparent border-[1px] border-black rounded-xl p-2"
>
{PoliceData.map((data) => (
<option key={data.id} value={data.name}>
{data.name}
</option>
))}
</select>
</label>
<div className="flex flex-row justify-center items-center m-5">
<button
type="submit"
className="bg-[#f7bc6a] w-[200px] p-2 rounded-xl duration-300 hover:bg-[#d5a96a]"
>
Submit
</button>
</div>
</form>
</div>
</div>
);
};
export default NewVisit;
I have also created an index.js file for my backend which is located in the server named directory which in turn is located in my root folder along with other directories such as src and public. I have also created a separate package.json file for server inside it's directory. Here is the code for the abovementioned index.js file:
const express = require('express');
const cors = require('cors');
const twilio = require('twilio');
const accSid = process.env.REACT_APP_TWILIO_ACCOUNT_SID;
const authToken = process.env.REACT_APP_TWILIO_AUTH_TOKEN;
const client = new twilio(accSid, authToken);
const app = express();
app.use(cors());
app.get('/', (req, res) => {
res.send('Welcome to the express server of hackathon project');
});
app.get('/send-text', (req, res) => {
const { recipient, polStation } = req.query;
console.log('SMS request received:', { recipient });
client.messages.create({
body: `Thank you for vising Police Station. \nYou are requested to kindly fill the feedback form by clicking on the url: https://feedback-system-police-private.vercel.app/myVisits after your visit. It is mandatory to fill the feedback form. \nRegards`,
to: recipient,
from: '+19287560963' // From Twilio
})
.then((message) => {
console.log(message.body);
res.send('SMS sent successfully');
console.log(recipient);
})
.catch((error) => {
console.error('Error sending SMS:', error);
res.status(500).send(`Error sending SMS: ${error.message}`);
console.log(recipient);
});
});
const port = process.env.PORT || 4000;
app.listen(port, () => console.log(`Running on port ${port}`));
below is the console error that I am getting after deploying on vercel:
I'd say you get this error because you hard-coded axios.get('http://localhost:4000/send-text')
in your Next.js app that is then deployed to Vercel. The Twilio side of things, however, runs on your local express server that is not deployed along with the rest of the application.
Your browser won't allow you to request another server running on localhost, hence the ECONNREFUSED
error. With Next.js, you're already using a framework that is capable of running the Twilio-related code as a server action on Vercel. I recommend that you follow this approach.
import twilio from "twilio";
import { SubmitButton } from "./submit-button";
const client = twilio(process.env.ACCOUNT_SID, process.env.AUTH_TOKEN);
export default function Home() {
console.log("Access to second page");
async function send(data: any) {
"use server";
const recipe = await client.messages.create({
to: `whatsapp:${data.get("phone")}`,
from: process.env.TWILIO_SENDER,
body: data.get("message"),
contentSid: data.get("message") ? undefined : process.env.CONTENT_SID,
});
function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
console.log(`Sent message ${recipe.sid}`);
await sleep(5000);
}
return (
<div className="flex flex-col items-center justify-center h-screen text-sm text-white font-medium ">
<div className="bg-slate-500 p-6 rounded shadow-lg w-10/12 h-10/12">
<h1 className="text-2xl m-2 mb-4">Send a WhatsApp</h1>
<form action={send}>
<div>
<label htmlFor="phone" className="block m-2">
Phone number
</label>
<input
type="tel"
id="phone"
name="phone"
className="rounded-lg w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 "
placeholder="+49 151 12341234"
pattern="^(\+49|0)(1\d{2}|(15|16|17|18|19)\d)(\s?\d{3,4}){2}$"
required
></input>
</div>
<label htmlFor="message" className="block m-2">
Your message
</label>
<textarea
id="message"
name="message"
className="rounded-lg w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400"
placeholder="Write your message here..."
></textarea>
<SubmitButton />
</form>
</div>
</div>
);
}
You can find the full source code on GitHub.