I'm working on a fullstack app with Express and React. For the calls to the backend, I use axios (version 1.1.2). Before this version, I was using a function to avoid writing the same calls to the database every time. Now, I get this error:
POST http://localhost:5005/api/auth/signup 400 (Bad Request)
Where does it come from?
This is my non working code:
const API_URL = process.env.REACT_APP_API_URL
export default axios.create({
baseURL: `${API_URL}/api`,
timeout: 1000,
headers: {
"Content-type": "application/json",
},
})
// Here, http refers to the axios.create function
class AuthService {
signup(data: any) {
return http.post("/auth/signup", data)
}
}
const handleSubmit = (e: React.ChangeEvent<HTMLFormElement>) => {
authService
.signup(inputs)
...rest
}
But this is working:
const handleSubmit = (e: React.ChangeEvent<HTMLFormElement>) => {
axios
.post(`${API_URL}/api/auth/signup`, inputs)
...rest
}
Thanks for your help!
EDIT:
When I submit the form, on the front end I get this error, which on the back end would be returned if an input is empty:
Please provide your full name.
Back end code:
if (!fullName) {
return res
.status(400)
.json({ message: "Please provide your full name." })
}
EDIT 2:
I tried to add a console.log on the back end with the req.body and this is what I get:
{}
This is the full backend code:
router.post("/signup", (req, res, next) => {
const { email, fullName, password } = req.body
console.log(req.body)
if (!fullName) {
return res
.status(400)
.json({ message: "Please provide your full name." })
}
User.findOne({ email })
.then(foundUser => {
...rest
return User.create({
email,
fullName,
password,
}).then(createdUser => {
const payload = { user: createdUser }
const authToken = jwt.sign(
payload,
process.env.TOKEN_SECRET,
jwtConfig
)
res.status(201).json({
user: createdUser,
authToken: authToken,
})
})
})
.catch(err => console.log(err))
})
And my terminal returns this:
POST /api/auth/login 401 16.919 ms - 39
EDIT 3:
This is my Express app.js:
require("dotenv/config")
require("./db")
const express = require("express")
const app = express()
require("./config")(app)
const allRoutes = require("./routes/index")
app.use("/api", allRoutes)
require("./error-handling")(app)
module.exports = app
You can find the full repo here: https://github.com/JulSeb42/tsx-express-jwt
Related
The code works properly and redirects to the stripe checkout page but after deployment, it doesn't. I'm getting the status 500 when trying to checkout but the cart items and amount get posted in stripe logs with the status unpaid.
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at new NodeError (node:internal/errors:387:5)
at ServerResponse.setHeader (node:_http_outgoing:603:11)
at NodeNextResponse.setHeader (C:\Users\subash\OneDrive\Desktop\ecommerce\ecommerce\node_modules\next\dist\server\base-http\node.js:56:19)
Here's the code
lib/getStripe.js
import {loadStripe} from '#stripe/stripe-js';
let stripePromise;
const getStripe = () => {
if(!stripePromise){
stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY);
}
return stripePromise;
}
export default getStripe;
cart.js
const handleCheckout = async () => {
const stripe = await getStripe();
const response = await fetch('/api/stripe', {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(cartItems)
});
if (response.statusCode === 500) return;
const data = await response.json();
toast.loading("Redirecting...");
stripe.redirectToCheckout({ sessionId: data.id });
};
pages/api/stripe.js
import Stripe from 'stripe';
const stripe = new Stripe(process.env.NEXT_PUBLIC_STRIPE_SECRET_KEY);
export default async function handler(req, res) {
if (req.method === 'POST') {
try {
// Create Checkout Sessions from body params.
const params = {
submit_type :'pay',
mode:'payment',
payment_method_types:['card'],
shipping_address_collection: {
allowed_countries: ['IN'],
},
shipping_options: [
{shipping_rate: '...'}
],
line_items: req.body.map((item)=>{
const img = item.image[0].asset._ref;
const newImage = img.replace('image-','https://cdn.sanity.io/..../').replace('-webp','.webp');
return {
price_data:{
currency:'inr',
product_data:{
name:item.name,
images:[newImage],
},
unit_amount:item.price*100,
},
adjustable_quantity:{
enabled:true,
minimum:1
},
quantity:item.quantity
}
}),
success_url: `${req.headers.origin}/success`,
cancel_url: `${req.headers.origin}`,
}
const session = await stripe.checkout.sessions.create(params);
res.status(200).json(session);
res.redirect(303, session.url);
} catch (err) {
res.status(err.statusCode || 500).json(err.message);
}
} else {
res.setHeader('Allow', 'POST');
console.log("error");
res.status(405).end('Method Not Allowed');
}
}
You're still using the legacy Checkout integration with redirectToCheckout instead you should look at the new integration path for Next.js that you can find here. If you want more info about migrating from the legacy integration you can check the Checkout migration guide here.
I'm new with React and I was trying to separate my Axios http utilities in different files.
It works fine if I login with correct credentials but when I try wrong credentials and my API responses with a 401 (Bad Credentials) message Axios execute the then() method instead of the catch() method.
axios.ts
import Axios from "axios";
const JSON_CONTENT_TYPE = "application/json";
// axios configuration
const axios = Axios.create({
baseURL: process.env.REACT_APP_API_URL,
responseType: "json"
});
// ... other requests handlers
export const post = <T = any>(
url: string,
body: any,
params?: any,
contentType = JSON_CONTENT_TYPE
) => {
return axios.post<T>(url, body, {
params,
headers: { "Content-Type": contentType }
});
};
login-adapter.ts
import { ILogin } from "../../model/login.model";
import * as Http from "../axios";
import * as StorageManager from "../storage-manager";
type TokenBody = {
id_token: string;
};
export const login = (credentials: ILogin) => {
return new Promise((resolve, reject) => {
Http.post<TokenBody>("/authenticate", credentials)
.then((resp) => {
// Stores jwt in local/session storage.
// HERE IS WHEN MY APP CRASHES, The error says 'resp is undefined' and THIS ERROR (not the response from my API) is caught by the catch method below.
StorageManager.setToken(resp.data.id_token, credentials.rememberMe);
// Does another request to get user info.
Http.get("/account").then(console.log);
resolve("Success");
})
.catch((error) => reject("Error, " + error.response.data)); // THIS SHOULD SEND THE ERROR MESSAGE TO LoginPage.tsx
});
};
LoginPage.tsx
import { FormEvent, useState } from "react";
import "../../styles/LoginPage.css";
import * as LoginAdapter from "../../adapters/loginAdapters/login-adapter";
import { RouteComponentProps } from "react-router-dom";
const LoginPage = ({history}: RouteComponentProps) = {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [rememberMe, setRememberMe] = useState(false);
const submit = (e: FormEvent<HTMLFormElement>) => {
LoginAdapter.login({ email, password, rememberMe })
.then(() => {
history.push("/admin/courses");
})
.catch((error) => {
console.log(error);
});
e.preventDefault();
}
return (
<form onSubmit={submit}>
{/* <input ... email, password, and 'remember me' form fields.*/}
</form>
);
}
export default LoginPage;
When I use axios directly from the package. (import axios from "axios") it works perfectly. But I have to rewrite my api endpoint, response type, interceptors, etc. I don't know why it is not working, Am I missing something?
My interceptor was the problem, I didn't notice that the error handler should return a Promise with a reject reason.
axios.interceptors.response.use(
(resp) => resp,
(error) => {
if (
error.response.status === 401 &&
error.response.config.url !== "/account"
) {
LoginAdapter.logout();
}
// before: <nothing>
// now:
return Promise.reject(error);
}
);
I am stuck on this problem for 2 days. I am sending POSTrequest from frontend to the backend (and other GET requests too but the problem is only with POST). However, when my data goes to the backend it does not post anything to the rest api even though response is 200 OK. That's why when in response it should have given the posted data, it can't find it and gives null. This is my POST code in backend index.js:
const { response, request } = require('express');
require('dotenv').config()
const express = require('express');
const morgan = require('morgan');
const Contact = require('./models/contact.cjs');
const cors = require('cors')
const app = express();
app.use(express.json())
app.use(express.static('build'))
app.use(cors())
morgan.token('body', req => {
return JSON.stringify(req.body)
})
app.use(morgan(':method :url :status :res[content-length] - :response-time ms :body'));
const generateId = () => {
const randNum = Math.floor(Math.random() * 5000)
return randNum;
}
app.post('/api/persons', (req, res) => {
const body = req.body
console.log(body)
if (!body.name || !body.number) {
return res.status(400).json({
error: "missing data"
})
} else if (Contact.find({name: body.name})) {
Contact.findOneAndUpdate({name: body.name}, {$set: {number: body.number}}, {new:true})
.then(updatedContacts =>
res.json(updatedContacts)
)
.catch(err => console.log(err))
} else {
const contact = Contact({
id: generateId(),
name: body.name,
number: body.number,
date: new Date()
})
contact.save()
.then(savedContact => {
console.log(savedContact)
res.json(savedContact)
})
.catch(err => {
console.log(err)
})
}
})
const PORT = process.env.PORT
app.listen(PORT, () => {
console.log(`Server is working on ${PORT}`)
})
and this is how my frontend sends data to backend: contacts.js:
const create = (newObject) => {
const readyToPost = {
method: 'post',
url: `${baseUrl}`,
data: newObject,
headers: {'Content-Type': 'application/json'},
json: true
}
const request = axios(readyToPost)
return request.then(response => {
console.log(response.data)
return response.data
})
.catch(err => {
console.log(err)
})
}
And this is my react app's frontend.
Any ideas about why my data becomes null?
Any help would be appreciated!
Due to the synchronous nature of your code, the condition Contact.find({name: body.name}) was always returning the Query object which is true due to which the else if block was getting executed even when there was no such document. After entering the else if block, since there was no match, so findOneAndUpdate() was returning null.
Use findOne() instead of find(). find() returns a cursor which is empty but true whereas findOne() returns the first document matched (if matched) or else it will return null (if not matched).
// index.js (Backend)
app.post("/api/persons", async (req, res) => {
const body = req.body;
if (!body.name || !body.number) {
return res.status(400).json({
error: "missing data",
});
}
// Using findOne() instead of find(). Returns null if record not found.
const existing = await Contact.findOne({ name: body.name });
if (existing) {
Contact.findOneAndUpdate(
{ name: body.name },
{ $set: { number: body.number } },
{ new: true }
)
.then((updatedContacts) => {
console.log(updatedContacts);
res.status(200).json(updatedContacts);
})
.catch((err) => console.log(err));
} else {
const contact = Contact({
id: generateId(),
name: body.name,
number: body.number,
date: new Date(),
});
contact
.save()
.then((savedContact) => {
console.log(savedContact);
res.status(201).json(savedContact);
})
.catch((err) => {
console.log(err);
});
}
});
This is the first project I've built from scratch and first time deploying anything with AWS, ubuntu, nginx. The app is built with postgres, express/node, and react.
When I click the login button in production, I get this error: POST http://18.216.221.221/dashboard/api/1/login 405 (Not Allowed)
But in development the POST request works fine and goes to http://localhost:5000/api/1/login
I can't figure out why in production it's adding the "dashboard" to the URL.
This is the server route:
app.post('/api/1/login', async (req, res)=>{
if (!req.body.username || !req.body.password) {
return res.status(400).json('missing fields')
}
try {
const result = await db.query("select * from login where username = $1", [req.body.username])
const isValid = bcrypt.compareSync(req.body.password, result.rows[0].hash);
if (isValid) {
res.status(200).json({
status: 'login successful',
data: {
user: result.rows[0].username
}
})
} else {
res.status(400).json({
status: 'login failed'
})
}
} catch(error) {
console.log('unable to login')
}
})
And this is the function for handling the login:
const handleLogin = async (e) => {
e.preventDefault()
try {
const response = await ClientFilesApi.post(`/login`, {
username,
password
})
console.log(response)
if (response.data.data.user === 'myemail') {
setUserLoggedIn(true)
setPassword("")
history.push(`/dashboard`)
}
} catch (err) {
console.log(err)
}
}
I've set my api baseURL like this:
const baseURL = process.env.NODE_ENV === 'production' ? "api/1" : "http://localhost:5000/api/1"
export default axios.create({
baseURL
})
I've followed all the instructions for deploying from this tutorial:
https://github.com/Sanjeev-Thiyagarajan/PERN-STACK-DEPLOYMENT
And this is my source code:
https://github.com/cipdv/ciprmt
I'm learning how to test a frontend webapp without any connection to the API.
My problem is: I have to test an POST HTTP Request but always get an error : TypeError: loginUser(...).then is not a function.
I know my expect is not correct. I must change the data for a JWT token, and also don't know yet hot to do it.
It's a simple user authentication. Http post sending an email and password, getting back a JWT (json web token). I have to write a test to make sure I've send the correct information and get a JWT as response.
Thanks for your help
Here is my code:
//login.test.js
const expect = require('chai').expect;
const loginUser = require('../src/actions/authActions').loginUser;
const res = require('./response/loginResponse');
const nock = require('nock');
const userData = {
email: 'test#test.com',
password: '123456'
};
describe('Post loginUser', () => {
beforeEach(() => {
nock('http://localhost:3000')
.post('/api/users/login', userData )
.reply(200, res);
});
it('Post email/pwd to get a token', () => {
return loginUser(userData)
.then(res => {
//expect an object back
expect(typeof res).to.equal('object');
//Test result of name, company and location for the response
expect(res.email).to.equal('test#test.com')
expect(res.name).to.equal('Tralala!!!')
});
});
});
//authActions.js
import axios from "axios";
import setAuthToken from "../utils/setAuthToken";
import jwt_decode from "jwt-decode";
import {
GET_ERRORS,
SET_CURRENT_USER,
USER_LOADING
} from "./types";
// Login - get user token
export const loginUser = userData => dispatch => {
axios
.post("/api/users/login", userData)
.then(res => {
// Save to localStorage
// Set token to localStorage
const { token } = res.data;
localStorage.setItem("jwtToken", token);
// Set token to Auth header
setAuthToken(token);
// Decode token to get user data
const decoded = jwt_decode(token);
// Set current user
dispatch(setCurrentUser(decoded));
})
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
// loginResponse.js
module.exports = { email: 'test#test.com',
password: '123456',
name: "Tralala!!!"
};
Actual result:
1) Post loginUser
Post email/pwd to get a token:
TypeError: loginUser(...).then is not a function
at Context.then (test/login.test.js:37:12)
The way you called loginUser method is not correct. This method returns another function. So, instead of loginUser(userData), you must also specify the dispatch parameter e.g. loginUser(userData)(dispatch).then().
I changed the method to specify return before axios statement
export const loginUser = userData => dispatch => {
return axios // adding return
.post("/api/users/login", userData)
.then(res => {
...
})
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
for test, I may involve Sinon to spy the dispatch
it("Post email/pwd to get a token", () => {
const dispatchSpy = sinon.spy();
return loginUser(userData)(dispatchSpy).then(res => {
//expect an object back
expect(typeof res).to.equal("object");
//Test result of name, company and location for the response
expect(res.email).to.equal("test#test.com");
expect(res.name).to.equal("Tralala!!!");
});
});
Hope it helps