Following this quick guide (React and PostgreSQL), the following app should print the JSON fetch to the bash terminal (at ~37min of video).
However this does not happen. There is no feedback on the npm or nodemon servers.
When adding a value via the front-end, firefox instantly sends back a 404 status (observed in console:network). In chrome, the thread hangs as pending until the nodemon server is shut down (and then fails with a connection reset error)(again in console:network).
npm is running app and nodemon is running the server.
app.js
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super();
this.state = {
title: 'Simple postgres app',
treatments: []
}
}
componentDidMount(){
console.log('COMPONENT HAS MOUNTED')
}
addStuff(event){
event.preventDefault()
// console.log('in method');
let data = {
test_field: this.refs.test_field.value,
};
var request = new Request('http://localhost:3000/api/new-thing', {
method: 'POST',
headers: new Headers({ 'Content-Type': 'application/json' }),
body: JSON.stringify(data),
message: console.log('JSON output: ', JSON.stringify(data))
});
fetch(request)
.then((response) => {
response.json()
.then((data) => {
console.log(data)
})
})
}
render() {
let title = this.state.title;
return (
<div className="App">
<h1> { title } </h1>
<form ref = "testForm">
<input type="text" ref="test_field" placeholder="test_field"/>
<button onClick={this.addStuff.bind(this)}>Add This</button>
</form>
</div>
);
}
}
export default App;
server.js
let express = require('express');
let bodyParser = require('body-parser');
let morgan = require('morgan');
let pg = require('pg');
const PORT = 3000;
// let pool = new pg.Pool({
// port: 5432,
// user: 'postgres',
// password: 'postgres',
// database: 'po1dev_v0.0.1',
// max: 10, //max connections
// host: 'localhost'
// })
let app = express();
app.use(bodyParser.json);
app.use(bodyParser.urlencoded({ extended:true }));
app.use(morgan('dev'));
app.use((request, response, next) => {
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
// app.post('/api/new-thing', (request,response) => {
// console.log(request.body)
// })
app.post('/api/new-thing', function(request,response){
console.log(request.body);
})
app.listen(PORT, () => console.log('Listening on port ' + PORT));
Any ideas on what may be causing the 404/hang problems in firefox/chrome and how to go about fixing it?
Cheers
That's because the route you're creating doen't return any response so it waits indefinitely for a response then gets timed out.
The route should return some response,
app.post('/api/new-thing', function(request,response){
console.log(request.body);
return response.json({"data": "Hello World"})
})
Which will return the {"data": "Hello World"} from the /api/new-thing route.
Also, bodyParser.json is a function not property. Change it to
app.use(bodyParser.json())
If you are using create-react-app try another port for the backend server. Because by default it uses 3000 for the react app.
Related
Since yesterday I'm trying to get the books on the google API for an exercise, I had to pass on a lot of errors and here is my final code, from this moment I can't move forward:
export async function fetchGoogle (url, options = {}) {
const headers = {Accept : 'application/json', ...options.headers}
const r = await fetch(url, {...options, headers})
if (r.ok) {
return r.json()
}
throw new Error ('Error server', {cause :r})
}
// Bypass error 200
var express = require('express')
var cors = require('cors')
var app = express()
var corsOptions = {
origin: 'http://example.com',
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
app.get('/products/:id', cors(corsOptions), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for only example.com.'})
})
app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})
Then I import this into my main script :
import { fetchGoogle } from "./api.js"
const books = await fetchGoogle ('https://www.googleapis.com/auth/books')
console.log(books)
After all these fights I get the error : Uncaught ReferenceError: require is not defined
at api.js:12:15
Thanks in advance for helping
Before I deploy, the app performed fine on localhost. But since I deployed my frontend (react) to Netlify and backend(node/express + mysql) to Heroku, all requests sent from the frontend started to get blocked by CORS policy, with the error message:
"Access to XMLHttpRequest at 'https://xxx.herokuapp.com/login' from origin 'https://xxx.netlify.app' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'https://xxx.app/' that is not equal to the supplied origin."
Most importantly, the value of my Access-Control-Allow-Origin header is literally the same as the origin stated.
Originally, I've tried to use a wildcard ("*"), but it seems that due to the withCredential problem, the system just can't allow that kind of vague statement.
I've also seen many people using Netlify.toml to tackle some configuration problems, but seems ineffective for me.
Is it the header's problem? If not, then what is the problem?
I really want to know what I should do to solve this error...
The console window of the app deployed:
Cors Error
My index.js in the server folder:
const express = require('express')
const mysql = require('mysql')
const cors = require('cors')
const session = require('express-session')
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser')
const port = 3010
const app = express()
app.use(express.json())
app.use(cors({
origin: ["https://xxx.app/"], // the link of my front-end app on Netlify
methods: ["GET", "POST"],
credentials: true
}))
app.use(cookieParser())
app.use(bodyParser.urlencoded({
extended: true
}))
app.use(
session({
key: "userId",
secret: "subscribe",
resave: false,
saveUninitialized: false,
cookie: {
expires: 60 * 60 * 24
},
})
)
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "https://xxx.netlify.app/"); // the link of my front-end app on Netlify
res.setHeader(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, PATCH, DELETE, OPTIONS"
);
res.setHeader('content-type', 'application/json');
next();
});
const db = mysql.createPool({
// create an instance of the connection to the mysql database
host: 'xxx.cleardb.net', // specify host name
user: 'xxx', // specify user name
password: 'xxx', // specify password
database: 'heroku_xxx', // specify database name
})
...
app.get('/login', (req, res) => {
if (req.session.user) {
res.send({
isLoggedIn: true,
user: req.session.user
})
} else {
res.send({
isLoggedIn: false
})
}
})
...
app.listen(process.env.PORT || port, () => {
console.log('Successfully Running server at ' + port + '.')
});
My Frontend:
import React, { useEffect, useState } from 'react'
import '../App.css'
import './HeroSection.css'
import { Link } from 'react-router-dom'
import Axios from 'axios'
function HeroSection() {
Axios.defaults.withCredentials = true
let username = "";
const [name, setName] = useState('');
const [isLoggedIn, setIsLoggedIn] = useState(false)
const [isLoading, setLoading] = useState(true)
...
useEffect(() => {
Axios.get('https://xxx.herokuapp.com/login').then((response) => {
if (response.data.isLoggedIn) {
username = response.data.user[0].username;
}
setIsLoggedIn(response.data.isLoggedIn)
Axios.post('https://xxx.herokuapp.com/getLang', {
username: username,
}).then((response) => {
console.log(response.data);
})
Axios.post('https://xxx.herokuapp.com/getStatus', {
username: username,
}).then(response => {
setName(response.data[0].firstname + " " + response.data[0].lastname);
setLoading(false);
})
})
}, [])
if (!isLoggedIn || isLoading) {
return (
<div>
...
</div>
)
} else {
return (
<div>
...
</div>
)
}
}
export default HeroSection
By the way, I use ClearDB MySQL on Heroku and MySQL WorkBench for the database, which all works fine.
You could debug by doing something like:
const allowList = ["https://yyy.app/"];
// Your origin prop in cors({})
origin: function (origin, callback) {
// Log and check yourself if the origin actually matches what you've defined in the allowList array
console.log(origin);
if (allowList.indexOf(origin) !== -1 || !origin) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
I have recently Implemented Proxy (in Express.js) for my React App to hide API URL's when making a request. It has been working perfectly fine when I run it the proxy and app on localhost. Now that I'm ready to deploy My application to AWS Amplify, I am a little confused as to how I get my proxy to run there since I'm not manually starting the app and proxy from the CLI. Do I need to use an EC2 instance instead or can I achieve this using Amplify?
Any Help would be greatly appreciated!
This is what my Project Directory Looks like :
This is what my Server.js looks like :
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const axios = require('axios');
var cors = require('cors')
app.use(cors())
app.use(bodyParser.urlencoded({
extended: false
}));
const BASE_URL = 'https://my-aws-lambda-base-url/dev'
const port = process.env.PORT || 5000;
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.listen(port, () => console.log(`Listening on port ${port}`));
app.use('/contact', require('body-parser').json(), async (req, res) => {
try {
await axios.post(`${BASE_URL}/contact`, {
Email : req.body.Email,
type : req.body.type,
Message : req.body.Message,
Phone : req.body.Phone
},
{
headers: {
'Content-Type': 'application/json',
}
},
).then(function(response) {
const result = response.data
console.log(result)
if(result && result.Message) {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(result))
}
}).catch((e)=> {
console.log(e)
res.send(false)
})
} catch (error) {
console.log(error)
res.send(false)
}
});
And this is how I make the request from In my React App
export async function sendContact(request){
try {
if(!request.Phone)
request.Phone = false
if(request.textMe){
request.type = "BOTH"
}
else{
request.type = "EMAIL"
}
let result ;
await fetch('/contact', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
Email : request.Email,
type : request.type,
Message : request.Message,
Phone : request.Phone
})
}
).then(async response =>
await response.json()
).then(data =>
result = data
).catch((e)=> {
notifyError(e.response.data.Message)
return false
})
console.log(result)
return result
} catch (error) {
console.log(error)
}
}
And Finally, Here's My Build Script from Amplify for my application
version: 1
frontend:
phases:
preBuild:
commands:
- npm i
build:
commands:
- npm run build
artifacts:
baseDirectory: build
files:
- '**/*'
cache:
paths:
- node_modules/**/*
P.S : I do also have "proxy": "http://localhost:5000" added to my Package.json
EDIT :
I tried Using a Background task manager like PM2 to run post build in the build script but that still did not work (although it did locally)
I ended up spinning up a proxy lambda as my API gateway (middle man) between my client and server. I also have the proxy denying any requests not coming from my website.
I'm trying to setup CSRF tokens so that I can do a number of checks before issueing a token to the client to use in future requests.
Taking the guidance from the csurf documentation, I've setup my express route with the following:
const express = require('express');
const router = express.Router({mergeParams: true});
const csurf = require('csurf');
const bodyParser = require('body-parser');
const parseForm = bodyParser.urlencoded({ extended: false });
const ErrorClass = require('../classes/ErrorClass');
const csrfMiddleware = csurf({
cookie: true
});
router.get('/getCsrfToken', csrfMiddleware, async (req, res) => {
try {
// code for origin checks removed for example
return res.json({'csrfToken': req.csrfToken()});
} catch (error) {
console.log(error);
return await ErrorClass.handleAsyncError(req, res, error);
}
});
router.post('/', [csrfMiddleware, parseForm], async (req, res) => {
try {
// this returns err.code === 'EBADCSRFTOKEN' when sending in React.js but not Postman
} catch (error) {
console.log(error);
return await ErrorClass.handleAsyncError(req, res, error);
}
});
For context, the React.js code is as follows, makePostRequest 100% sends the _csrf token back to express in req.body._csrf
try {
const { data } = await makePostRequest(
CONTACT,
{
email: values.email_address,
name: values.full_name,
message: values.message,
_csrf: csrfToken,
},
{ websiteId }
);
} catch (error) {
handleError(error);
actions.setSubmitting(false);
}
Postman endpoint seems to be sending the same data, after loading the /getCsrfToken endpoint and I manually update the _csrf token.
Is there something I'm not doing correctly? I think it may be to do with Node.js's cookie system.
I think your problem is likely to be related to CORS (your dev tools will probably have sent a warning?).
Here's the simplest working back-end and front-end I could make, based on the documentation:
In Back-End (NodeJS with Express) Server:
In app.js:
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var bodyParser = require('body-parser')
var express = require('express')
const cors = require('cors');
var csrfProtection = csrf({ cookie: true })
var parseForm = bodyParser.urlencoded({ extended: false })
var app = express()
const corsOptions = {
origin: "http://localhost:3000",
credentials: true,
}
app.use(cors(corsOptions));
app.use(cookieParser())
app.get('/form', csrfProtection, function (req, res) {
res.json({ csrfToken: req.csrfToken() })
})
app.post('/process', parseForm, csrfProtection, function (req, res) {
res.send('data is being processed')
})
module.exports = app;
(make sure you update the corsOptions origin property to whatever your localhost is in React.
In Index.js:
const app = require('./app')
app.set('port', 5000);
app.listen(app.get('port'), () => {
console.log('App running on port', app.get('port'));
});
In React:
Create file "TestCsurf.js" and populate with this code:
import React from 'react'
export default function TestCsurf() {
let domainUrl = `http://localhost:5000`
const [csrfTokenState, setCsrfTokenState] = React.useState('')
const [haveWeReceivedPostResponseState, setHaveWeReceivedPostResponseState] = React.useState("Not yet. No data has been processed.")
async function getCallToForm() {
const url = `/form`;
let fetchGetResponse = await fetch(`${domainUrl}${url}`, {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"xsrf-token": localStorage.getItem('xsrf-token'),
},
credentials: "include",
mode: 'cors'
})
let parsedResponse = await fetchGetResponse.json();
setCsrfTokenState(parsedResponse.csrfToken)
}
React.useEffect(() => {
getCallToForm()
}, [])
async function testCsurfClicked() {
const url = `/process`
let fetchPostResponse = await fetch(`${domainUrl}${url}`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"xsrf-token": csrfTokenState,
},
credentials: "include",
mode: 'cors',
})
let parsedResponse = await fetchPostResponse.text()
setHaveWeReceivedPostResponseState(parsedResponse)
}
return (
<div>
<button onClick={testCsurfClicked}>Test Csurf Post Call</button>
<p>csrfTokenState is: {csrfTokenState}</p>
<p>Have we succesfully navigates csurf with token?: {JSON.stringify(haveWeReceivedPostResponseState)}</p>
</div>
)
}
Import this into your app.js
import CsurfTutorial from './CsurfTutorial';
function App() {
return (
<CsurfTutorial></CsurfTutorial>
);
}
export default App;
That's the simplest solution I can make based on the CSURF documentations example. It's taken me several days to figure this out. I wish they'd give us a bit more direction!
I made a tutorial video in case it's of any help to anyone: https://youtu.be/N5U7KtxvVto
Following the example from OAuth2WebServer from google I'm trying to set up an authentication flow from an express app using the HTTP/REST method they have but with every request I am returned with an error
I went through Google OAuth “invalid_grant” nightmare — and how to fix it but unfortunately it did not help.
{
error: "unsupported_grant_type",
error_description: "Invalid grant_type: "
}
This is a shortened version of the error I am receiving. If you need to see more of the error let me know and I can post it.
Server
const express = require('express');
const axios = require('axios');
const { web } = require('./src/client_id.json');
const app = express();
const { client_id, client_secret } = web;
let count = 0;
app.use(express.json());
/*************************
** REDIRECT USER TO GOOGLE AUTH **
*************************/
app.get('/', (req, res) => {
const redirect_uri = 'http://localhost:5000/auth';
const scope = 'https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.metadata.readonly';
const access_type = 'offline';
res.redirect(`https://accounts.google.com/o/oauth2/v2/auth?scope=${ scope }&access_type=${ access_type }&redirect_uri=${ redirect_uri }&response_type=code&client_id=${ client_id }`);
});
/*************************
** ON AUTH WE EXCHANGE ACCESS TOKEN FOR REFRESH TOKEN **
*************************/
app.get('/auth', (req, res) => {
count++;
if (count >= 2) {
return res.redirect('http://localhost:3000');
}
const { code } = req.query;
const redirect_uri = 'http://localhost:5000/auth';
const grant_type = 'authorization_code';
axios.post('https://www.googleapis.com/oauth2/v4/token', {
code,
client_id,
client_secret,
redirect_uri,
grant_type
}, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(data => {
console.log(data)
res.redirect('http://localhost:3000');
})
// ALWAYS HITS THE CATCH "Invalid grant_type"
.catch(err => {
console.log(err);
console.log('ERROR')
});
});
app.listen(5000, console.log('Server listening on port 5000'));