Using axios / fetch to fetch data from node toreact - reactjs

Please I need a help on how to fetch a data from node to react, I have been stuck here for 2 weeks now.
Here are my backend code:
server.js:
require("dotenv").config();
const app = require("./src/app");
const port = process.env.PORT || 4000;
app.get("/", (req, res) => {
res.send("Hello World!");
});
app.listen(port, () => {
console.log(`Server is running on port http://localhost:${port}`);
});
app.js:
const express = require("express");
const cors = require("cors");
const cookieSession = require("cookie-session");
const app = express();
app.use(
cors({
origin: ["http://localhost:4000/api", "http://localhost:3000"],
})
);
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
app.use(express.json());
app.use(express({ type: "application/vnd.api+json" }));
app.use(express.urlencoded({ extended: true }));
app.use(
cookieSession({
name: process.env.COOKIE_NAME, //ookie name in .env
secret: process.env.COOKIE_SECRET, //secret name in .env
httpOnly: true,
sameSite: "strict",
maxAge: 24 * 60 * 60 * 1000, // 24 hours duration before expire
})
);
app.use("/uploads", express.static("uploads"));
const jobRoute = require("./routes/job.routes");
app.use("/api/", jobRoute);
module.exports = app;
service.js:
const db = require("../config/database");
const notificationServices = require("./notification.services");
const { jobReuseQuery } = require("../job reuseable query/job.queries");
const createJob = async (body) => {
const {
title,
salary_type,
salary,
job_types,
description,
company_id,
sector_id,
category_id,
} = body;
const { rows } = await db.query(
`INSERT INTO jobs (title, salary_type, salary, job_types, description, company_id, sector_id, category_id)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *`,
[
title,
salary_type,
salary,
job_types,
description,
company_id,
sector_id,
category_id,
]
);
notificationServices.sendMatchJobsToUserProfiles(rows[0]);
return rows[0];
};
const getAllJobs = async () => {
const { rows } = await db.query("SELECT * FROM jobs");
return rows;
};
controller.js:
const jobService = require("../services/job.services");
const createJob = async (req, res) => {
try {
const job = await jobService.createJob(req.body);
res.status(201).send({
message: "Job created successfully",
data: job,
});
} catch (err) {
res.status(400).send(err.message);
}
};
const getAllJobs = async (req, res) => {
try {
const jobs = await jobService.getAllJobs();
res.status(200).send({ data: jobs });
} catch (err) {
res.status(400).send({ message: err.message });
}
};
routes.js:
const router = require("express-promise-router")();
const jobController = require("../controllers/job.controller");
const auth = require("../middleware/auth.middleware");
router.post("/jobs", auth, jobController.createJob);
auth.js:
const db = require("../config/database");
const jwt = require("jsonwebtoken");
const dotenv = require("dotenv");
dotenv.config();
const auth = async (req, res, next) => {
const token = req.session.token;
if (!token) {
return res.status(401).send({ error: "Please Authenticate" });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const { rows } = await db.query("SELECT * FROM users WHERE id = $1", [
decoded.id,
]);
if (!rows[0]) {
throw new Error("User not found");
}
req.user = rows[0];
next();
} catch (error) {
return res.status(401).send({ error: error.message });
}
};
module.exports = auth;
React frontend code:
import React, { useEffect } from "react";
import tech from "../../image/tech-big.svg";
import health from "../../image/health-big.svg";
import eng from "../../image/eng-big.svg";
import axios from "axios";
import { useState } from "react";
const Joblist = () => {
const [name, setName] = useState([]);
//first method
const response = axios
.get("http://localhost:4000/api/jobs/")
.then((res) => res.json());
console.log(response);
//second method
const fetchData = async () => {
const newData = await fetch("http:localhost:4000/api/jobs", {
method: "GET",
headers: {
"Content-Type": "application/json",
ACCEPT: "application/json",
"Access-Control-Allow-Credentials": true,
"Access-Control-Allow-Origin": true,
credentials: "same-origin",
Authorization: `Bearer ${token}`,
},
}).then((res) => res.json());
console.log(newData);
setName(newData.jobs.name);
fetchData();
};
you can see in my react, I have 2 method i used trying to fetch the data fron node to the react
first method return error in my browser console :
Promise {<pending>}
GET http://localhost:4000/api/jobs/ 401 (Unauthorized)
Uncaught (in promise) AxiosError {message: 'Request failed with status code 401', name: 'AxiosError', code: 'ERR_BAD_REQUEST', config: {…}, request: XMLHttpRequest, …}
while the second method return nothing in my browser console
I am trying to fetch a data from my node backend into frontend react but my first method log error while the second method log nothing

I think you need to clean up a bit your setting, since you're using CORS than you can first make some changes :
// .....
const app = express();
// with CORS you can do all your setting at the same place, so you don't need to set the header
const corsOptions = {
origin: ["http://localhost:4000/api", "http://localhost:3000"],
methods: "GET, POST, PUT, DELETE, OPTIONS, HEAD",
credentials: true, // for jwt/cookie !
};
app.use(cors(corsOptions));
app.use(express.json());
app.use(express({ type: "application/vnd.api+json" }));
app.use(express.urlencoded({ extended: true }));
app.use(
cookieSession({
name: process.env.COOKIE_NAME,
secret: process.env.COOKIE_SECRET,
maxAge: 24 * 60 * 60 * 1000,
httpOnly: true,
sameSite: false, //set this to "None" if you deploy to production on cross domaine.
secure: false, //set to true is required on production with https
});
app.use("/uploads", express.static("uploads"));
const jobRoute = require("./routes/job.routes");
app.use("/api/", jobRoute);
module.exports = app;
Update the fetch part I clean up (I remove the header) and i just notice on your job.controller.js you put data property on your response json.. so you need to check again your database structure if it's still not working.
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch("http:localhost:4000/api/jobs", {
credentials: "include", //to be able to send with cookies...
});
if(response.ok) {
const newData = await response.json();
console.log(newData);
setName(newData.data.jobs.name); // this part you need to check your data structure again...
}
} catch (error) {
console.log(error)
}
}
fetchData();
}, []);
Optional note: this part is not part of your question, just in case if there is still issue with the cookie-session and jwtoken, you can change how the JWT is stored in the cookie: cookie-session purpose is to create a "session id" to authenticate the user by storing it at the client side (on the browser, with the cookie), i don't really see the point to use this if you're gonna use jwt token to authenticate anyway ? I let you see the step below if you re still stuck at this part:
First, you may need to install cookie-parser middleware, because if this method work for you, you will be able to uninstall cookie-session.
const cookieParser = require('cookie-parser')
/...
app.use(cookieParser());
on the auth.controllers.js
const loginAuth = async (req, res) => {
try {
const token = await authServices.login(req.body);
// set the jwt token on the cookie
res.cookie("jwt", token, {
maxAge: 24 * 60 * 60 * 1000,
httpOnly: true,
sameSite: false, //set this to "None" if you deploy to production on cross domaine.
secure: false, //set to true is required on production with https
})
return res.status(200).json({
//controller will return this message if the body sent was match
message: "User logged in successfully!",
});
} catch (error) {
//ratther it will return this erroe message
return res.status(500).json({ message: error.message });
}
};
//create a logout session for the user to logout by signing session to null
const logoutAuth = async (req, res) => {
res.clearCookie("jwt")
return res.status(200).send({ message: "User logged out successfully!" });
};
You also need to replace const token = req.session.token; in your activeAuth function, and in your auth.middleware.js at the auth middleware function by this:
const token = req.cookies["jwt"] //or
const token = req.cookies.jwt
Finally if it work you can uninstall cookie-session.

Related

How do I get Heroku to differentiate between my backend (Express/Node) and frontend (React) routes?

I'm building a full stack application that uses Express/Node/Postgres on the backend and React on the frontend. I have an API file in my frontend code that sends requests to my express server, which I'm intending to specify via the BASE_URL. When I deploy the app on Heroku, the frontend routing works, but not the backend routing.
api.js
import axios from "axios";
import cookie from "react-cookies";
const BASE_URL = window.location.origin; /* originally http://localhost:3001 but obviously this won't work in production */
class MainAPI {
//admin methods
static async getAdmin() {
const request = await axios.get(`${BASE_URL}/admin`, {
withCredentials: true,
});
console.log(request);
return request;
}
static async adminLogin(username, password) {
const request = await axios.post(`${BASE_URL}/admin/login`, {
username: username,
password: password,
});
if (request.status == "200") {
console.log(request);
cookie.save("admin_token", request.data.token);
console.log(cookie.load("admin_token"));
return request;
} else {
return request;
}
}
static async adminLogout() {
const request = await axios.get(`${BASE_URL}/admin/logout`, {
withCredentials: true,
});
if (request.status == 200) {
cookie.remove("admin_token");
}
return request;
}
static async getNewsletters() {
const request = await axios.get(`${BASE_URL}/admin/newsletters`, {
withCredentials: true,
});
return request.data.newsletters;
}
static async getNewsletter(id) {
const request = await axios.get(`${BASE_URL}/admin/newsletter/${id}`, {
withCredentials: true,
});
console.log(request);
return request.data.newsletter;
}
static async createNewsletter() {
const request = await axios.post(`${BASE_URL}/admin/newsletter`, {
withCredentials: true,
headers: {
Cookie: `${cookie.load("admin_token")}`,
},
});
console.log(request);
return request;
}
static async publishNewsletter(id) {
const request = await axios.patch(`${BASE_URL}/admin/newsletter/${id}`, {
withCredentials: true,
headers: {
Cookie: `${cookie.load("admin_token")}`,
},
});
return request.data.publishNewsLetter;
}
static async deleteNewsletter(id) {
const request = await axios.delete(`${BASE_URL}/admin/newsletter/${id}`, {
withCredentials: true,
})
console.log(request)
return request.data;
}
static async getSubscribers() {
const request = await axios.get(`${BASE_URL}/admin/subscribers`, {
withCredentials: true,
})
return request.data
}
//user methods
static async getLatestNewsletter() {
try {
const request = await axios.get(`${BASE_URL}/newsletter`);
return request.data.newsletter;
} catch(e) {
return e
}
}
static async subscribe(email) {
const request = await axios.post(`${BASE_URL}/subscribe`, {
body: {
email: email
}
})
return request.data
}
static async confirmSubscription(code) {
const request = await axios.get(`${BASE_URL}/confirm-email${code}`)
return request.data
}
}
export default MainAPI;
I understand that one of my major issues here is that I'm not distinguishing between the client/server endpoint, because I'm not specifying a port or anything in the BASE_URL. In development mode, it was easy to simply hardcode my Express server url by referring to its port (3001), but I don't know what the equivalent action would be going into production mode here.
How do I set up my routing/API in such a way that I can properly distinguish between the client-side and server-side routes in the Heroku application?
For reference, here are some of my other relevant files:
App.js
const express = require("express");
const cors = require("cors");
const path = require("path")
const cookieParser = require("cookie-parser");
const adminRoutes = require("./routes/admin_routes");
const userRoutes = require("./routes/user_routes");
const { SECRET_KEY } = process.env;
const session = require("express-session");
const app = express();
const corsOptions = {
origin: "https://[my-app-name].herokuapp.com",
methods: ["GET", "POST", "PATCH", "DELETE"],
allowedHeaders: ["Content-Type", "Authorization", "Cookie"],
credentials: true,
};
app.use(express.json());
app.set("trust proxy", 1);
app.set("view engine", "js")
app.use(cookieParser());
app.use(
express.static(path.join(__dirname, "../front-end/build"))
)
app.use(
session({
secret: SECRET_KEY,
resave: false,
saveUninitialized: true,
cookie: { maxAge: 24 * 60 * 60 * 1000 },
})
);
app.use(cors(corsOptions));
app.use("/", userRoutes);
app.use("/admin", adminRoutes);
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, '../front-end/build', 'index.html'));
});
module.exports = app;
`Server.js
const app = require("./app");
const { PORT } = require("./config")
app.listen(PORT, () => console.log('ELI5-AI listening on port 3001!'))
setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:3000',
changeOrigin: true,
})
);
};
I tried changing the BASE_URL so that I wasn't simply hardcoding localhost. However, I don't know what the equivalent is to specifying a different port of the same server when deploying to Heroku for production.

how to add flask localhost to existing react app

package.json code
I want to add flask localhost to it but I already have the localhost use in react, so how to add the two proxy parameter, or is there any way to do it?
create a file setupProxy.js in react src folder ,
install npm package "http-proxy-middleware"
delete proxy from package.json
in setupProxy.js
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
createProxyMiddleware("/your route", {
target: "http://localhost:/", //url of your server
})
);
app.use(
createProxyMiddleware("/your route", {
target: "http://localhost:/",//url of your server
})
);
};
how to change the index file when using "http-proxy-middleware"?
require("dotenv").config();
const express = require("express");
const { createProxyMiddleware } = require("http-proxy-middleware");
const querystring = require("querystring"); //let us parse and stringigy query string
const app = express();
const axios = require("axios");
const path = require("path");
// Priority serve any static files.
const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
const REDIRECT_URI = process.env.REDIRECT_URI;
const FRONTEND_URI = process.env.FRONTEND_URI;
const PORT = process.env.PORT || 8889;
/**
* Generates a random string containing numbers and letters
* #param {number} length The length of the string
* #return {string} The generated string
*/
const generateRandomString = (length) => {
let text = "";
const possible =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (let i = 0; i < length; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
};
const stateKey = "spotify_auth_state";
app.use(express.static(path.resolve(__dirname, "./client/build")));
app.get("/login", (req, res) => {
const state = generateRandomString(16);
res.cookie(stateKey, state);
const scope = ["user-read-private", "user-read-email", "user-top-read"].join(
" "
);
const queryParams = querystring.stringify({
// querystring.stringify() take object with keys and value
client_id: CLIENT_ID, // and serializes them into query string
response_type: "code",
redirect_uri: REDIRECT_URI,
state: state,
scope: scope,
});
res.redirect(`https://accounts.spotify.com/authorize?${queryParams}`);
});
app.get("/callback", (req, res) => {
const code = req.query.code || null; // req.query: object containing a property for each query string parameter a route
// if route : callback?code=abc123&state=xyz789
// => req.query.code is abc123 & req.query.state is xyz789
axios({
//send the request when invoked
method: "post",
url: "https://accounts.spotify.com/api/token",
data: querystring.stringify({
grant_type: "authorization_code",
code: code,
redirect_uri: REDIRECT_URI,
}),
headers: {
"content-type": "application/x-www-form-urlencoded",
Authorization: `Basic ${new Buffer.from(
`${CLIENT_ID}:${CLIENT_SECRET}`
).toString("base64")}`,
},
})
.then((response) => {
if (response.status === 200) {
const { access_token, refresh_token, expires_in } = response.data;
const queryParams = querystring.stringify({
access_token,
refresh_token,
expires_in,
});
//redirect to react app
res.redirect(`${FRONTEND_URI}/?${queryParams}`);
//pass along tokens in query params
} else {
res.redirect(`/?${querystring.stringify({ error: "invalid_token" })}`);
}
})
.catch((error) => {
res.send(error);
});
});
app.get("/refresh_token", (req, res) => {
const { refresh_token } = req.query;
axios({
method: "post",
url: "https://accounts.spotify.com/api/token",
data: querystring.stringify({
grant_type: "refresh_token",
refresh_token: refresh_token,
}),
headers: {
"content-type": "application/x-www-form-urlencoded",
Authorization: `Basic ${new Buffer.from(
`${CLIENT_ID}:${CLIENT_SECRET}`
).toString("base64")}`,
},
})
.then((response) => {
res.send(response.data);
})
.catch((error) => {
res.send(error);
});
});
// All remaining requests return the React app, so it can handle routing.
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "./client/build", "index.html"));
});
app.listen(PORT, () => {
console.log(`express app listening at http://localhost:${PORT}`);
});

Access session data in Next.js page using withIronSession()

After reading the following post, https://dev.to/chrsgrrtt/easy-user-authentication-with-next-js-18oe and consulting the following question Using next-iron-session's "withIronSession" with Next.JS to perform simple authentication, I am still unable to access the session using req.session.get('user'). Below is my implementation in a Next.js project:
Create a util
import {withIronSession} from 'next-iron-session';
const cookie = {
cookieName: process.env.COOKIE_NAME,
password: process.env.COOKIE_SECRET,
cookieOptions: {secure: process.env.NODE_ENV === 'production'},
};
export const guard = (handler) => {
return withIronSession(handler, cookie);
};
export default cookie;
Create an API endpoint
const zlib = require('zlib');
import cookie from '#/utils/cookie';
const fetch = require('node-fetch');
import {withIronSession} from 'next-iron-session';
export default withIronSession(
async (req, res) => {
if (req.method === 'POST') {
try {
const request = await fetch(
process.env.NEXT_PUBLIC_API_BASE_URL + '/api/login',
{
method: 'post',
body: req.body,
headers: {
'Content-Type': 'application/json',
'Origin': req.headers.host || req.headers.origin,
},
}
);
const response = await request.text();
const {success, data, message} = JSON.parse(response);
// set JWT in session
compressor(data, (x) => req.session.set('user', x));
// persist session value
await req.session.save();
// console.log(req.session.get('user'));
return res.status(201).json({success, message});
} catch (error) {
console.log(error);
}
}
return res.status(404).json('Not found');
},
cookie
);
Access session data in a page
export const getServerSideProps = guard(async (ctx) => {
const {req} = ctx;
const session = req.session.get();
console.log({session});
return {redirect: {destination: '/sign-in', permanent: false}};
});
The above terminal log gives an empty object. Is there something am doing wrong??
Try the following:
export const getServerSideProps = guard(async function ({
req,
res,
query,
}) {
//Assuming you have "user" session object
const user = req.session.get("user");
...
});
Harel

When creating a MERN application, I need to create a POST request from frontend to the backend, but the request body is empty

I am creating a MERN application, where I am trying to send a post request to my backend, however when i log the req.body from my backend, it is empty
const removeTour = (id) => {
const getDevices = async () => {
const settings = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'here', info: '1', image: '1', price: '1' })
};
try {
const fetchResponse = await fetch(`http://localhost:3080/add-tour`, settings);
const data = await fetchResponse.json();
return data;
} catch (e) {
return e;
}
};
getDevices();
const newTours = tours.filter((tour) => tour._id !== id);
setTours(newTours);
};
The function is called when the button is pressed.
On the backend, i have an app.post, which should receive the request. THe request.body received is empty
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');
require('dotenv').config();
const data = require('./data.json');
//dotenvs
const DBLink = process.env.DB_HOST;
const port = process.env.PORT;
const home = process.env.HOME;
//app
const app = express();
app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
//shema
const TourSchema = mongoose.Schema({
name: {
type: String,
required: true
},
info: {
type: String
// required: true
},
image: {
type: String
// required: true
},
price: {
type: String
// required: true
}
});
const Tour = mongoose.model('tour', TourSchema);
//app routes
app.get('/', (req, res) => {
(async () => {
const tours = await Tour.find((data) => data);
try {
res.json(tours);
} catch (error) {
console.log(error);
}
})();
});
app.post('/add-tour', (req, res) => {
console.log(req.body);
// const { name, image, info, price } = req.body;
// const tour = new Tour({ name, image, info, price });
// tour.save();
// res.status(201).redirect('http://localhost:3000/');
res.send('here');
});
//mongoose
mongoose
.connect(DBLink, {
useCreateIndex: true,
useFindAndModify: false,
useUnifiedTopology: true,
useNewUrlParser: true
})
.then(() => {
app.listen(port, () => console.log(`Server is running on ${port}`));
})
.catch((err) => console.log(err));
All i get in the terminal is -
terminal:
{}
However when i am making a post request from a html form, with input that has a value and a name, the received request has the body
You probably have some issue applying body-parser. Check following official example https://github.com/expressjs/body-parser "Express/Connect top-level generic". If you add app.use(bodyParser.json()) it should parse json correctly.

Expressjs can not set cookie in the frontend of reactjs after authorization by bcrypt

i am using reactjs to create a online shopping web, and use express js to do backend, when i authorize user login, i can not set cookie to the front end
controller/user/login from expressjs
// user.controller
bcrypt.compare(req.body.password, user[0].password, (err, result) => {
if (err) {
console.log(err);
return res.status(401).json({ message: "password problem" });
}
if (result) {
const userId = JSON.stringify(user[0]._id);
res.cookie('userId', userId, { httpOnly: true, secure: true });
return res.status(200)
.json({
message: userId
})
}
return res.status(401).json({ message: "Auth failed" });
})
app.js from express
const express = require('express');
const app = express();
const morgan = require('morgan');
const bodyPaser = require('body-parser');
const mongoose = require('mongoose');
const cookiePareser = require('cookie-parser');
const cors = require('cors');
const productRouter = require('./api/routes/products');
const orderRouter = require('./api/routes/orders');
const userRouter = require('./api/routes/user');
mongoose.connect("mongodb+srv://conor_9tails:Mongoatlas123#cluster0-xcpy1.mongodb.net/test?retryWrites=true&w=majority",
{
useUnifiedTopology: true
}
);
mongoose.Promise = global.Promise;
mongoose.connection.on('connected', () => {
console.log("connected to mongo atlas");
});
app.use(morgan('dev'));
app.use(cookiePareser());
// app.use('/uploads', express.static('uploads'));
app.use(bodyPaser.urlencoded({ extended: false }));
app.use(bodyPaser.json());
app.use(cors({ origin: "*" }));
app.use('/products', productRouter);
app.use('/orders', orderRouter);
app.use('/user', userRouter);
app.use((req, res, next) => {
const error = new Error('Not found');
error.status = 404;
next(error);
});
app.use((error, req, res, next) => {
res.status(error.status || 500);
res.json({
error: {
message: error.message
}
});
});
module.exports = app;
then i make an middleware called auth normally just print out the cookie userId
check-auth.js from middleware/checkauth from expressjs
module.exports.requireAuth = (req, res, next) => {
console.log("THIS IS cookies", req.cookies);
next();
};
from the front end, i create a login component from reactjs
handleSubmit(event) {
let { email, password } = this.state;
fetch("http://localhost:5000/user/login", {
method: "POST",
body: JSON.stringify({
email, password
}),
headers: {
"content-type": "application/json; charset=UTF-8"
}
})
.then(response => {
return response.json();
})
.then((json) => {
console.log(json);
})
.catch((error) => {
console.log(error);
})
event.preventDefault();
}
as you can see i console.log response.json() which contain message of userId and it succeeded enter image description here
but there is no cookie was stored in the Application of Chrome
Thank you for your time to help me tackle with this daunting problem

Resources