So I tried integrating Stripe into React and it required setting up a node js express server.
The server is well set up and returns build folder of react when deployed to Heroku by some changes I made to my package.json and a new server.js file I wrote.
// package.json
{
...
"scripts": {
"dev": "react-scripts start",
"start": "node server.js",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"heroku-postbuild": "yarn run build"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"proxy": "http://localhost:9000",
"engines": {
"node": "10.15"
}
}
// server.js
const express = require("express");
const app = express();
const path = require("path")
const cors = require("cors")
app.use(express.static(path.join(__dirname, 'build')));
app.use('/charge', express.json());
// app.use(cors())
let whitelist = ['localhost:9000', 'http://myapp.herokuapp.com', 'http://herokuapp.com']
let corsOptions = {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
}
app.post("/charge", cors(corsOptions), async (req, res) => {
try {
let {amount, tokenId, stripe_key, company_name} = req.body; // params sent in with client
const stripe = require("stripe")(stripe_key); // initializes stripe with the stripe keys passed from client
// console.log(amount, tokenId, stripe_key);
let description = `Payment for posting for ${company_name} on MyApp.io`
let {status} = await stripe.charges.create({
amount,
currency: "usd",
description,
receipt_email: 'emeruchecole9#gmail.com',
source: tokenId
});
console.log(status);
res.json({status});
} catch (error) {
// res.status(500).end();
res.json({error}).end();
console.log(error)
}
});
app.get('*', (req,res) =>{
res.sendFile(path.join(__dirname+'/build/index.html'));
});
app.listen(process.env.PORT || 9000, () => console.log("Listening on port 9000"));
I have a Checkout.js file that handles the stripe payment. It sends the generated token to the express server.
The part of the Checkout.js file that sends the POST data to the server is:
axios({
method: 'post',
url: '/charge',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
data: {
amount: amount*100 || 2000, //this should be dynamic and coming from price of selected tier in tier selection plan
tokenId,
stripe_key,
company_name: company_name || 'Test Inc.'
}
})
The issue is this:
This totally works in dev mode (when I run yarn run dev and then node server.js to fire up the server) and also when I run yarn run build to manually build and yarn run start which fires up the server and serves the build file according to the server.js file above.
But after deploying to heroku and trying the post action, I get a 405 Not Allowed Error. I have tried adding CORS as seen in the server file above but it did not work.
Did you try to add the cors middleware to express
Like
app.use(cors())
Just Add Below code first of your server.js
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();
});
i hope this code help you,it was helpful for me.
also you can read this article:
clickme:)
Related
I've created a react app with node backend and recently ran the npm run build command on it.
when served, I noticed the fetch request sent from my frontend don't work properly and actually just respond with the index.html file in my frontend>public folder for no reason.
I'll try to add info that might be relevant.
In my package.json (the one that's in the frontend folder):
"proxy":"http://localhost:3000",
"scripts": {
"start": "set PORT=3001 && react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
In index.js (server side)
const PORT = process.env.PORT || 3000;
app.use(cors({
origin: 'http://localhost:3001',
credentials: true,
}));
an example of a post definition on the server:
app.post("/user/login", (req, res) => {
login(req, res)
});
and the request itself:
const requestOptions = {
method: 'POST',
headers: { 'accept': 'application/json', 'Content-Type': 'application/json' },
body: JSON.stringify({ "email": email, "password": password }),
credentials: 'include'
};
//HTTP://localhost:3000
fetch(`/user/login`, requestOptions).then(response => {
if (response.status == 200) {
console.log(document.cookie.split("=")[1])
setToken(document.cookie.split("=")[1]);
console.log("here")
}
else {
setError(true)
response.json().then((data) => {
setMessage(data.msg)
console.log(data.msg)
})
}
})
The request's status gives 200 and the respond is always the content of index.html . the request URL as shown on my browser is http://localhost:3000/user/login.
When I run the app with npm start and nodemon, everything works fine.
Only when I try to serve the build it stops working as it should.
Any idea what could be the problem?
So I followed a tutorial on how to deploy NextJs app to a subdomain on a Cpanel hosting by adding a server.js file and modifying the Package.json file with the following:
// server.js
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const hostname = 'localhost'
const port = process.env.port || 3000
// when using middleware `hostname` and `port` must be provided below
const app = next({ dev, hostname, port })
const handle = app.getRequestHandler()
app.prepare().then(() => {
createServer((req, res) => {
// Be sure to pass `true` as the second argument to `url.parse`.
// This tells it to parse the query portion of the URL.
const parsedUrl = parse(req.url, true)
const { pathname, query } = parsedUrl
if (pathname === '/a') {
app.render(req, res, '/a', query)
} else if (pathname === '/b') {
app.render(req, res, '/b', query)
} else {
handle(req, res, parsedUrl)
}
}).listen(port, (err) => {
if (err) throw err
console.log(`> Ready on http://${hostname}:${port}`)
})
})
//Package.json file
...
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "NODE_ENV=production node server.js",
"lint": "next lint",
"json-server": "json-server --watch db.json --port 3004"
}
...
I run npm build and uploaded the files to a folder that points to a subdomain. However, when I create my application in Node.js in Cpanel, the "Run NPM Install" button is greyed out and the information I keep getting is that the package.json cannot be found in the folder meanwhile it is actually there.
Any help on what could be wrong or a link to a better tutorial?
Your application root name should be the same with the application url.
Also ensure you uploaded all your file inside your application root name.
The components needed are the .next/ directory and files next.config.js, package.json and server.js, package-lock.json
Click Stop app button and refresh page.
I am currently building a simple CRUD app using ExpressJS, and host it on Heroku using free account.
The problem I ran into is:
GET API for getting all items works on localhost, but show status 503 when hosting on Heroku;
POST API for updating one item works on localhost, but same issue as GET API above;
All 503 errors are after 30s of loading, this should be a setting from Heroku.
I do have other API end points that work on both local and Heroku server:
GET API for getting one item using ID
My guessing:
The issue should not be a code issue
There is some issue when the code is deployed and Heroku cannot process this request
I tried to find some articles on the web but this seems hard to diagnose, anyone who has experience please let me know how I can solve this issue. Appreciate your comments.
My Mongoose Schema
const mongoose = require("mongoose");
const ThoughtSchema = mongoose.Schema({
title: {
type: String,
required: true
},
content: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model("Thought", ThoughtSchema);
2 end points that do not work
// Returns all thoughts
router.get("/", async (req, res) => {
try {
const thought = await Thought.find();
res.json(thought);
} catch (err) {
res.json({ message: err });
}
});
// Submit a post
router.post("/", async (req, res) => {
const thought = new Thought({
title: req.body.title,
content: req.body.content
});
try {
const savedThought = await thought.save();
res.json(savedThought);
} catch (err) {
res.json({ message: err });
}
});
The end point that works
// Specific thought
router.get("/:thoughtId", async (req, res) => {
try {
const thought = await Thought.findById(req.params.thoughtId);
res.json(thought);
} catch (err) {
res.json({ message: err });
}
});
My package.json for this express app
{
"name": "my-thoughts-app",
"version": "0.1.0",
"description": "An app to records user's thoughts",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/PayneTang/my-thoughts-app.git"
},
"author": "Payne Tang",
"license": "ISC",
"bugs": {
"url": "https://github.com/PayneTang/my-thoughts-app/issues"
},
"homepage": "https://github.com/PayneTang/my-thoughts-app#readme",
"dependencies": {
"dotenv": "^8.2.0",
"express": "^4.17.1",
"mongoose": "^5.8.11"
},
"devDependencies": {
"typescript": "^3.7.5"
}
}
EDIT:
My index.js
const express = require("express");
const path = require("path");
const app = express();
const mongoose = require("mongoose");
const thoughtRoute = require("./routes/thought");
require("dotenv").config();
console.log(process.env);
// Mongoose settings
mongoose.connect(
process.env.DB_CONNECTION,
{ useNewUrlParser: true, useUnifiedTopology: true },
() => {
console.log("Connected to DB!");
}
);
app.use(express.json());
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
app.use("/api/thought", thoughtRoute);
app.get("/api/test", (req, res) => {
res.send("hi");
});
// Serve client side
app.use(express.static(path.join(__dirname, "client/build")));
app.use(express.static(path.join(__dirname, "client/public")));
// app.get("*", (req, res) => {
// res.sendFile(path.join(__dirname, "client/build/index.html"));
// });
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log("Listening on port " + PORT + "...");
});
The root cause after checking is due to the access restriction from Heroku to Mongo atlas.
After adding 0.0.0.0/0 for IP whitelist, I am able to get data from MongoDB.
Reference: https://docs.atlas.mongodb.com/security-whitelist/#add-whitelist-entries
I had the same problem and I was able to fix it by doing these two steps:
Add node version to package.json file:
"engines": {
"node": "14.17.3"
}
}
I changed access settings in MongoDB, allowing access to the database from anywhere (basically, whitelisting 0.0.0.0/0 IP address)
In my case, the issue was in package.json I installed two new packages yesterday and in my package.json:
"engines": {
"node": "12.16.0"
},
I changed the version to the my current system version:
"engines": {
"node": "14.5.0"
},
this is how my 503 service unavailable error gone.
This can be caused in several ways to the Node app.
1- Make sure that you specify the right port as what heroku does is it runs our app on a dynamic port.
const PORT = process.env.PORT || 3000;
app.listen(PORT, err => {
if(err) throw err;
console.log("%c Server running", "color: green");
});
as described in this comment
https://stackoverflow.com/a/52992592/11887902
2- Make sure that you added the Procfile with
npm start
to start the application.
3- If you are using Nodemon and you install it globally, just make sure that you install it locally too.
Finally, just have a look at the logs of the project to figure what heppen to make your project not in the service.
These cases happened to me and caused this error.
My issue was caused due to case sensitivity that is at times ignored by node,
in my controllers I had a file named sessions.js yet when importing in my routes I had mistakenly put ... = require('../controllers/Sessions'),
Nodemon was running without issues as I was developing but upon deploying on heroku it crashed so I changed to ... = require('../controllers/sessions')
and now it runs ok
The issue I am having is my React application hosted on Heroku is calling "https://localhost:8000" for it's calls to the Express server.
I have the proxy in package.json set to https://localhost:8000 to call my Express server. From my understanding this is all I need to do and Heroku handles the connection when it is deployed.
When I go to my endpoint like so: https://heroku-app.herokuapp.com/v1/products/:productid my Express server successfully sends back JSON data in the browser, so I do know my Node server is up and running on Heroku. The issue seems to be the React app proxy is not calling the Heroku URL post-deploy.
Here is my React apps package.json:
{
"name": "client",
"version": "0.1.0",
"private": true,
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"proxy": "http://localhost:8000/",
"devDependencies": {
"enzyme-matchers": "^7.0.2"
}
}
This is the package.json file for my server:
{
"name": "stub_boilerplate",
"version": "1.0.0",
"description": "Quick Stub",
"main": "server.js",
"scripts": {
"test": "jest",
"start": "node server/server.js",
"heroku-postbuild": "cd client && npm install --only=dev && npm install && npm run build"
},
"engines": {
"node": "~9.10.1",
"npm": "~5.6.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/manm/xxx.git"
},
"author": "Maison M",
"license": "MIT",
"bugs": {
"url": "https://github.com/maonm/xxx/issues"
}
}
Here is my server.js file. I am setting the port to process.env.PORT || 8000:
const express = require('express');
const app = express();
const port = process.env.PORT || 8000;
//Allows access to enviroment variables in development
require('dotenv').config({ path: __dirname + '/.env' });
//Middleware
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(methodOverride('_method'));
//Serve build folder of client
app.use(express.static(path.join(__dirname, '../client/build')));
app.use('/v1/products', product_routes);
//Error handling
app.use(errorHandler);
//Initialize Express server
app.listen(port, err => {
if (err) console.info(`Error: The server failed to start on ${port}`);
else console.info(`****** Node server is running on ${port} ******`);
});
This is the fetch() request inside of the component:
componentDidMount() {
this.fetchStripePlans();
}
fetchStripePlans = () => {
const stripeProduct = 'prod_FlXXXXXBVn8'; //QS (product)
const url = `http://localhost:8000/v1/products/${stripeProduct}`;
const fetchConfig = {
method: 'GET',
headers: {
'content-type': 'application/json'
}
};
fetch(url, fetchConfig)
.then(data => data.json())
.then(stripe => {
const { data } = stripe;
this.setState({
stripePlans: data
});
})
.catch(err => {
this.setState({
error: true,
errorMessage: err.genericError
});
});
};
This is what I am seeing in the console of the React app:
SignUpContainer.js:48 OPTIONS http://localhost:8000/v1/products/prod_FRon8 net::ERR_CONNECTION_REFUSED
So to me logically, it's not being routed to the Heroku URL. I've scoured a few tutorials on deploying React/Express projects to Heroku and all of them leave the React proxy set to the local host of the Express server. So I am not too sure what is happening here.
In order to make use of the proxy value in your package.json, you must specify a relative URL in your fetch request, such as /v1/products/${stripeProduct}. You should not include the hostname or port in your component.
For reference, see "Running the server and the React app" and "Using the proxied server from React" sections in here: https://www.twilio.com/blog/react-app-with-node-js-server-proxy
Although a GET request usually qualifies as a simple request, the fact that the Content-Type is set as application/json qualifies it as a pre-flight [1] request. Therefore, what happens is that the browser sends a HTTP request before the original GET request by OPTIONS method to check whether it is safe to send the original request.
Try enabling CORS Pre-Flight for your route handler sending the application/json response. You can do this by using the cors [2] middleware in the options handler for your route, like such:
const express = require('express')
cosnt cors = require('cors')
const app = express()
app.options('/products/:id', cors()) // enable pre-flight request for GET request
app.get('/products/:id', cors(), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for all origins!'})
})
[1] https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
[2] https://www.npmjs.com/package/cors
Parameters are set and once button is pressed, a request [Node request package] or fetch request is made to be received by expressjs request. While link address is created, response returned is 400 Bad Request.
I've tried both a fetch request and a node package 'request' and
Server JS
const express = require ('express');
const path = require('path') //core node module
const app = express();
const cors = require('cors');
const router = express.Router();
// app.use(cors())
const publicdirpath = path.join(__dirname, '../public')
console.log(path.join(__dirname, '../public'))
app.use(cors());
app.use(express.static(publicdirpath))
app.post('/testcall', (req, res) => {
if(!req.query.startdate && !req.body.enddate &&
!req.body.projectnumber){
return res.status(400).send({
success: 'false',
message: 'dates or project or both required'
});
}
//call stored procedures
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
JS Class that calls express JS
handleDropdownClick = (event, selection) =>{
const { name, innerText, value } = event.target;
console.log( event.target + " : " + innerText + " : " +
this.props.formData);
const request = require('request');
switch(selection){
case 1:
//call api or stored procedure
if(this.validation()){
//call api
request.get({url:"http://localhost:3000/testcall", qs:this.state.formData} , function (err, res, body) {
if(err){
console.error('error with request: error: ' + err + '. res: ' + res + ' + body: ' + body);
}
console.log("Get response: " + res.statusCode + ". Body: " + body);
})
//Using Fetch
const jsonData = JSON.stringify(this.state.formData);
fetch('/testcall', {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'credentials': 'include'
},
body: jsonData
})
.then( response => {
return response.json();
})
.then( response => {
console.log(response);
})
.catch(function (e) {
console.log("fail: " + e);
})
}
break;
//more code
Package.json
{
"name": "app-name",
"version": "0.1.0",
"private": true,
"main": "index.js",
"dependencies": {
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"nodemon": "^1.19.1",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-scripts": "3.0.1",
"request": "^2.88.0",
"save": "^2.4.0",
"semantic-ui-react": "^0.87.2",
"table": "^5.4.1",
"tedious": "^6.2.0",
"webpack": "^4.29.6"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"dev": "run-p server start"
},
"eslintConfig": {
"extends": "react-app"
},`enter code here
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"proxy":"http://localhost:3000"
}
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
File Path
public
src
-api
-components
-storedprocedures
-tabs
-app.js
-config.js
-index.js
-server.js
package-lock.json
package.json
Expected results would be for the expressjs to accept the call and complete logic.
Actual results are:
POST http://localhost:3000/calculatecommission 400 (Bad Request)
- for the npm package request
request.js:149 GET http://localhost:3000/calculatecommission?projectnumber=&startdate=2019-06-02&enddate=2019-06-28 400 (Bad Request)
- for fetch request
You can use axios or use proxy at front-end
I would recommend using Axios as well, it's a whole lot easier to use then fetch. You can find out more here
https://alligator.io/react/axios-react/
Got the same result with axios unfortunately. I'm wondering if the issue is that the express is server file is set up wrong. Maybe it's not getting the request at all.
request.js:149 GET http://localhost:3000/calculatecommission?projectnumber=&startdate=2019-06-02&enddate=2019-06-28 400 (Bad Request) - for fetch request
Any time you get an error like this, don't forget, you are literally allowed to run this command yourself in the command-line interface. That will give you the exact error that the install script is actually running into, i.e.:
GET https://www.google.com/
In my case, the error was: HTTPS not supported without installing LWP::Protocol::https.
So:
I resolved all of the issues from the GET command.
Since the network it was trying to access was private, I used sudo npm install --s.