Backend code example: I am trying to get users here from my SQL Server database Account:
async function executeQuery() {
try {
const pool = await sql.connect(config);
const result = await pool
.request()
.query(`USE Account SELECT TOP 10 UserNo FROM UserTable`);
return result;
} catch (err) {
console.log(err);
}
}
app.get("/api/data", async (req, res) => {
const result = await executeQuery();
res.json(result);
});
React frontend code: I am getting an error when try to render data from SQL Server.
import React, { useState, useEffect } from "react";
function SqlTest() {
const [data, setData] = useState([]);
async function fetchData() {
const result = await fetch("/api/data").then((res) => res.json());
setData(result);
}
useEffect(() => {
fetchData();
}, []);
return (
<div>
{data.map((item) => (
<div key={item.UserNo}>{item.UserNo}</div>
))}
</div>
);
}
export default SqlTest;
I am trying to render data from SQL Server, but nothing helps..
Ok, your problem is a very common one that has a very common solution.
You have 2 separate projects (HTTP servers):
The HTTP server that CRA comes with, which is a NodeJS server.
Your API server, which happens to also be a NodeJS server.
Now you want to fetch data from React by querying your API server.
Look at your URL: api/data. This is a relative URL. Relative URL's are resolved by the browser by using the current page's domain. I don't know your exact setup, but the URL will end up being something like http://localhost:3000/api/data.
Do you see the problem already? Your API server is not listening on port 3000. It is probably listening on some other port number. After all, no 2 applications can listen on the same TCP port.
So you would then be tempted to change your fetch URL to a full URL that specifies the server API: http://localhost:3001/api/data. That probably works, but there's a chance it errors out due to CORS.
So long story short, do as the CRA help pages say you should do: Set a proxy up in your CRA server. Here it is: https://create-react-app.dev/docs/proxying-api-requests-in-development/
In a nutshell, create the file src/setupProxy.js with code similar to this:
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:5000', // <-- USE YOUR API PORT
changeOrigin: true,
})
);
};
The sample assumes you're running your API server in port 5000. Change it to fit your configuration.
Related
I am trying to create a simple react app with node/express for the backend. When I start my app I get this error:
Proxy error: Could not proxy request /users from localhost:3000 to http://localhost:5000/.
See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNREFUSED).
My front-end package.json looks like this
The front-end is pretty simple. fetchUserData() under useEffect() is what calls the backend.
import React, { useEffect, useState } from "react";
function App() {
let [userData, setUserData] = useState([{}]);
useEffect(() => {
const fetchUserData = async () => {
const response = await fetch(`/users`);
const data = await response.json();
setUserData(data);
};
fetchUserData();
}, []);
return (
<div>
<h1>Hello React World!</h1>
</div>
);
}
export default App;
The backend is pretty barebone as I just started this project. I have no problem getting the correct response if I just request http://localhost:5000/users directly from the browser or postman:
const express = require("express");
const app = express();
const port = process.env.PORT || 5000;
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get("/users", (req, res) => {
console.log(req);
res.json({ users: ["Bob", "Sally"] });
});
app.listen(port, () => {
console.log(`Listening on port ${port}`);
});
The error message appears when I try to load the front end on http://localhost:3000 which should then fetch the backend resource. I look at the network tab of chrome and for some reason it is going to port 3000 instead of 5000:
I've tried the below but no luck:
Closing my app. Deleting package-lock.json and node_modules, reinstalling them.
Add '/' to the end of the string for the "proxy" setting in package.json
Replacing localhost with 127.0.0.1 in package.json
Tried adding "--ignore client" to dev script in server package.json
Tried adding "secure": false in client package.json
Edit 1: This issue is driving me crazy. If I remove the proxy from package.json, add cors to the server side, and use the absolute path of the endpoint instead of the relative path in my client-side fetch request, it works. But I would much rather use the relative path.
import React, { useEffect, useState } from "react";
function App() {
let [userData, setUserData] = useState([{}]);
useEffect(() => {
const fetchUserData = async () => {
const response = await fetch(`http://localhost:5000/users`);
const data = await response.json();
setUserData(data);
};
fetchUserData();
}, []);
return (
<div>
<h1>Hello React World!</h1>
</div>
);
}
export default App;
Just don't use 'localhost'. Put everything as 127.0.0.1
Font: hours trying every solution possible.
From trying to replicate your issue I think in your proxy you have "proxy": "http://localhost:5000/", but then you also pass a fetch request for "/users" which would lead to a "http://localhost:5000//users.
I would imagine that when you were trying to figure this out that the issue was that you didn't restart your React App after changing the package.json to include the Proxy, and then by the time you restarted the React App, you had already added the extra "/".
Also in your browser console.log when, no matter where your proxy is it will come up with http://localhost:3000 as the address rather than your actual endpoint - it can be a bit of a red herring
Hope it helps
I finally got it to work!
After a lot of experimenting, I realized that this was indeed an environment problem. That explains a lot since the many other suggestions I tried worked for other people but not for me.
What had happened was that my client-side was using wsl for the terminal yet my backend was using PowerShell. When I switched both to wsl it worked. Or when I switched both to Powershell it also worked.
Everything runs smoothly locally, and I can make HTTP requests to other routes just fine. However the one route that makes requests to the Twitch API is giving me no response. I believe this has something to do with the port, or the redirect URI.
If I run my local version it will work on the Heroku deployment, so I think that means there's an issue with my redirect URI or port. I've tried removing the || 5000 option in express, changing my redirect URL on twitch, but I can't quite get anything to work. The app is pretty small, it just searches channels through the Twitch API.
Twitch Api Redirect URL:
https://twitchfinder.herokuapp.com/
Heroku Config Vars:
CLIENT_ID.
CLIENT_SECRET
REDIRECT_URI. appname.herokuapp.com
REDISCLOUD_URL
Express Port:
const port = process.env.PORT || 5000;
app.listen(port, () => {
console.log(`App is running on port ${port}`);
});
Express Route (again, works fine locally, or if I just have this route send something other than the twitch API)
app.get("/search/:channels", async (req, res) => {
const { channels } = req.params;
const results = await twitchSearch(channels, CLIENT_ID);
res.json(results);
});
GET request function:
async function twitchSearch(query, CLIENT_ID) {
const access_token = await client.get("access_token");
console.log(access_token);
const response = await axios
.get(`https://api.twitch.tv/helix/search/channels?query=${query}`, {
headers: {
Authorization: `Bearer ${access_token}`,
"Client-Id": CLIENT_ID,
},
})
.then((res) => {
data = res.data.data;
return data;
})
.catch((e) => {
console.log("error from inside twitchSearch", e);
});
return response;
}
I'm also running middleware, with Cors, serving a static 'build', and running a validation middleware that's set to a cron job.
I can post my get token or validate functions as well, but I think I've narrowed down the issue to the redirect url/uri or a port issue... I'm just at a loss to the solution after a few days.
I'm finally dipping my toe into the world of server side react using Next.js, however I'm pretty stumped with this issue.
I'm making a call to an API from pages/customer-preferences.tsx using isomorphic-unfetch
CustomerPreferencesPage.getInitialProps = async () => {
const res = await fetch(API_URL + '/preference-customer');
const initialData = await res.json();
return { initialData };
};
All works fine locally in dev mode or once built and ran build > start. To host it I'm running it from a docker container node:10, and when I run this locally all is fine also. The issue only happens once it's deployed.
When I navigate to / and then click a link to /customer-preferences all works as expected. But if I refresh the page or load the page directly at /customer-preferences I see this error from Next.js
So the issue only seems to happen when trying to make the API calls from the server and not the client.
I've also setup a simple express server to use instead, but not sure if this is necessary?!
const express = require('express');
const next = require('next');
const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = express();
server.all('*', (req, res) => {
return handle(req, res);
});
server.listen(port, err => {
if (err) throw err;
console.log(`> Ready on http://localhost:${port}`);
});
});
When checking the server logs I get this:
FetchError: request to http://xxx failed, reason: getaddrinfo EAI_AGAIN xxx xxx:80
Any help would be greatly appreciated.
No, the server setup is not necessary.
This is happening because the browser/client is not capable of resolving your docker container's hostname. As it stands, the only solution I know of is to check for the req object in getInitialProps (so as to determine which environment the fetch will run in) and call the Docker hostname when on server, localhost when on client. E.g.
async getInitialProps (ctx) {
if (ctx.req) // ctx.req exists server-side only
{
// call docker-host:port
}
else {
// call localhost:port
}
}
My suspicion has to do with the fact that fetch is not a native node module but a client in browsers. So, if you navigate from one page to this page; per the documentation; getInitialProps will be called from the client side, making the fetch method accessible. A refresh ensures that the getInitialProps called from the server side.
You can test this theory by running typeof fetch from a browser's inspector and from a node REPL.
You are better of calling the method from component or using a third-party HTTP client like axios...
If you want to skip calling the AJAX method from the backend and only call it from the frontend, you can test if the method is calling from the frontend or the backend, like so:
CustomerPreferencesPage.getInitialProps = async () => {
if (typeof window === 'undefined') {
// this is being called from the backend, no need to show anything
return { initialData: null };
}
const res = await fetch(API_URL + '/preference-customer');
const initialData = await res.json();
return { initialData };
};
I am new to Node, and I can't pass request parameters when using http-proxy-middleware..
It throws me 404 error
This is the express listener:
app.put("/api/markets/:id",()=>{..code..});
This is the axios PUT request:
axios
.put("/api/markets/" + idToPass, {..Object..})
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
And this is the proxyMiddleware:
const proxy = require("http-proxy-middleware");
module.exports = function(app) {
app.use(proxy("/api/*", { target: "http://localhost:3050" }));
};
My client runs on localhost:3000 (react app)
And my server is on localhost:3050
When I open my browser network tab to inspect requests I see that call is being made to
localhost:3000/api/markets/idContent
not
localhost:3050/api/markets/idContent
as it is supposed to.
It works when I send it manually to
localhost:3050/api/markets/idContent
How can I fix this, any ideas?
Thx in advance
It seems the problem is with the proxy configuration. As of now it matches urls only one level deep.
Try /api or /api/** instead
const proxy = require("http-proxy-middleware");
module.exports = function(app) {
app.use(proxy("/api", { target: "http://localhost:3050" }));
};
You are not specifying the base URL. Create an axios instance and specify the correct base URL:
const instance = axios.create({
baseURL: 'http://localhost:3050'
});
And then make every axios requests using that instance:
instance
.put("/api/markets/" + idToPass, {..Object..})
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
I am trying something simple where I make a request from the front-end and fetch the JSON data on the back-end. I'm using ReactJS on the front-end and NodeJS and ExpressJS on the Back-end.
I am getting little confused with figure what the right way how I can access JSON data onto the front-end by make the name server file name from server.js or con.now.
I am getting in my console not it refused to connect the link to the server. I have been tried to name the file server.js to cors.now would that connect the server to the front-end but I was wrong.
Failed to load resource: net::ERR_CONNECTION_REFUSED
But when every do that getting an error message in my console that talks about cors.now.sh/https://us-central1-aaronklaser-1.cloudfunctions.net/medium?username=#aaron.klaser
Medium.js
import React from 'react';
import PageHeader from './PageHeader';
import PageContent from './PageContent';
import axios from 'axios';
export default class Medium extends React.Component {
state = {
posts: []
}
componentDidMount() {
this.fetchPosts().then(this.setPosts)
}
fetchPosts = () => axios.get(`https://cors.now.sh/https://us-central1-aaronklaser-1.cloudfunctions.net/medium?username=#aaron.klaser`)
setPosts = response => {
this.setState({
posts: response
})
}
render() {
return (
<div>
<PageHeader/>
<PageContent>
<pre>{JSON.stringify(this.state.posts, null, 2)}</pre>
</PageContent>
</div>
)
}
}
cors.now
const express = require('express');
const app = express();
const port = process.env.PORT || 5000;
const request = require('request');
app.get("/api/medium", (req, res) => {
if(!req.query.username){
return res.status(400).send('Error: You need to include query param ?username=#yourUsername');
} else {
return request(url,(error, response, body) => {
const url = `https://medium.com/${req.query.username}/latest?format=json`;
const prefix = `])}while(1);</x>`
const strip = payload => payload.replace(prefix, ``)
res.send(JSON.parse(strip(body)));
});
}
});
app.listen(port, () => console.log(`Listening on port ${port}`));
I see you're using the https://cors.now.sh site as a reverse proxy. I am not sure if that site is functioning at this time?
If you hit your API endpoint directly (https://us-central1-aaronklaser-1.cloudfunctions.net/medium?username=#aaron.klaser) - I see a valid JSON response returned to the browser.
I believe the issue is with the reverse proxy site not running or responding at this time.