Heroku server api call (localhost) not working - node.js

I'm on the final stretch of my web app, and I'm trying to deploy it with Heroku. The backend is very simple, and just uses express and while I was coding, a simple localhost server to create a token on login (localhost:8080/login). Unfortunately, on Heroku the login and token generation will not work unless I'm using my computer and I've ran node server.js already in my terminal. Also not sure if this context is needed, but locally, my react app is launched at localhost:3000 while the server is localhost:8080/login. I've heard that I should change them both to be the same localhost with some slight changes to the url, but nothing's worked so far. What changes do I need to make to the relevant code below so that Heroku will use its own server for the api call?
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.use('/login', (req, res) => {
res.send({
token: 'test123' // simple test send token
});
});
app.listen(8080, () => console.log('API is running on http://localhost:8080/login'));
Server.js (I have to run npm start, Ctrl+C, then node server.js in order to login properly)
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import './Login.css';
async function loginUser(credentials) {
return fetch('http://localhost:8080/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(credentials)
})
.then(data => data.json())
}
export default function Login({ setToken }) {
const [username, setUserName] = useState();
const [password, setPassword] = useState();
const handleSubmit = async e => {
e.preventDefault();
const token = await loginUser({
username,
password
});
setToken(token);
}
return(
<div className="login-wrapper">
<h1>Please Log In</h1>
<form onSubmit={handleSubmit}>
<label>
<p>Username</p>
<input type="text" onChange={e => setUserName(e.target.value)} />
</label>
<label>
<p>Password</p>
<input type="password" onChange={e => setPassword(e.target.value)} />
</label>
<div>
<button type="submit">Submit</button>
</div>
</form>
</div>
)
}
Login.propTypes = {
setToken: PropTypes.func.isRequired
};
Login component where token will be generated

This is due to your Heroku app's localhost is not pointing to your server application.
The most straight forward way to resolve this is to publish your server application on Heroku as well, then change your function loginUser to point to the URL to your server application's Heroku URL. You may later refactor the URL to refer to an environment variable so you have zero code change when developing locally and pushing to Heroku. This deploy guide will probably come in handy for you in this case.
If you do not intend to have publish your server application on Heroku due to any reason, you may also start your server on a local machine and expose it through ngrok, but you will need to refactor the fetch URL now as ngrok will have different URL every time you start it.

Related

How to make external GET request using Nuxt.js Server Middleware

I am working with a Nuxt.js v2.15.8 project and I am attempting to use the server middleware feature that Nuxt offers for A custom API endpoint. https://nuxtjs.org/docs/configuration-glossary/configuration-servermiddleware/#custom-api-endpoint
What I am trying to accomplish:
Use Nuxt server middleware to make a GET request to a 3rd party api to retrieve data. When I try to set this up and make the request to the endpoint in Postman, I get an error
<!doctype html>
<html data-n-head-ssr lang="en" data-n-head="%7B%22lang%22:%7B%22ssr%22:%22en%22%7D%7D">
<head>
<title>This page could not be found</title> etc....
How do I use the Nuxt server middleware to make api calls to external api's?
Nuxt.config.js
serverMiddleware: [
{
path: '/api/server-middleware',
handler: '~/api/getData.js',
},
],
~/api/getData.js
const bodyParser = require('body-parser');
const app = require('express')();
app.use(bodyParser.json());
app.all('https://jsonplaceholder.typicode.com/todos/1', (req, res) => {
res.json({ data: res.data });
});
module.exports = app;
In Postman I try to make a GET request to http://localhost:3000/api/server-middleware after running npm run dev and my Nuxt project is running.
Am I misunderstanding how this is supposed to work? Is the Server Middleware for internal api calls only?
Applying the least possible amount of changes to your shared code gives us the following
getData.js
import axios from 'axios'
const app = require('express')()
app.all('/jsonplaceholder/:id', async (req, res) => {
const { data } = await axios(
`https://jsonplaceholder.typicode.com/todos/${req.params.id}`
)
res.json({ ...data })
})
module.exports = app
/pages/index.vue
<template>
<div>
<input id="name" v-model="todoId" type="text" name="name" />
<button #click="callNuxtApi">try local Nuxt API</button>
<div>
Response from the backend:
<pre>{{ response }}</pre>
</div>
</div>
</template>
<script>
export default {
name: 'JsonPlaceholderPage',
data() {
return {
todoId: 1,
response: {},
}
},
methods: {
async callNuxtApi() {
const response = await this.$axios.$get(`/api/server-middleware/jsonplaceholder/${this.todoId}`)
console.log('response', response)
this.response = response
},
},
}
</script>
As you can see, /jsonplaceholder/:id is something more reasonable considering that it will be prefixed by /api/server-middleware/ already.
Having https:// inside of a path is not really nice to the browser overall.
PS: you need to install axios and express for it to work. #nuxtjs/axios will not work here.
This answer joins my other one here: https://stackoverflow.com/a/72102209/8816585

User session does not get destroyed Nodejs

Guys I have this logout button on my react app that executes the LogoutSession() function that goes like this:
<span onClick={LogoutSession} className="btn btn-light rounded-pill text-primary py-2
px-4 ms-lg-5">Log Out</span>
And this is the function:
const LogoutSession = (e) =>{
e.preventDefault();
Axios.get('http://localhost:3001/logout')
}
And in my server side (nodejs) I have this route:
router.get('/logout', async(req,res) => {
req.session.destroy();
console.log(req.session)
});
And I guess the problem is with the LogoutSession function cause I didn't know how to use Axios well to make that route work, is there any parameters that I'm missing or something I did wrong in the client side
Setting the session to null will also work:
req.session = null;

fetch and get requests URL

I'm trying to get information with a fetch (client) and a get (server) requests to get data from the server with the client and printing it.
for some reason I can't get the information I'm looking for and I think it has somthing to do with the url I'm entering, can I get an explanation, or maybe an example about the url I'm supposed to enter?
I'll enter my code as an example:
client:
//bitcoin page: url - 'http://localhost:3000/bitcoin'
//NOTE: the proxy is: 'http://localhost:3001'
import React from "react";
import { Link } from "react-router-dom";
function BitCoin() {
const [data, setData] = React.useState(null);
console.log("entered bitcoin page");
React.useEffect(() => {
fetch("NOT SURE WHAT TO WRITE HERE")
.then((res) => res.json())
.then((data) => setData(data.message));
}, []);
return (
<div style={{textAlign:"center", fontFamily:"Comic Sans MC", fontSize:"100"}}>
THIS IS THE BitCoin PAGE
<nav>
<Link to="/"> Home </Link>
</nav>
<nav>
<Link to="/coins"> Coins </Link>
</nav>
<p>{!data ? "Loading..." : data}</p>
</div>
)
}
export default BitCoin;
server:
//index.js: url - 'http://localhost:3001'
const express = require("express");
const PORT = process.env.PORT || 3001;
const app = express();
app.get('NOT SURE WHAT TO WRITE HERE', (req, res) => {
console.log("entered bitcoin query!");
let msg = "";
//some functions to get the msg I'm looking for (not relevant)
res.json({ message: msg });
});
app.listen(PORT, () => {
console.log(`Server listening on ${PORT}`);
});
as you can see there's a log when entering the get request but the console's not logging it, I'm guessing the client and the server are not communicating and that's what makes the problem.
thank you <3
It's important to understand the general architecture of your technology stack:
You have the React frontend running under http://localhost:3000. This is simply serving the React app through a development server on your computer.
Additionally, you have a NodeJS app (using express) running at http://localhost:3001. Notably, this runs under a different port to the React app.
In your express code, you would define a url which the React frontend can call, to fetch data. So let's call it /coin-message:
app.get('/coin-message', (req, res) => {
Now, in the React app, you can make ajax requests to this url:
fetch("http://localhost:3001/coin-message")
Note that you need to include the full URL and port in the fetch() - this is because the Node app runs under a different port.
Important
Because you want to make ajax requests to a URL which has a different port to the React app, you will encounter a Same Origin Policy problem. This is a security measure activated by browsers to prevent web app vulnerabilities. To solve this, you can use one of the CORS middlewares for express, such as this one.
server:
app.get('/bitcoin', ...)
client:
fetch('http://localhost:3001/bitcoin')

React is having trouble rendering elements when I serve the React from the same origin as express

I'm serving a React build from the same port (port 3000) that my express files receive requests on. Below is my code for doing so.
app.use(express.static(path.join(__dirname, "client", "build")))
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, "client", "build", "index.html"))
})
In my react app, I make an API call to the server and the API's response updates the state. I know (from testing) that I'm getting the correct response from the server, and I know that the state is properly updated with the response. But when I try to render <p>{this.state.data}</p>, the entire app breaks and nothing is rendered.
Anyone have any ideas about why this is happening? I tried serving the React app from its folder on a different port and it still doesn't work. Posting all relevant code below for reference.
React Code
import React from 'react';
import './App.css';
class Form extends React.Component{
constructor(props){
super(props);
}
onTrigger = (event) => {
this.props.callBack();
}
render(){
return(
<div>
<form action="/photoupload" method="POST" className="" encType="multipart/form-data">
<label for="species">Species Name:
<input type="text" name="species" id="species"/>
</label>
<label for="description">Description:
<textarea name="description" id="description" placeholder="Enter a description of your shroom..."></textarea>
</label>
<label for="photo">Photo:
<input type="file" name="mushroom" id="mushroom" accept="image/*"/>
</label>
<input onClick={this.onTrigger} type="submit"/>
</form>
</div>
)
}
}
class App extends React.Component {
constructor(props){
super(props);
this.state = {
data: ""
};
}
componentDidMount(){
fetch('/api', {method: "GET"})
.then(response => response.json())
.then(json => this.setState({
data: json
}))
console.log(this.state)
}
handleClick = () => {
console.log("Hello!")
}
render(){
return (
<div>
<Form callBack={this.handleClick} />
<p>{this.state.data}</p>
</div>
)
}
}
export default App;
Server Code
const express = require('express')
const app = express();
const bodyParser = require('body-parser')
const multer = require('multer')
const upload = multer({dest: 'uploads/'})
const fs = require('fs')
const Mushroom = require('./db')
const path = require('path')
const cors = require('cors')
app.use(bodyParser.urlencoded({extended: false}));
app.use(express.static(path.join(__dirname, "client", "build")))
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, "client", "build", "index.html"))
})
app.get('/api', (req, res) => {
const mushroom = Mushroom.find({}, (err, docs) => {
res.send(docs)
});
})
app.post('/photoupload', upload.single('mushroom'), function (req, res) {
var mushroom = new Mushroom();
mushroom.species = req.body.species;
mushroom.description = req.body.description;
mushroom.path = req.file.path;
mushroom.save()
res.redirect("/")
})
app.listen(3000, () => {
console.log("listening on 3000!")
})
EDIT
I'm just going to copy and paste the errors below:
App.js:51 Objectdata: "{_id: 132454}"__proto__: Object
:3000/manifest.json:1 Failed to load resource: the server responded with a status of 404 (Not Found)
:3000/manifest.json:1 Manifest: Line: 1, column: 1, Syntax error.
:3000/favicon.ico:1 Failed to load resource: the server responded with a status of 404 (Not Found)
react-dom.production.min.js:216 Error: Minified React error #31; visit https://reactjs.org/docs/error-decoder.html?invariant=31&args[]=object%20with%20keys%20%7B_id%2C%20age%2C%20species%2C%20description%2C%20path%2C%20__v%7D for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
at ka (react-dom.production.min.js:140)
at d (react-dom.production.min.js:144)
at m (react-dom.production.min.js:146)
at react-dom.production.min.js:150
at Do (react-dom.production.min.js:176)
at Hu (react-dom.production.min.js:271)
at Pi (react-dom.production.min.js:250)
at xi (react-dom.production.min.js:250)
at _i (react-dom.production.min.js:250)
at vi (react-dom.production.min.js:243)
uu # react-dom.production.min.js:216
react-dom.production.min.js:140 Uncaught (in promise) Error: Minified React error #31; visit https://reactjs.org/docs/error-decoder.html?invariant=31&args[]=object%20with%20keys%20%7B_id%2C%20age%2C%20species%2C%20description%2C%20path%2C%20__v%7D for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
at ka (react-dom.production.min.js:140)
at d (react-dom.production.min.js:144)
at m (react-dom.production.min.js:146)
at react-dom.production.min.js:150
at Do (react-dom.production.min.js:176)
at Hu (react-dom.production.min.js:271)
at Pi (react-dom.production.min.js:250)
at xi (react-dom.production.min.js:250)
at _i (react-dom.production.min.js:250)
at vi (react-dom.production.min.js:243)
As per your error, it seems the {this.state.data} is an object and have only 1 property called id so you might change it to {this.state.data.id} instead
If you go to the Minified error page from your console log, you can see that the error is from trying to render an object, which won't work with <p>.
It depends on what you want to render the data as, to see the data as a string, change your line to <p>{JSON.stringify(this.state.data)</p> and it will render the object as a string.

NodeJS (Express) - app.delete route not working

Working on understanding CRUD basics with setting up simple routes from my HTML5 doc to Postgres database. My GET and POST buttons are working but my DELETE is not deleting from my database. I realize the routes all look very similar (and tried renaming them to see if it would hit the callback function that is linked to the database, but it didn't work). Can anyone tell me why my HTML5 form is not working with my route to reach the database for DELETE? Thanks!
I will only include the code I'm referring to as I have all the other code working well. Starting with showing the crappy HTML first, then the index.js with the routes, and then the queries.js with the database queries. ( ////// seperate the documents where the code is pulled :) )
<h1>Let's DELETE ONE Human</h1>
<form action="/users/:id" method="delete">
ID:<input type="number" name="id">
<input type="submit" name="">
</form>
/////////////////////////////////////////////////////////////////
app.get('/', (request, response) => {
response.sendFile(path.join(__dirname + '/html/homepage.html'))
}, db.getUsers)
app.get('/newHuman.html', (request, response) => {
response.sendFile(path.join(__dirname + '/html/newHuman.html'))
})
app.get('/users', db.getUsers)
app.get('/users/:id', db.getUserById)
app.post('/users', db.createUser)
app.put('/users/:id', db.updateUser)
app.delete('/users/:id', db.deleteUser)
app.listen(port, () => {
console.log(`App running on port ${port}.`)
})
////////////////////////////////////////////////////////////////////////
const deleteUser = (request, response) => {
const id = parseInt(request.query.id)
pool.query('DELETE FROM users WHERE id = $1', [id], (error, results) => {
if (error) {
throw error
}
response.status(200).send(`User deleted with ID: ${id}`)
})
}
TL;DR
How can I send to the correct route (even with just POSTing twice) from my HTML when the app.delete and app.put have the exact same route? Tried renaming route, didn't work but I know you shouldn't have to rename for it to work. Here are routes:
app.put('/users/:id', db.updateUser)
app.delete('/users/:id', db.deleteUser)
HTML form method only supports GET and POST method.
Either you have to use GET or POST or you can use ajax or some library like request or axios to make the DELETE request.
For example, if you use axios, try the following code.
Ignore importing jQuery and axios, if you already imported them.
<!-- import jQuery -->
<script
src="https://code.jquery.com/jquery-3.3.1.js"
integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60="
crossorigin="anonymous"></script>
<!-- import axios -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<h1>Let's DELETE ONE Human</h1>
<form id='myFormId' action="/users/:id" method="delete">
ID:<input type="number" name="id" id='deleteId'>
<input type="submit" name="">
</form>
<script>
$( document ).ready(function() {
const myForm = $('#myFormId');
myForm.submit((event) => {
event.preventDefault();
const id = $('#deleteId').val();
const url = `/users/${id}`;
axios.delete(url)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
});
});
</script>
Another easier way of doing this is using a npm module called method-override.
In your main entry point file for your server, add the following lines:
const express = require('express');
const app = express();
const methodOverride = require('method-override');
app.use(methodOverride('_method'));
In your HTML form, you can now use PUT or DELETE requests easily:
For example:
<h1>Let's DELETE ONE Human</h1>
<form id='myFormId' action="/users/:id?_method=DELETE" method="delete">
ID:<input type="number" name="id" id='deleteId'>
<input type="submit" name="">
</form>
Notice the action attribute of the form, all you have to do now is add that simple line and you are done!

Resources