I'm trying to create a form with Node.js and React where you can input your email to submit it to a Mailchimp newsletter, but when I click submit, I get a "Cannot POST /" error.
This is server.js
const bodyParser = require('body-parser');
const favicon = require('serve-favicon');
const express = require('express');
const app = express();
app.use(express.static(__dirname + '/client/public'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(favicon(__dirname + '/public/favicon.ico'));
app.listen(process.env.PORT, function() {
console.log("Listening to the application");
});
app.get('/', (req, res) => {
res.send("res");
});
app.post('/', (req, res) => {
sendEmailToMailchimp(req.body.email);
console.log(req.body.email);
});
function sendEmailToMailchimp(email){
console.log("Sending " + email + " to Mailchimp.");
var request = require("request");
var options = { method: 'POST',
url: 'https://us18.api.mailchimp.com/3.0/lists/f9c3130fc4/members',
headers:
{ 'Postman-Token': 'f6c16b72-09b7-48f2-926b-b156a428c67b',
'Cache-Control': 'no-cache',
Authorization: 'Basic YW55c3RyaW5nOmNiNTQyYzA1NWVmMjY1ZTI4Y2I0ZDk0NmRhZmM5MmYzLXVzMTg=',
'Content-Type': 'application/json' },
body: { email_address: email, status: 'subscribed' },
json: true };
request(options, function (error, response, body) {
if (error) throw new Error(error);
//console.log(body);
});
}
This is package.json
{
"name": "myapp",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js",
"server": "nodemon server.js",
"client": "npm start --prefix client",
"dev": "concurrently \"npm run server\" \"npm run client\""
},
"keywords": [
"example",
"heroku"
],
"author": "",
"license": "MIT",
"dependencies": {
"body-parser": "^1.18.3",
"concurrently": "^3.6.1",
"express": "^4.16.3",
"jquery": "^3.3.1",
"nodemon": "^1.18.3",
"request": "^2.87.0",
"serve-favicon": "^2.5.0"
},
"engines": {
"node": "8.11.1"
}
}
This is App.js in my client React app. The input field for the name still isn't meant to do anything yet, just the email.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<form className="newsletterSignup" action="/" method="post">
<label>NAME</label>
<input id="nameInput" className="infoInput inlineInput" name="userName" placeholder="John Ipcus" type="text" required></input>
<label>EMAIL ADDRESS</label>
<input id="emailInput" className="infoInput inlineInput" name="userEmailAddress" placeholder="joe#shmoe.com" type="text" required></input>
<input id="signUpBtn" value="SUBMIT" type="submit"></input>
</form>
</div>
);
}
}
export default App;
And this is the package.json in the client React app
{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^16.4.2",
"react-dom": "^16.4.2",
"react-scripts": "1.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"proxy": {
"/": {
"target": "http://localhost:5000",
"secure": "false"
}
}
}
Also if it helps mentioning, I'm going to deploy it on Heroku.
Thanks
Edit: the browser console displays "Failed to load resource: the server responded with a status of 404 (Not Found)"
Do you use webpack-dev as react development server?
If so you need to setup proxy there, webpack.config.js like:
module.exports = setup({
context: __dirname,
entry: './app.js',
devServer: {
proxy: {
'/api': 'http://127.0.0.1:50545'
}
}
});
Related
server.js
import express from 'express';
import { routes } from './routes';
import { db } from './db';
const app = express();
app.use(express.json());
routes.forEach(route => {
app[route.method]('/api' + route.path, route.handler);
});
const start = async () => {
await db.connect('mongodb://localhost:27017');
await app.listen(8080);
console.log('Server is listening on port 8080');
};
start();
one route deleteIngredientRoute.js
import { deleteIngredient, getIngredients } from '../db';
export const deleteIngredientsRoute = {
path: '/meals/:name',
method: 'DELETE',
handler: async (req, res) => {
const { name } = req.params;
await deleteIngredient(name);
const updatedIngredients = await getIngredients();
res.status(200).json(updatedIngredients);
},
};
package.json
{
"name": "fsa-back-end-template",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "npx nodemon --exec 'npx babel-node src/server.js'"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"nodemon": "^2.0.20"
},
"dependencies": {
"#babel/cli": "^7.19.3",
"#babel/core": "^7.20.2",
"#babel/node": "^7.20.2",
"#babel/preset-env": "^7.20.2",
"#babel/register": "^7.18.9",
"express": "4.17.1",
"mongodb": "4.1.0"
}
}
Error
app[route.method]('/api' + route.path, route.handler);
^
TypeError: app[route.method] is not a function
Also sometimes i get this error
''npx' is not recognized as an internal or external command,
operable program or batch file.
[nodemon] app crashed - waiting for file changes before starting...
I am using NVM i added to environment Path variables
every thing is ok but when we write the code in this format
only show api call in formate of json or send but not show the react template
i can't undersatnd
const express = require('express');
const app = express();
const port = process.env.PORT || 5000;
const NODE_ENV = process.env.NODE_ENV || "devlopment"
const path = require("path");
var cors = require('cors')
app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: true }));
app.get('/', (req, res) => {
res.json({
message: "Mern Deploy secces"
})
})
app.get('/sample', (req, res) => {
res.send("Hello Express");
})
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"))
app.get(" * ", (req, res) => {
res.sendFile(path.resolve(__dirname, "../client", "build", "index.html"))
});
console.log('production modde')
}
app.listen(port, () => {
console.log('server is running on 5000')
})
this is my node js file if i write the code in this formate
only show react js not show api request
index.js
const express = require('express');
const app = express();
const port = process.env.PORT || 5000;
const NODE_ENV = process.env.NODE_ENV || "devlopment"
const path = require("path");
var cors = require('cors')
app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: true }));
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"))
app.get(" * ", (req, res) => {
res.sendFile(path.resolve(__dirname, "../client", "build", "index.html"))
});
console.log('production modde')
}
app.get('/', (req, res) => {
res.json({
message: "Mern Deploy secces"
})
})
app.get('/sample', (req, res) => {
res.send("Hello Express");
})
app.listen(port, () => {
console.log('server is running on 5000')
})
This is my React js File
import Raact, { useEffect ,useState } from 'react';
import './App.css';
import axios from 'axios';
function App() {
const [show, setshow] = useState("")
useEffect(()=>{
axios.get('/',)
.then((result)=>{
setshow(result.data.message)
}).catch((err)=>{
console.log(err)
})
},[])
return (
<div className="App">
<h1>Hello world</h1>
<h1>{show}</h1>
</div>
);
}
export default App;
client package.json
{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
"#testing-library/jest-dom": "^5.11.10",
"#testing-library/react": "^11.2.6",
"#testing-library/user-event": "^12.8.3",
"axios": "^0.21.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
"web-vitals": "^1.1.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"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:5000/"
}
package.json server
{
"name": "MERN-DEPLOY",
"version": "1.0.0",
"description": "",
"main": "index.js",
"engines": {
"node": "14.15.5",
"npm": "7.5.4"
},
"scripts": {
"start": "node server/index.js",
"backend": "nodemon server/index.js",
"fontend": "npm run front --prefix client",
"heroku-postbuild": "NPM_CONFIG_PRODUCTION=false npm install --prefix client && npm run build --prefix client ",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.17.1",
"nodemon": "^2.0.7"
}
}
I am trying to upload my app to Heroku which has frontEnd and sending mail backend both are working in my local machine. Please help anyone to solve my issue that will be of great help because this is my first and practice project to be live.
The structures and error are mentions below:
Heroku error
heroku logs --tail
client package.json
{
"name": "hephy-web",
"version": "0.1.0",
"private": true,
"dependencies": {
"#testing-library/jest-dom": "^4.2.4",
"#testing-library/react": "^9.5.0",
"#testing-library/user-event": "^7.2.1",
"axios": "^0.19.2",
"bootstrap": "^4.4.1",
"react": "^16.13.1",
"react-bootstrap": "^1.0.0",
"react-dom": "^16.13.1",
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.1"
},
"scripts": {
"start": "react-scripts start",
"heroku-postbuild": "cd client && npm install && npm run build",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
server package.json
{
"name": "hephy-back-end",
"version": "1.0.0",
"description": "Hephy BackEnd",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Uchom",
"license": "ISC",
"dependencies": {
"#sendgrid/mail": "^7.0.1",
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"express-handlebars": "^4.0.3",
"nodemailer": "^6.4.6"
}
}
server app.js
require('dotenv').config()
const path = require('path')
var express = require('express');
var router = express.Router();
var nodemailer = require('nodemailer');
var cors = require('cors');
require('dotenv').config()
var transport = {
service: 'gmail',
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS
}
}
var transporter = nodemailer.createTransport(transport)
transporter.verify((error, success) => {
if (error) {
console.log(error);
} else {
console.log('Server is ready to take messages');
}
});
router.post('/send', (req, res, next) => {
var name = req.body.name
var email = req.body.email
var message = req.body.message
var content = `Name: ${name} \n \nE-mail: ${email} \n \nMessage: ${message} `
var mail = {
from: name,
to: 'xxxxxxxxxxx', // Change to email address that you want to receive messages on
subject: 'Hephy Inquiry Contact',
text: content
}
transporter.sendMail(mail, (err, data) => {
if (err) {
res.json({
status: 'fail'
})
} else {
res.json({
status: 'success'
})
}
})
})
const app = express()
app.use(cors())
app.use(express.json())
app.use('/', router)
app.listen(3005)
// Serve static files from the React frontend app
app.use(express.static(path.join(__dirname, 'client/build')))
it looks like you app listens to port 3005, this wont work on Heroku where you need to bind to the port provided
const PORT = process.env.PORT || 3005;
I've been able to use React for heroku applications before and I've had successful api calls without React on heroku, but I've never been able to mix the two. Not even once.
The api route works on localhost.
I have incredibly basic code that produces a 404 error whenever I attempt to access one of the api routes on deployment to Heroku. Below is my server.js file:
const express = require("express");
const mongoose = require("mongoose");
const PORT = process.env.PORT || 3001;
const path = require("path");
const routes = require("./routes");
const app = express();
let MONGODB_URI = process.env.MONGODB_URI || "mongodb://localhost/database";
app.use(express.urlencoded({extended:false}));
app.use(express.json());
app.use(express.static("public"));
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
}
mongoose.connect(MONGODB_URI, {useNewUrlParser: true, useUnifiedTopology: true});
mongoose.connection.on("connected", function() {
console.log("~ Connected To Database ~");
});
app.use(routes);
app.get("*", function(req,res) {
res.sendFile(path.join(__dirname, "/client/build", "index.html"));
});
app.listen(PORT, function() {
console.log("App listening in on " + PORT);
});
My api route is set via the file structure:
routes (located in the same directory as server.js)
index.js
api.js
Here is the index.js:
const apiRoutes = require("./api.js");
const router = require("express").Router();
router.use("/api", apiRoutes);
module.exports = router;
And here is the api.js:
const router = require("express").Router();
router.get("/users/all", function(req, res) {
console.log("Running! The API Route is Being Called!");
res.send("Success");
});
module.exports = router;
Here is the Base react component where the axios call is initiated:
import React, {Component} from "react";
import "./style.css";
import axios from "axios";
class Base extends Component {
testAxios = async () => {
axios.get("/api/users/all");
}
render() {
return (
<div>
<p>Helloooo</p>
<button onClick = {this.testAxios}>Test Axios</button>
</div>
)
}
}
export default Base;
And, finally, here are the relevant package.json files:
For the Client Folder:
{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-router-dom": "^5.1.2",
"react-scripts": "3.2.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
For the Root Folder:
{
"name": "garbage",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "if-env NODE_ENV=production && npm run start:prod || npm run start:dev",
"start:prod": "node server.js",
"start:dev": "concurrently \"nodemon --ignore 'client'/*'\" \"npm run client\"",
"install": "cd client && npm install",
"client": "cd client && npm run start",
"build": "cd client && npm run build",
"heroku-postbuild": "cd client && npm install --only=dev && npm install && npm run build"
},
"devDependencies": {
"concurrently": "^5.0.0",
"nodemon": "^1.19.4",
"http-server": "^0.11.1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^0.19.0",
"concurrently": "^5.0.0",
"express": "^4.17.1",
"mongoose": "^5.7.10",
"node": "^12.11.1",
"nodemon": "^1.19.3",
"react": "^16.10.2",
"react-router-dom": "^5.1.2"
},
"repository": {
"type": "git",
"url": "git+https://github.com/garbage/garbage.git"
}
}
I've tried adding a static.json file, and it didn't work. If you have any ideas, please let me know.
I've discovered the source of my problem. In the package.json, it was one line:
"scripts": {
"start": "node server.js",
"start:original": "if-env NODE_ENV=production && npm run start:prod || npm run start:dev"
}
The "start:original" line was, as it implies, the original start function. Rather than rely on the "if-env NODE...", I simply replaced it with "node server.js". When I'm developing and I want to start the server with concurrently, I now just use node run start:dev.
Since then, all my React apps have successfully worked with api routes.
I'm new to react and node and I'm trying to build a simple app to retrieve stock prices.
I'm able to reach my server route that I want but I'm getting a FetchError: invalid json response body from my server, what does this mean? How should I properly fetch an external api in my express route and send that data as a response?
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
state = {
response: '',
symbol: '',
};
handleSubmit = async e => {
e.preventDefault();
let apiURL = `/stocks/${ this.state.symbol }/price`
fetch(apiURL)
.then(res => res.json())
.then( (result) => {
this.setState({
response: result
})
})
};
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
</header>
<form onSubmit={this.handleSubmit}>
<input
type="text"
value={this.state.symbol}
onChange={e => this.setState({ symbol: e.target.value })}
/>
<button type="submit">Submit</button>
</form>
<p>{this.state.response}</p>
</div>
);
}
}
export default App;
server.js
const express = require('express')
const path = require('path')
const bodyParser = require('body-parser')
const fetch = require('node-fetch');
const app = express()
const port = process.env.PORT || 5000
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
// API routes
app.get('/stocks/:symbol/price', (req, res) => {
const token = 'pk_5f69bda1e3074237a9d2e844a3dafbff'
const symbol = req.params.symbol
const apiURL = `https://sandbox.iexapis.com/stable/stock/${symbol}/price?token=${token}`
fetch(apiURL)
.then(res => res.json())
.then(data => {
res.send({ data })
})
})
app.listen(port, () => console.log(`Listening on port ${port}`))
package.json
{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.19.0",
"body-parser": "^1.18.3",
"express": "^4.16.4",
"node-fetch": "^2.6.0",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"react-scripts": "0.9.x"
},
"devDependencies": {
"concurrently": "^4.0.1",
"nodemon": "^1.9.2",
"ws": "3.3.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"client": "yarn start",
"server": "nodemon server.js",
"dev": "concurrently --kill-others-on-fail \"yarn server\" \"yarn client\""
},
"proxy": "http://localhost:5000/"
}