Correct Async/Await usage with Axios - node.js

I'm working with axios to post user responses to a database. I'm using this set up shown below to handle many posts back to back. I'm wanting to make sure that this is the correct set up to avoid backing up requests.
Is this the correct way to use async and await when using Axios?
// Frontend React Code
// Posting conclusionInput to Mongodb
const postConclusion = async () => {
await axios({
method: "POST",
data: {
conclusion: conclusionInput,
},
withCredentials: true,
url: "http://localhost:4000/conclusion",
}).then((res) => console.log(res));
};
//Backend Node / Express code
app.post("/conclusion", (req, res) => {
console.log("Attempting to post the conclusion");
User.findOne({ username: req.user.username }, async (err, user) => {
if (err) throw err;
if (user) {
(user.conclusion = req.body.conclusion), await user.save();
res.send();
}
});
});

Frontend
In an async function use await and try/catch. Any .then calls can be rolled out into const x = await y variables.
Return values from promise functions, in case you want to use them.
const postConclusion = async () => {
const res = await axios({
method: "POST",
data: {
conclusion: conclusionInput,
},
withCredentials: true,
url: "http://localhost:4000/conclusion",
})
console.log(res)
return res
};
Backend
Again, if you are going with async use that API consistently.
Mongoose provides a promise API, so use that too.
app.post("/conclusion", async (req, res) => {
try {
console.log("Attempting to post the conclusion");
const user = await User.findOne({ username: req.user.username })
if (!user) {
return res.send('not found')
}
user.conclusion = req.body.conclusion
await user.save()
return res.send('saved')
}
catch (error) {
console.error(error)
return res.send('error')
}
});

When using async await, setting an await call to a variable is equal to the parameter in a .then callback
// Frontend React Code
// Posting conclusionInput to Mongodb
const postConclusion = async () => {
// Set the await call to a variable.
const res = await axios({
method: "POST",
data: {
conclusion: conclusionInput,
},
withCredentials: true,
url: "http://localhost:4000/conclusion",
})
// Now no need for .then()!! This is why async/await is so nice.
console.log(res)
};
//Backend Node / Express code
app.post("/conclusion", (req, res) => {
console.log("Attempting to post the conclusion");
User.findOne({ username: req.user.username }, async (err, user) => {
// You need to send the error to the request. Otherwise, the
// request will keep waiting for a response until it times out.
if (err) {
console.error(err)
res.status(500).send(err)
}
if (user) {
// Side note: these should all be on separate lines:
user.conclusion = req.body.conclusion
await user.save();
// You should also send a status code and a response message
res.status(200).send({status: "Success}");
}
});
});

I recommended have a folder called "services" and inside it has yours services by backend.
services/
getData.js
import axios from "axios";
export const getData = () => {
axios.post("http://localhost:4000/conclusion");
};
App.js
import { useEffect, useState } from "react";
import { getData } from "./services/getData";
export default function App() {
const [data, setData] = useState([]); // save the value of service
useEffect(() => {
try {
getData().then((res) => {
setData(res?.data);
});
} catch (e) {
console.error(e);
}
}, []); // execute once
return <div className="App">{data}</div>;
}

Related

Node Express server not send messages with whatsapp-web-js

i have a problem with my custom server, i'm trying to setup an api that send whatsapp messages in react as a frontend.
So, i have actually a route that send the QR to the frontend (working fine), a route that handle the authentication event (working fine) and
PROBLEM HERE:
...a route that send a message to a specific number (NOT WORK)
here my server code...what i'm doing wrong? if i launch a POST request to the endpoint on postman, i get an infinite loading (no errors).
const express = require('express')
const router = express.Router()
const fs = require('fs')
const { Client, LocalAuth } = require('whatsapp-web.js')
const authStrategy = new LocalAuth({
clientId: 'adminSession',
})
const worker = `${authStrategy.dataPath}/session-admin/Default/Service Worker`
if (fs.existsSync(worker)) {
fs.rmdirSync(worker, { recursive: true })
}
const client = new Client({
takeoverOnConflict: true,
authStrategy,
})
const sessionData = {
client: 'admin',
session: true,
qrCodeScanned: true,
}
client.on('authenticated', (session) => {
fs.writeFile(
'waSession.json',
JSON.stringify(sessionData),
'utf-8',
(err) => {
if (!err) {
console.log('Session saved on disk...')
}
}
)
})
router.get('/whatsapp/auth', async (req, res) => {
const dir = './waSession.json'
fs.readFile(dir, (err, data) => {
if (data.length === 0) {
return res.status(200).json({
message: 'You need to login first',
})
} else {
return res.status(200).json({
message: 'You are logged in.',
})
}
})
})
router.get('/whatsapp', async (req, res) => {
try {
client.on('qr', (qr) => {
res.status(200).send({
message: 'Connect whatsapp with this qr-code',
qrCode: qr,
})
})
await client.initialize()
res.status(404)
} catch (err) {
res.send(err)
}
})
router.post('/whatsapp/send', async (req, res) => {
const { phoneNumber, message } = req.body
try {
client.on('ready', async () => {
const number = phoneNumber
const text = message
const chatId = number.substring(1) + '#c.us'
await client.sendMessage(chatId, text)
})
await client.initialize()
res.json('Messaggio inviato')
} catch (err) {
res.status(404).send(err)
await client.destroy()
}
})
module.exports = router
A Client represents one authenticated WhatsApp user, and you have only one global variable client. This implies that all incoming requests will represent the same WhatsApp user, even if several different real users send requests to your server in parallel. This is probably not what you intend.
I suggest that you use express-session to associate every client with a session. Then a user needs to create a client and authenticate it only once during a session. All subsequent requests in the same session will re-use that client, and the client.on(...) and client.initialize() commands will not be repeated.

Jest spyOn.mockImplementation calls actual method

I have a middleware function:
// middlewares/auth.middleware.ts
export async function verifyUser(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
try {
// verify token logic
req.user = user; // I add the verified user details to req
next();
} catch (error) {
console.log(error);
res
.status(401)
.json({ error: 'You are not authorized to make this request' });
}
}
In my test, I'm doing the following:
import * as middleware from '../../middlewares/auth.middleware';
const spy = jest.spyOn(middleware, 'verifyUser');
describe('POST /api/users', () => {
test('when the user has ONLY been created in external auth', async () => {
spy.mockImplementation(
jest.fn(async (req, _res, next) => {
req.user = {
id: '1',
email: 'user1#email.com',
fullName: null,
};
next();
})
);
const res = await request(app)
.post('/api/users')
.send({ fullName: 'Jimmy Neutron' });
expect(res.status).toEqual(200);
spy.mockRestore();
});
});
However this calls the original verifyUser function, instead of the what I added.
But, if I add this to the test file then my test passes:
jest.mock('../../middlewares/auth.middleware', () => ({
verifyUser: jest.fn((req, _res, next) => {
req.user = {
id: '1',
email: 'user1#email.com',
fullName: null
};
next();
}),
}));
How can I make my code work so that I can change the implementation per test?
If you look at the jest.spyOn docs you can see this:
Note: By default, jest.spyOn also calls the spied method. This is different behavior from most other test libraries. If you want to overwrite the original function, you can use jest.spyOn(object, methodName).mockImplementation(() => customImplementation) or object[methodName] = jest.fn(() => customImplementation);
So this explains why adding a mock makes your tests pass.
Depending on what version of jest you are running, there is a very useful helper method that you can now use, jest.mocked:
jest.mocked<T>(item: T, deep = false)
In your case you could:
Add a mockImplementation to your spyOn:
const spy = jest.spyOn(middleware, 'verifyUser').mockImplementation(jest.fn((req, _res, next) => {
req.user = {
id: '1',
email: 'user1#email.com',
fullName: null
};
next();
});
// ...
Remove the spyOn entirely and refactor as follows:
import * as middleware from '../../middlewares/auth.middleware';
jest.mock('../../middlewares/auth.middleware');
const mockMiddleware = jest.mocked(middleware);
const mockVerifyUser = jest.fn(async (req, _res, next) => {
req.user = {
id: '1',
email: 'user1#email.com',
fullName: null,
};
next();
});
describe('POST /api/users', () => {
test('when the user has ONLY been created in external auth', async () => {
mockMiddleware.mockImplementation(() => ({
verifyUser: mockVerifyUser,
});
// ...
But the two approaches are equivalent.

Why mongo query is showing undefined in node js

I'm trying to check the data with findOne when im trying with the postman getting undefined in console.log , i checked with the same query in roboMongo and its showing the data
this is the result:-
Here is the code:-
exports.signIn = async( req, res ) => {
const {
userEmailPhone,
} = req.body;
await User.findOne ({ email : userEmailPhone}).then((err, user)=> {
console.log("user..", user)
if (user){
res.status(200).send({
message: "sucess"
});
}
})
}
the postman response:-
Since you are already using async - await, I believe there is no need of using the .then() block.
Your code should be updated to use async and await as below:
exports.signIn = async( req, res ) => {
const { email } = req.body;
const user = await User.findOne ({ email : userEmailPhone})
console.log("user..", user)
if (user){
res.status(200).send({
message: "sucess"
});
}
}
If you still want to use the .then() block, I would recommend making the following changes in the code:
exports.signIn = async ( req, res ) => {
const {email} = req.body;
User.findOne ({ email : email}).then((user, err)=> {
console.log("user..", user)
if (user){
res.status(200).send({
message: "sucess"
});
}
})
}
Since the promise callback for MongoDb queries has the following callback format:
.then( (res, err) => {
// do stuff
})
Reference : https://docs.mongodb.com/drivers/node/fundamentals/promises/
You are sending raw json data. First you should use app.use(bodyParser.json());. Only app.use(bodyParser()); is deprecated.
This should fix it assuming you have a json body-parser
exports.signIn = async( req, res ) => {
const {email} = req.body;
User.findOne ({ email : email}).then((err, user)=> {
console.log("user..", user)
if (user){
res.status(200).send({
message: "sucess"
});
}
})
}

Why does my register user post request fail with a 500 error?

I'm building an app with React and Node/Express, and I'm having trouble with my register user function. The data I am passing in is correct, and other endpoints work fine. The register one keeps returning a 500 error and I can't figure out why.
This is my request:
console.log(values)
axios
.post(
'https://foodtrackr-backend.herokuapp.com/api/register',
values
)
.then(res => {
console.log('res.data', res.data);
})
.catch(error => {
console.log('nope');
console.error(error);
});
};
and this is my endpoint:
router.post('/register', async (req, res) => {
let user = req.body;
const newUser = await Users.add(user);
try {
if (newUser) {
res.status(201).json(user);
} else res.status(404);
} catch (error) {
res.status(500).json('noooooo');
}
});
and this is my model:
function findById(id) {
return (
db('users')
.where({ id })
.first()
);
}
async function add(user) {
const [id] = await db('users').insert(user, 'id');
return findById(id);
}
Any help would be appreciated!

Problem with async/await and http client requests

Not sure what I'm missing here but the console.log() line prints "Promise { }" instead of the JSON body from the response.
I believe that I'm doing something wrong with async/await.
My code (Express):
async function axiosPost(url, payload) {
try {
const res = await axios.post(url, payload);
const data = await res.data;
return data;
} catch (error) {
console.error(error);
}
}
app.get('/data', (req, res) => {
data = axiosPost('http://localhost:8080', {
userpass: 'XXX',
method: 'getdata'
});
console.log(data)
res.status(200).send({
message: data
})
});
Any help is appreciated.
replace your router with this. you were not using await while making an API call. Hope it helps.
app.get('/data', async (req, res) => {
let data = await axiosPost('http://localhost:8080', {
userpass: 'XXX',
method: 'getdata'
});
console.log(data)
res.status(200).send({
message: data
})
});
You get that result because you didn't resolve the call to axiosPost() which is asynchronous. This can be solved in 2 ways, one by appending .then() to the axiosPost() call or simply awaiting it using the await keyword. See below:
async function axiosPost(url, payload) {
try {
const res = await axios.post(url, payload);
const data = await res.data; // this is not required but you can leave as is
return data;
} catch (error) {
console.error(error);
}
}
// I converted the callback to an async function and
// also awaited the result from the call to axiosPost(),
// since that is an async function
app.get('/data', async (req, res) => {
data = await axiosPost('http://localhost:8080', {
userpass: 'XXX',
method: 'getdata'
});
console.log(data)
res.status(200).send({
message: data
})
});
// OR using `then()`
app.get('/data', (req, res) => {
axiosPost('http://localhost:8080', {
userpass: 'XXX',
method: 'getdata'
}).then((data) => {
console.log(data);
res.status(200).send({
message: data
});
});
})

Resources