NEXT.js API route wont accept POST requests - reactjs

I have a Next.js API route set up at "pages/api/contact". When I process a GET request or navigate to localhost:3000/api/contact I can see the api is working. However, whenever I try to process a get request to this API route nothing happens. I have tried using axios and fetch but nothing seems to work for post request. See below. Any help would be greatly appreciated.
called from a component when a button is clicked
const handleClick = async (e) => {
e.preventDefault();
console.log("in handleSubmit");
try {
const res = await axios.post(
"http://localhost:3000/api/contact",
{
firstName,
lastName,
email,
},
{
headers: {
"Content-Type": "application/json",
},
},
console.log(res) //this comes back undefined
);
} catch (e) {}
};
in pages/api/contact
export default async function sendEmail(req, res) {
const { firstName, lastName, email } = req.body;
console.log(req.method);
if (req.method === "POST") {
return res.status(200).json({
message: "This is in post",
});
} else {
return res.status(200).json({
message: "This is not a post",
});
}
}

I think it syntax error
try {
const res = await axios.post(
"http://localhost:3000/api/contact",
{
firstName,
lastName,
email,
},
{
headers: {
"Content-Type": "application/json",
},
},
);
console.log(res) //check now
} catch (e) {}

Related

Redirecting to a React Component - Express

I have a Login component on the front in which I make a POST to a rout on the server:
const handleSubmit = async (e) => {
e.preventDefault();
try {
fetch("http://localhost:3000/login", {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({
email,
password,
}),
});
} catch (error) {
console.log(error.message);
}
};
On the server side, I validate the information sended from the front:
app.post("/login", async (req, res) => {
const user = await Users.findOne({
where: { email: req.body.email, password: req.body.password },
});
if (user) {
console.log(user);
console.log("usuario logeado");
res.status(200).send("LOGGED");
} else {
console.log("Usuario no Registrado");
}
});
I want to redirect to a component Home on the front once I validate the user and idk how to do it.
You need to read the response from the server and figure out what you want to do with it. I'd recommend not sending just a string from the server, so this solution will restructure a bit to hopefully help you understand and expand for your own needs.
// From your server - we're sending some json that we can
// read in the front end code. Maybe you also want to send
// the user object or other data, and json is great for that.
app.post("/login", async (req, res) => {
const user = await Users.findOne({
where: { email: req.body.email, password: req.body.password },
});
if (user) {
console.log(user);
console.log("usuario logeado");
res.status(200).send({
isLoggedIn: true,
});
} else {
console.log("Usuario no Registrado");
res.status(200).send({
isLoggedIn: false,
});
}
});
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await fetch("http://localhost:3000/login", {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({
email,
password,
}),
});
// Get the server response as json
const data = await response.json()
if(data.isLoggedIn) {
// Redirect to the account
window.location.href = "/account"
} else {
// Show some error message
window.alert("Invalid login")
}
} catch (error) {
console.log(error.message);
}
};

Error: Cannot read property 'token' of undefined

I have a function api inside the method onSubmit, which make request to the server:
onSubmit: async (formValues) => {
setSubmitting(true);
try {
const res = await api('api/auth/register', {
method:'POST',
body: JSON.stringify(formValues)
});
if(Array.isArray(res)){
setErrorMessage(res[0].message);
} else {
const token = res.token.token;
localStorage.setItem('myToken', token);
history.push("/home");
}
} catch(e) {
console.error(e);
} finally {
setSubmitting(false);
}
},
});
Function api is in a separate file and looks like this:
export const api = async (url, args) => {
const response = await fetch(`${apiUrl}${url}`, {
...args,
headers: {
"Content-type": "application/json; charset=UTF-8 ",
"Accept": 'application/json',
...args.headers,
},
});
return response.json();
}
This function was created simply for convenience. But now I dont need this function. I need that code from api was inside method onSubmit. That is, that the function api not exist at all.
And I did it:
onSubmit: async (formValues) => {
setSubmitting(true);
try {
const resf = await fetch(`${apiUrl}api/auth/register`, {
method:'POST',
body: JSON.stringify(formValues),
headers: {
"Content-type": "application/json; charset=UTF-8 ",
"Accept": 'application/json',
}
});
const res = resf.json();
if (Array.isArray(res)){
setErrorMessage(res[0].message);
} else {
const token = res.token.token;
localStorage.setItem('myToken', token);
history.push("/home");
}
} catch(e) {
console.error(e);
} finally {
setSubmitting(false);
}
},
});
But I have error:
TypeError: Cannot read property 'token' of undefined
Why is this error occurring?
Try with await for resf.json() because calling .json() gets you another promise for the body of the http response that is yet to be loaded.
const res = await resf.json();

Generic function to request api with Axios

I am trying to build a generic function for my endpoints, using Axios and React. Generic because I have always the same header and I do not want to repeat a lot of code for each of my components.
To do that, I built this function (sorry, a lot of comments that I will remove after of course) :
export const getRequest = ( endpoint ) => axios
.get( env._URL_SERVER_ + endpoint, { headers: getHeaders() } )
.then((res) => {
// Success
console.log(res);
return {error: false, response: res.data};
})
.catch((error) => {
// Error
if (error.response) {
/*
* The request was made and the server responded with a
* status code that falls out of the range of 2xx
*/
console.log(error.response.data);
console.log(error.response.status);
return {error: true, status: error.response.status, data: error.response.data};
} else if (error.request) {
/*
* The request was made but no response was received, `error.request`
* is an instance of XMLHttpRequest in the browser and an instance
* of http.ClientRequest in Node.js
*/
console.log(error.request);
return {error: true, data: error.request };
} else {
// Something happened in setting up the request and triggered an Error
console.log('Error', error.message);
return {error: true, data: error.message}
}
});
Ant then in my components I do that :
getSchools = () => {
this.setState({
loadingSchools: true
}, () => {
getRequest(`/schools?name=${this.state.filterByName}&city=${this.state.filterByCity}&school_type_id=${this.state.filterBySchoolTypeId}&page=${this.state.selectedPage}`)
.then((response) => {
// there is an error
if (!response.error) {
this.setState({
schools: response.response.data,
meta: response.response.meta,
links: response.response.links
})
} else {
this.setState({
error: true,
errorMessage: response.data,
})
}
})
.then(() => {
this.setState({loadingSchools : false});
})
})
}
It works fine. I tested it in several situation (all is OK - 200, not found - 404, no response). But is it a good practice ? I feel that there is a lot of codes in the parent component. Maybe I complicate my life?
Here is how I've done it:
var URL_BACKEND = "http://localhost:5000/";
// Create Function to handle requests from the backend
callToBackend = async (ENDPOINT, METHOD) => {
const options = {
url: `${URL_BACKEND}${ENDPOINT}`,
method: METHOD,
headers: {
Accept: "application/json",
"Content-Type": "application/json;charset=UTF-8",
},
};
const response = await axios(options);
return response.data;
}
// Then you make a call with the exact endpoint and method:
const response = await this.callToBackend('createSetupIntent', 'POST');
console.log(JSON.stringify(response));
create one common file for base URL let's say api.js
// api.js file code
export const apiUrl = axios.create({
baseURL: 'http://localhost:5000',
});
Register file
// register.js file code
import { apiUrl } from './api';
try {
const resp = await apiUrl.post('/api/register', {
username,
email,
password,
});
const { data, status } = resp;
if (Object.keys(data).length && status === 200) {
// received api data successfully
console.log('API response', data);
}
} catch (err) {
console.log(err);
}
// For auth request
try {
const token = localstorage.getItem('token');
const res = await apiUrl.post(
'/authroute',
{
name: fullName,
originCountry: country,
career: careerStatus,
},
{
headers: { Authorization: `Bearer ${token}` },
}
);
const { data, status } = strapiRes;
if (Object.keys(data).length && status === 200) {
return res.status(status).json(data);
}
} catch (error) {
throw new Error(error);
}
// same for all request
apiUrl.get(endpoint);
apiUrl.post(endpoint, body);
apiUrl.put(endpoint, body);
apiUrl.delete(endpoint, body);

How to send token through headers by using axios post method in react

In my react app i am using axios to perform the REST api requests.
But it's unable to send the Authorization header with the request.
Here is my code:
This is authentication.js
async login(data) {
try {
const res = await axios.post(`'http://localhost:5000'/api/login`, data);
this.subject.next(true);
return res;
} catch (error) {
throw error;
}
}
This is login.js
async handleSubmit(e) {
e.preventDefault();
try {
const res = await auth.login(this.state.data);
tokenService.saveToken(res.data.token);
this.setState({});
swal({
title: "Good job!",
text: "Login successfully!",
icon: "success",
});
}
catch (error) {
swal({
title: "incorrect or password!",
text: "Login failed!",
icon: "error",
});
}
}
You can use Axios to create an instance of it with the headers passed to it save in local storage. Then, use that instance to further make requests. In this way, you don't to include it in every request.
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
headers: {'Authorization': bearer <TOKEN_FROM_LOCALSTORAGE>}
});
Use the instance to make request
instance.get("users")
.then(res => {
console.log(res);
console.log(res.data);
})
You can use this instance and customize it according to your instance so that code won't repeat. For further reference
Store it in localstorage and then concatenate it with 'Bearer'
let bearer = 'Bearer ' + JSON.parse(localStorage.getItem('token'));
//payload is the data which you're trying to send to the api endpoint
axios({
method: 'post',
url: '/api-endpoint',
headers: {
Authorization: bearer
},
data: payload
})
.then(response => response.json())
.then(json => json)
.catch(error => {
throw error;
});
check if the user is authenticated to use the Get or Post requests made by them
isAuthenticated() {
const token = localStorage.getItem('token');
}
Use the token to make the post request
axios({
method: 'post',
url: ''http://localhost:5000'/api/login',
{ headers: {"authorization" : token} }
data: payload
}),
.then(response => response.json())
.then(json => json)
.catch(error => {
throw error;
});
Handle your login
async handleSubmit(e) {
e.preventDefault();
try {
const res = await auth.login(this.state.data);
tokenService.saveToken(res.data.token);
this.setState({});
swal({
title: "Good job!",
text: "Login successfully!",
icon: "success",
});
}
catch (error) {
swal({
title: "incorrect or password!",
text: "Login failed!",
icon: "error",
});
}
}
Why you don't use axios interceptors like this:
axiosInstance.interceptors.request.use(
config => {
config.headers.authorization = localStorage.getItem("token");
return config;
},
error => Promise.reject(error)
);
Or declared on https://github.com/axios/axios/issues/1383

Proxy to express - 500-timeout - Server code is executed more then once

I don't know what is going on. When i try to send request to the backend to add follower(my route bellow), I get server tiemout error instead of sucess, but in my database the follower is added correctly(and removed), buuuut not always. Sometimes it saves 3 times the same result(follower to db), or sometimes doesn't delete the follower.
And the problem is that i have no idea what's is going on.
In my console i have this error sometimes i see this:
[HPM] Error occurred while trying to proxy request /api/users/user/follow from 127.0.0.1:8080 to http://[::1]:1648 (ECONNRESET) (https://nodejs.org/api/errors.html#errors_common_system_errors)
setFollower route:
const setFollowing = async (req, res, next) => {
try {
const userId = req.body.userId;
const followId = req.body.followId;
await User.findByIdAndUpdate(
userId,
{
$push: {
following: followId,
},
},
);
next();
} catch (err) {
res.status(400).json({
error: err,
});
}
};
const setFollower = async (req: Request, res: Response) => {
try {
const followId = req.body.followId;
const userId = req.body.userId;
const result = await User.findByIdAndUpdate(
followId,
{
$push: {
followers: userId,
},
},
{ new: true },
)
.populate('following', '_id name')
.populate('followers', '_id name')
const followerResult = { ...result._doc };
const { photo, salt, passwordHash, ...rest } = followerResult;
return res.status({ ...rest });
} catch (err) {
res.status(400).json({
error: err,
});
}
};
router.put(
'/user/follow',
isUserSignIn,
setFollowing,
setFollower,
);
sending request on button click
try {
setLoading(true);
const response = await fetch('/api/users/user/follow', {
body: JSON.stringify({
followId: params.userId,
userId: loggedInUser._id,
}),
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
method: 'PUT',
});
const data = await response.json();
setLoading(false);
setFollowing(true);
} catch (err) {
if (err.message) {
setServerError(err.message);
} else {
setServerError(JSON.stringify(err));
}
}
my repo: https://github.com/bartek-fecko/fullstackapp
for my assumption, you're using express, given the logs you have in your question. The
key is to set the timeout property on server (the following sets the timeout to one
second, use whatever value you want):
var server = app.listen(app.get('port'), function() {
debug('Express server listening on port ' + server.address().port);
});
server.timeout = 1000;

Resources