-1

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

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Eze
  • 3
  • 2
  • You're getting a 401 HTTP status code that tells you you're not authenticating to the API. This could be an invalid or missing token passed in the Authorization header. I see you posted that you see the 401 error in your browser but what do the server logs show? – Cody Jan 08 '23 at 19:47
  • you got your frontend and backend separate with different domaine right ? if yes i will give a try to fix it – ShueiYang Jan 08 '23 at 20:03
  • @ShueiYang yes i have my frontend and backend separate with different domain. the backend runs on locahost:4000 while the frontend runs on localhost:3000 – Eze Jan 08 '23 at 21:24
  • @ShueiYang and to everyone. https://github.com/cullkid/jobplus-node-backend, https://github.com/cullkid/jobplus-react-frontend sorry guys i have to paste the github project here for you guys to go through it and check it well for me. Like i said i have been stocked on this for 2weeks now and i have lost many opportunities because of it because i need to show that i can be able to fetch data from api i created myself. LOOKING FORWARD FROM YOU GUYS – Eze Jan 08 '23 at 21:30

1 Answers1

0

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.

ShueiYang
  • 648
  • 1
  • 3
  • 7
  • i still get POST http://localhost:3000/localhost:4000/api/jobs 404 (Not Found) after editing, i have added the github source of my both frontend and backend code in the comment section because i have been deburging the codes for days now and i am loosing many opportunities guys. i believe i something is not right somewhere but i haven't find out, that's why i need your helps for deburging please. THANKS – Eze Jan 08 '23 at 21:52
  • I didn't see your get route, I update it try again with get method just in case. – ShueiYang Jan 08 '23 at 22:26
  • could you show me the update one please? This is what my get method looks like : '''const response = axios .get("http://localhost:4000/api/jobs/") .then((res) => res.json()); console.log(response);''' – Eze Jan 09 '23 at 06:53
  • What did you get when you try the same code I suggest with GET method ? i remove POST method since fetch will do GET by default, if it's not working what error did you get ? – ShueiYang Jan 09 '23 at 09:30
  • i got empty console in my browser – Eze Jan 10 '23 at 06:56
  • ok but whats the https response ? 200 ? 401 ? 404? btw you' re not using passport jwt-strategy right ? then there is no point to put the token on the header – ShueiYang Jan 10 '23 at 12:35
  • after having a look again at your source code, I think the issue was the way you get the data, so I update my answer on the fetch part so have a look. – ShueiYang Jan 10 '23 at 15:34
  • thank you so much @shueiyang for trying your best to help out, i don't know if it's allow to seek for a help outside of the forum. I would appreciate to share my screen with you or someone else concerning it? i have deburged my backend to the best of my knowledge and i think it can be fastly be solve if i can such favour from any of us here. it's a first experience on backend as i stated, though i haven't releented on it but need a professional ideal to take me through more on deburging please. thank you – Eze Jan 10 '23 at 23:05
  • Oh I see, it's just i am not familiar with the query language you use on the database, so i am not sure to be able to help that part, but here can you tell what error you get when you try ? before debug the backend it's interresting to know if it work as expected in the front or not with the cookie and jwt. – ShueiYang Jan 10 '23 at 23:08
  • the cookie and jwt update you recommendded stop my postman from fetching or semdimg a request and it also print post URL unauthourised. And i just dicovered that the datas i created through postman is not longer available in my pgadmin but i can view the datas oin postman. In pgadmin i have the database values but when i create a data from postman it doesn't reflect to my pgadmin anymore. could this cause the error in frontend? – Eze Jan 11 '23 at 19:28
  • What i suggest is only for the JWT storage issue, this should have nothing to do with the fetching issue on your data, for now since your project is build on cookie session don't change anything, and let's focus about the fetch issue, I just want to know what you got when you only try to fetch and set this `setName(newData.data.name)` I will go to look on your github again – ShueiYang Jan 11 '23 at 19:59
  • Well after relook again I just notice when you send back the data to the front you re using `res.send()` and not `res.json()`.. a response object is more than just its JSON part, you need to use `res.json()` to separate only the JSON part from all the rest (HTTP protocol elements, headers, state of the response etc) – ShueiYang Jan 11 '23 at 20:32
  • Please confirm to me in which of the file do i need to update the res.send() to res.json(), I am thinking it should be on controller files, am i correct? – Eze Jan 12 '23 at 23:06
  • Yea for example in your controller folder job.controller.js – ShueiYang Jan 13 '23 at 00:51