Is it possible to implement socket.io connection in express route? - javascript

I implement a payment service which depend on one of my express route as a callback route, so whenever a user want to make a payment, they will be redirected to this payment service link which entirely different my backend/frontend domain. After a successful payment, user will then be redirected to my express GET route (callback route), in this route is where I give users their asset and then redirect them to the frontend.
EXPECTATION
My expectation is, whenever a user make a purchase, I want a real time update on the frontend for others to see some details about the purchase without refreshing their browser.
WHAT I'VE TRIED
I had think socket.io would solve this, like adding a socket connection in the route to then push the data to the frontend. But after making lot of research, no solution seems to work for me.
HERE IS A SIMPLE CODE OF WHAT I'VE TRIED
=============================== server.js ========================
const express = require("express")
const app = express()
const http = require("http")
const cors = require("cors")
const session = require("express-session")
const runSocket = require("./runSocket")
const { Server } = require("socket.io")
app.use(cors())
app.use(express.json())
const server = http.createServer(app)
server.listen(3004, () => {
console.log("SERVER IS RUNNING")
})
const io = new Server(server, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
},
})
const postRoute = require("./routes/postData")(io)
app.use("/post-data", postRoute)
==================================== postData Route ======================================
module.exports = function (io) {
router.post("/", async (req, res) => {
const data = req?.body?.data.message
const room = req?.body?.data?.room
io.on("connection", (socket) => {
console.log("Socket Running...")
socket.to(room).emit("the_message", data)
})
console.log("Under socket...")
return res.status(200).json({ data: req.body.data })
})
return router
}
This log: in postData route is not printing console.log("Socket Running...")
EXPECTATION
My expectation is, whenever a user make a purchase, I would like to make a real time update on the frontend for others to see some details about the purchase.
UPDATE: The Payment Gateway config looks somthing like this:
const { body } = await got.post("https://payment-provider-link", {
headers: { Authorization: "Bearer token for payment" },
json: {
email: "email#gmail.com",
amount: amount * 100,
initiate_type: "inline",
callback_url: `${BackendBaseUrl}/payment-callback`, // <<<============
},
})

Okay so you don't need the io.on("connection") in ur route. Remove that piece of code and simply change it to io.to(room).emit("the_message", data). Also make sure to have the other sockets joined the room ur trying to emit to otherwise they won't receive the data.

Related

how to get cookie in react passed from express js api (MERN stack)

I have an api in express js that stores token in cookie on the client-side (react). The cookie is generated only when the user logins into the site. For example, when I test the login api with the postman, the cookie is generated as expected like this:
But when I log in with react.js then no cookie is found in the browser. Looks like the cookie was not passed to the front end as the screenshot demonstrates below:
As we got an alert message this means express api is working perfectly without any error!!
Here is my index.js file on express js that includes cookie-parser middleware as well
require("dotenv").config();
const port = process.env.PORT || 5050;
const express = require("express");
const app = express();
const cors = require("cors");
const authRouter = require("./routes/auth");
var cookieParser = require('cookie-parser')
connect_db();
app.use(express.json());
app.use(cookieParser())
app.use(cors());
app.use("/" , authRouter);
app.listen(port , () => {
console.log("Server is running!!");
})
Code for setting up the cookie from express api only controller
const User = require("../models/user");
const jwt = require("jsonwebtoken");
const bcrypt = require('bcrypt')
const login = async (req, res) => {
const { email, password } = req.body;
try {
const checkDetails = await User.findOne({ email });
if (checkDetails) {
const { password: hashedPassword, token, username } = checkDetails;
bcrypt.compare(password, hashedPassword, function (err, matched) {
if (matched) {
res.cookie("token", token, { expires: new Date(Date.now() + (5 * 60000)) , httpOnly: true }).json({ "message": "You logged in sucessfully!" });
} else {
res.status(500).json({ "message": "Wrong password" });
}
});
} else {
res.status(500).json({ "message": "Wrong email" });
}
} catch (error) {
console.log(error.message);
}
}
Here is the react.js code that I am using to fetch data from api without using a proxy in package.json file
if (errors.length === 0) {
const isLogin = await fetch("http://localhost:5000/api/login", {
method: "POST",
body: JSON.stringify({ email, password }),
headers: {
"Content-Type": "application/json"
}
});
const res = await isLogin.json();
if(res) alert(res.message);
}
I want to get to know what is the reason behind this "getting cookie in postman but not in the browser". Do I need to use any react package?
The network tab screenshot might help you.
If I see in the network tab I get the same cookie, set among the other headers
To my understanding, fetch doesn't send requests with the cookies your browser has stored for that domain, and similarly, it doesn't store any cookies it receives in the response. This seems to be the expected behaviour of fetch.
To override this, try setting the credentials option when making the request, like so:
fetch(url, {
// ...
credentials: 'include'
})
or, alternatively:
fetch(url, {
// ...
credentials: 'same-origin'
})
You can read more about the differences between the two here.
I got my error resolved with two changings in my code
In front end just added credentials: 'include'
fetch(url, {
method : "POST"
body : body,
headers : headers,
credentials: 'include'
})
And in back end just replaced app.use(cors()); to
app.use(cors({ origin: 'http://localhost:3000', credentials: true, exposedHeaders: ['Set-Cookie', 'Date', 'ETag'] }))
That's it got resolved, Now I have cookies stored in my browser!!! Great. Thanks to this article:
https://www.anycodings.com/2022/01/react-app-express-server-set-cookie-not.html
during development i also faced same things, let me help you that how i solve it,
Firstly you use proxy in your react package.json, below private one:-
"private": true,
"proxy":"http://127.0.0.1:5000",
mention the same port on which your node server is running
Like:-
app.listen(5000,'127.0.0.1',()=>{
console.log('Server is Running');
});
above both must be on same , now react will run on port 3000 as usual but now we will create proxy to react So, react and node ports get connected on same with the help of proxy indirectly.
Now, when you will make GET or POST request from react then don't provide full URL, only provide the path on which you wants to get hit in backend and get response,
Example:-
React side on sending request, follow like this:-
const submitHandler=()=>{
axios.post('/api/loginuser',
{mobile:inputField.mobile,password:inputField.password})
.then((res)=>{
console.log(res);
})
.catch((err)=>{
console.log(err);
})
}
Node side where it will hit:-
app.post('/api/loginuser', async(req,res)=>{
//Your Code Stuff Here
res.send()
}
on both side same link should hit, it is very important
it will 100%.
don't forget to mention
on node main main where server is listening

How to use data returned from an HTTP request using http-proxy-middleware and Express

I am making API calls to Squarespace for an inventory management system. I've set up a Node.js and Express server, and have been using http-proxy-middleware to set up a proxy and make GET requests.
I am able to generate the GET requests successfully on my localhost - an HTML pre-tag is filled with all of the JSON data of the request. However, I am completely clueless on how to handle and use the data further that was returned to me. When I make a call and receive Pending orders, I want to pull JSON data from the returned request body of the orders, such as SKU numbers for products purchased.
const { response } = require('express');
const express = require('express');
require("dotenv").config();
const { createProxyMiddleware, responseInterceptor } = require('http-proxy-middleware');
const router = express.Router();
const PORT = 3000;
const HOST = "localhost";
const GET_ORDERS_URL = process.env.SS_GET_ORDERS_URL;
const API_KEY = process.env.SS_AUTH;
const app = express();
const proxy = app.use("/testing", createProxyMiddleware({
target: GET_ORDERS_URL,
headers: {
'Authorization': API_KEY,
'User-Agent': 'halp me'
},
changeOrigin: true,
pathRewrite: {
[`^/testing`]: '',
},
selfHandleResponse: true, //
onProxyRes: responseInterceptor(async (responseBuffer, proxyRes, req, res) => {
var orderResponse = responseBuffer.toString('utf-8');
return orderResponse;
}),
}));
// Start Proxy
app.listen(PORT, HOST, () => {
console.log(`Starting Proxy at ${HOST}:${PORT}`);
});
The API request returns JSON data, which I would love to use and process for the next part of my inventory management. I'm trying to figure out why I can't get return orderResponse; to output anything at all.
I have tried every variation of returning a variable I can imagine, console.logged a million things - any guidance to what I'm missing here would be greatly appreciated!

Alternative to sending a lot of requests to server side

I got cart products at my eCommerce website that I built with Angular,NodeJS,MongoDB.
When my client wants to add quantity or decrease quantity of product for example from 4 to 2, it will send 2 patch requests to update the quantity (first one from 4 to 3 and second from 3 to 2), I want it to make a way better algorithm that will do in 1 request (for example at the end of the session, when the user is leaving the website, etc..)
I tried using Navigator.sendBeacon and it sometimes works and sometimes not (which I can't use, I need something that works all the time..)
I don't want to show the user any message before he leaves which I know that make the fix for that issue and it will work with navigator.sendBeacon
Here is what I made with navigator.sendBeacon :
Client Side:
#HostListener('window:beforeunload', ['$event'])
onBeforeUnload(): void {
const data = new FormData();
data.append('cartProducts', JSON.stringify(this.cartProducts));
navigator.sendBeacon("http://localhost:3000/api/shopping-online/get-beacon", data)
}
public cartProducts: CartItem[] = [];
Cart products gets the array of objects from the database.
Server Side:
router.post("/get-beacon", async (request, response) => {
try {
console.log("hi");
}
catch (err) {
console.log("bye");
}
})
Sometimes I get the "hi" message on NodeJS terminal and sometimes it comes in delay of few seconds and sometimes it doesn't come at all.
I will glad for any idea with navigator.sendBeacon or any other idea you got to stop that bad algorithm that every click on change quantity it sends to server side (can be even 10 clicks in a row which is very bad).
Have you tried listening for visibilitychange instead of using onBeforeUnload?
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
const data = new FormData();
data.append('cartProducts', JSON.stringify(this.cartProducts));
navigator.sendBeacon("http://localhost:3000/api/shopping-online/get-beacon", data);
}
});
MDN docs
With a great advice of Alan got to exactly what I wanted, here is the solution.
Btw there is no mention how to get exactly the sendBeacon on NodeJS so I will add all the way here.
Client Side (Angular):
#HostListener('document:visibilitychange', ['$event'])
visibilitychange() {
if (this.cartProducts && this.isCartProductsChanged) {
const blob = new Blob([JSON.stringify(this.cartProducts)], { type: 'application/json' });
navigator.sendBeacon("http://localhost:3000/api/shopping-online/get-beacon", blob)
this.isCartProductsChanged = false;
}
}
public cartProducts: CartItem[] = [];
public isCartProductsChanged: boolean = false;
It will send the beacon only if there are products in cart, only if any changes made to the cart (added quantity or product)
Server side (NodeJS):
On controller:
router.post("/get-beacon", async (request, response) => {
try {
console.log(request.body);
}
catch (err) {
console.log("bye");
}
})
On app.js
const shoppingController = require("./controllers/shopping-controller");
const express = require("express");
const server = express();
const cors = require("cors");
server.use(function (req, res, next) {
res.header("Access-Control-Allow-Credentials", "true");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-
Type, Accept");
next();
});
server.use(express.text());
let port = process.env.PORT || 3000;
server.use("/api/shopping-online", shoppingController);
server.listen(port, () => console.log(`Listening on
http://localhost:${port}`));
The usage instead of body parser since express version 4.16.0 to make it work is
server.use(express.text());
Before express version 4.16.0 you have to install body-parser
npm i body-parser
And add at app.js:
const bodyParser = require("body-parser");
server.use(bodyParser.text());

Apple-pay with Stripe not recognized in Dashboard

I seem to have gotten backend index.js file working with an apple-pay payment request on the back-end. However the results do not show up in my Stripe dashboard. Spinning my wheels and cannot figure out why. Any help would be much appreciated.
Below is the code I am using on the back-end. When I press the pay button in Apple-Pay in the App, my terminal window shows "Payment requested" and "Success" messages.
I would then expect the USD amount to process in my stripe dashboard but I am gettin $0.00 and no activity. Any help would be wonderful!
// Add packages we need
const express = require('express')
const bodyParser = require('body-parser')
var stripe = require('stripe')('YOUR-SECRET-KEY')
// Create an express app
const app = express()
// Use body parser so we can parse the body of requests
app.use(bodyParser.json())
// Just a sanity check endpoint we can hit in the browser
app.get('/', function (req, res) {
res.send('This is the backend server for Metatoll Application!')
})
app.post('/pay', function (req, res) {
console.log('Payment requested')
var token = req.body.stripeToken
var amount = req.body.amount
var description = req.body.description
stripe.charges.create({
amount: amount,
currency: "usd",
description: description,
source: token,
}, function(err, charge) {
if (err !== null) {
console.log('error capturing')
console.log(err)
res.status(400).send('error')
} else {
console.log('success')
res.status(200).send('success')
}
});
})
app.listen(3000, () => {
console.log('Metatoll listening on port 3000')
})

How to take clientside Javascript arrays and POST through a Node.js API into a MongoDB database?

I have a single webpage that initially has two form inputs, one for a list of names and another for the title of a game. I've written some javascript/jquery that takes the X names and creates X more form inputs meant for each person's specific score. The javascript then creates the following variables upon the clicking of the names/scores form's submit button:
gameTitle = Monopoly
users = [Bob, Bill, Jim, Janet]
scores = [100, 110, 90, 80]
positions = [2, 1, 3, 4]
I then have a MongoDB schema set up as such:
const SessionSchema = new mongoose.Schema({
gameTitle: String,
users: [],
scores: [],
positions: []
});
And a Node.js handler as such:
const express = require('express');
const router = express.Router();
const bodyParser = require('body-parser');
const timestamps = require('mongoose-timestamp');
router.use(bodyParser.urlencoded({ extended: true }));
router.use(bodyParser.json());
const Session = require('./Session');
//Post a session to the database
router.post('/', function(req, res) {
Session.create({
gameTitle : req.body.gameTitle,
users : req.body.user,
scores : req.body.score,
positions : req.body.position
},
function (err, session) {
if (err) return res.status(500).send("There was a problem adding the information to the database");
res.status(200).send(session);
});
});
Using Postman I can see that posting works when I use this format:
Postman POST
Postman GET
How do I take the created javascript variables and, also upon the clicking of the names/scores form's submit button, POST them through the API and into the MongoDB database?
Apologies if I have missed any important information/code - I haven't fully wrapped my head around how the backend stuff works.
You need to register your Schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const SessionSchema = new mongoose.Schema({
gameTitle: String,
users: [],
scores: [],
positions: []
});
module.exports = mongoose.model('session', SessionSchema);
And here you need to use the mongo schema model, like this:
const express = require('express');
const router = express.Router();
const bodyParser = require('body-parser');
const timestamps = require('mongoose-timestamp');
router.use(bodyParser.urlencoded({ extended: true }));
router.use(bodyParser.json());
const SessionSchema = require('./Session'); // register the mongo model
const mongoose = require('mongoose');
const Session = mongoose.model('session');
//Post a session to the database
router.post('/', function(req, res) {
const new_session = {
gameTitle : req.body.gameTitle,
users : req.body.user,
scores : req.body.score,
positions : req.body.position
};
new_session.save((err, saved_session) => {
if(err) {
res.json(err);
} else {
res.json(saved_session);
}
});
});
Sounds like you have the backend working. What you're missing is the API request. Since your website is not under the same host:port than your API server, when doing it from the browser you'll face CORS issues. Let's get to that later:
First, you'll be making an API call. You can use axios or fetch. Let's go with fetch here:
fetch(url, {
body: JSON.stringify(yourJavascriptVariablesAsAnObject),
headers: {
'Content-Type': 'application/json'
},
method: 'POST',
})
.then(response => {
// here you can check for status if you want.
// ...
return response.json(); // assuming server returns JSON.
})
.then(responseBody => {
// Do something with the server's response body
});
Now for the CORS problem, if your client app is from create-react-app or at least you're using webpack-dev-server, you can proxy request really easy.
If you're not, then you need to allow CORS on your nodeJS server. The simplest way is to use a library.
PS: CORS basically means you can't do requests from a browser to a service living in a different `url:port:, unless that service explicitly says it's ok.
A third option would be putting both UI and server project behind a Web server like Nginx and proxy the requests, but that sounds too complex for what you need.

Categories