i seriously need help with this:
i built my first node js server, here:
var express = require('express'),
app = express(),
https = require('https');
port = process.env.PORT || 443,
mongoose = require('mongoose'),
Task = require('./api/models/myCarModel'),
bodyParser = require('body-parser');
fs = require("fs");
const options = {
key: fs.readFileSync('./static/HttpsFile/privatekey.pem').toString(),
cert: fs.readFileSync('./static/HttpsFile/certificate.pem').toString(),
};
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/Tododb');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
var routes = require('./api/routes/myCarRoutes'); //importing route
routes(app); //register the route
https.createServer(options, app).listen(port);
console.log(options.key)
console.log('RESTful API server started on: ' + port);
and here's my simple react-native app home:
import React, { useState } from 'react';
import { useEffect } from 'react';
import { Button, StyleSheet, Text, TextInput, View } from 'react-native';
const Home = () => {
const [text, setText] = useState('');
useEffect(()=> {
debugger
fetch('https://192.168.1.8:443/cars', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}})
//axios.get("https://192.168.1.8:3000/cars")
.then((res:any) => {
debugger;
}, (err:any) => {
console.log(err)
debugger;
})
}, [])
return(...etc...)
now:
when I first used postman to test the API I got an error regarding the use of self-signed certificates ( which I'm using through OpneSSL ). I kindly asked Postman to accept the idea, and everything went on fine.
This is not the case with react-native:
I'm using expo, testing my app on my android phone and when I debug the API call I get the following error in the error-then: "TypeError: Network request failed".
I don't want to dirty solve the problem by making Android accept HTTP protocol.
Ty everyone
So you are running the app on your phone? Your phone does not have access to the server you are running on your computer. 'https://192.168.1.8:443/cars' is only accessible if you try to connect to it on the same device (it could be a simulator running on your computer). But still this may not work if you use an internal device. In that case you can access it trough http://10.0.2.2:433 (this worked for me).
Bonus point: Https does not work on local computer. Since it cant verify the request. When testing localy use "http".
Related
I run a dotnet 5 web api application which I want to call from my express web app. Think of it as a backend running on net5.0 and a front-end with a server running on nodejs and express.
My dotnet app uses a localhost certificate it generated and runs on https://localhost:5003.
My express app hosts a svelte front-end and runs on http://localhost:5005 (remark that the express app is not running on https). When I call my server I do something like:
const express = require('express');
const app = express();
const http = require("http");
const port = process.env.PORT || 5005;
const cors = require('cors');
const axios = require('axios');
const settings = require("./../settings");
const httpAgent = new http.Agent({
// something needs to happen here!
});
axios.default.options = httpAgent;
// parse application/json
app.use(express.json());
app.use(cors());
app.post("/api/login", async (req, res) => {
console.log(req.body);
const { name, password } = req.body;
var loginResponse = await axios.post(settings.server.url + "api/login", {
userName: name,
password: password
});
console.log(loginResponse);
res.send(loginResponse);
});
The error I get is:
(node:15896) UnhandledPromiseRejectionWarning: Error: self signed certificate
at TLSSocket.onConnectSecure (_tls_wrap.js:1507:34)
at TLSSocket.emit (events.js:376:20)
at TLSSocket._finishInit (_tls_wrap.js:932:8)
at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:706:12)
I've found this question and some other ones, not worth mentioning atm, but I keep running up against blocks and I really do not want to "just ignore the certificates".
My localhost certificate is installed and I can find it in de certificates of my machine. I can't, however, physically find it on my machine searching for "localhost.crt".
My question is: How can I set up my httpAgent, so that when I use axios to call my back-end I call it using its https endpoint and not get an error?
So basically I've got a MEVN-stack app hosted on Heroku and I've been struggling for days trying to understand why my Axios posts won't work on Android but are O.K on P.C. The only time I got it to work was when I hardcoded the IP while debugging locally but since I can't have a static I.P in Heroku that won't work. I've even tried to dynamically change the BaseURL to the I.P-address in production without any luck. Is there anything I'm missing completely? I've read about "usesCleartextTraffic" for android and some issues regarding both headers and certificates. I'm deep down the rabbit hole and nothing seems to work.
I'll provide snippets through the stack for the request;
From the view:
import GuestsService from "../services/GuestsService.js";
async registerGuestMail() {
await GuestsService.registerGuestMail({ email: this.email })
.then(
response => (
(this.apiResponse =
"Request O.K")
)
)
.catch(
err => (
console.log(err),
(this.apiResponse = "Request failed")
)
);
From GuestService.js:
import axios from 'axios'
export default {
registerGuestMail(params) {
return axios.post('registerGuestMail', params)
}
}
In app.js - express:
const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const morgan = require('morgan')
const mongoose = require('mongoose');
const app = express()
app.use(morgan('combined'))
app.use(bodyParser.json())
app.use(cors())
mongoose.connect(" - insert connection string here - ")
app.post('/registerGuestMail', userController.signupPost);
app.listen(process.env.PORT || 8081, process.env.IP)
As stated before, it works, deployed in Heroku, but only for P.C (if I set the baseURL: to 'http://localhost:8081')
I'm just learning Express/React and I'm trying to get set up with routes and basic database connections. I suspect I'm missing something very simple. I've tried to boil it down to the following.
Backend
server.js:
require('dotenv').config({path: '../.env'});
const mysql = require('mysql');
const express = require('express');
const bodyParser = require('body-parser');
const port = process.env.PORT || 5000;
const app = express();
const router = express.Router();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
const users = require('./routes/api/users');
app.use('/api/users', users);
const events = require('./routes/api/events');
app.use('/api/events', events);
const db = mysql.createConnection({
host: process.env.DB_HOST,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD
});
db.connect(function(err) {
if (err) throw err;
console.log('MySQL Connected!');
})
app.listen(port, () => console.log(`Listening on port ${port}`));
/routes/api/events.js:
const express = require('express');
const router = express.Router();
// GET api/events
router.get('/', (req, res) => {
res.send({id: "1", name: "hi"});
});
module.exports = router;
Frontend
App.js:
import React, { Component } from 'react';
import EventList from './components/EventList';
class App extends Component {
render() {
return (
<div className="App">
<EventList/>
</div>)
}
}
export default App;
/components/EventList.js:
import React, { Component } from 'react';
import axios from 'axios';
class EventList extends Component {
constructor() {
super();
this.state = {
events: []
}
}
componentDidMount() {
axios.get('/api/events')
.then(events => {
this.setState({events: events.data})
})
.catch(err => console.log(err))
}
render() {
var events = this.state.events;
return (
<div>
<p>Events:</p>
<ul>
{ events.map(({ id, name }) => (
<li>{name}</li>
))}
</ul>
</div>
)
}
}
export default EventList;
The error I get is http://localhost:3000/api/events 500 (Internal Server Error). What am I missing? I have truly scoured all of the docs I can find, but I'm not quite getting it.
Edit
I haven't changed anything, but now I'm getting a 404 (Not Found) instead. I had been getting a 500 for awhile, so it wasn't a momentary fluke. I'm not sure what could have changed.
Update
It may help to know that the .env variables I'm pointing to are actually for a remote MySQL database (i.e., DB_HOST != localhost, but a remote URL). Eventually, I'd like to connect the GET call on the events route to that db, but since it doesn't work with what I have here I figured my first issue to solve was upstream. As noted in comments, the PORT var I'm loading in is 3306. When I start the server, it says it's listening on 3306 as expected.
I think you are running your server on port 5000 and front on port 3000. if you request events with http://localhost:5000/api/events instead of /api/events, you would get 200 status code with your json data.
// as is
axios.get('/api/events')
// to be
axios.get('http://localhost:5000/api/events')
You could try typing componentDidMount function like this.
componentDidMount = async () =>{
//like this
}
In addition, I would recommend making the GET ALL its own function so you could just invoke in the componentDidMount function. whenever you run another CRUD action it will automatically update your events for you and instead of setting state every time you can invoke the this.getEvents() again to update it that way.
componentDidMount = async () =>{
this.getEvents()
}
Also you need to add this package (npm install cors) its so you can connect your api to your frontend.
Import it like this in your server.js file
const cors = require('cors')
app.use(cors())
You should add a proxy inside package.json in frontend like this.
"proxy": "http://localhost:5000",
General information about my setup
Currently I am building a web application using react and a nodejs API that is providing the data for this web application. Both apps are hosted on heroku.com and run independently from each other. I have bought a custom domain from a different hosting provider and used the heroku custom domain option to point the DNS to my website.
Technical details about my setup
NodeJS server: Express
NodeJS version: v10.15.0
React version: v16.2.0
Custom domain: www.tabbs.nl
Heroku domain: tabbs-web-app.herokuapp.com
The issue I am experiencing
I have been digging into a lot of documentation and tutorials in order to setup SSL for react / NodeJS but couldn't find a decent tutorial about how to set SSL / security for my setup.
Tutorials I already have read:
Node + Express + Lets Encrypt
How to use SSL/TLS with nodejs
Stack overflow posts and probably a whole lot more I am forgetting right now.
What do I want to achieve?
The goal I would like to achieve is setting up a secure connection between React web application (frontend) and NodeJS API (backend) so that all data between those is encrypted and safe. Also I want my custom domain (bought by a different hosting provider than Heroku) to be secure and forced using https.
For any questions or additional information please do not hesitate to ask!
Have you tried using the https module in node?
You can do something like this:
var express = require('express');
var https = require('https');
var http = require('http');
var app = express();
http.createServer(app).listen(80);
https.createServer(options, app).listen(443);
The app returned by express() is in fact a JavaScript Function, designed to be passed to Node’s HTTP servers as a callback to handle requests. This makes it easy to provide both HTTP and HTTPS versions of your app with the same code base, as the app does not inherit from these (it is simply a callback.
If you are using create react app, open your terminal and type “npm run build”. This creates a build folder with all of your static files.
Now go back to your node backend service and add the following:
var express = require('express');
var path = require('path');
var https = require('https');
var http = require('http');
var app = express();
const options = {
key: fs.readFileSync("/srv/www/keys/my-site-key.pem"),
cert: fs.readFileSync("/srv/www/keys/chain.pem")
};
http.createServer(app).listen(80);
https.createServer(options, app).listen(443);
app.use(express.static(path.join(__dirname, 'build')));
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
If you’re using react router to handle routing for you web app then you would amend the GET request as such:
var express = require('express');
const path = require('path');
var https = require('https');
var http = require('http');
var app = express();
const options = {
key: fs.readFileSync("/srv/www/keys/my-site-key.pem"),
cert: fs.readFileSync("/srv/www/keys/chain.pem")
};
http.createServer(app).listen(80);
https.createServer(options, app).listen(443);
app.use(express.static(path.join(__dirname, 'build')));
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
This ain't a complex issue, do not worry about ssl, just create your own certificate for Node.JS/Express and that's enough.
and, React has a built-in way of doing api calls,
add this line to package.json of your React installation,
"proxy": "http://localhost:8000/"
and just call the api service like this,
//Generic API Call
callApi = async () => {
const response = await fetch('/api/hello');
const body = await response.json();
if (response.status !== 200) throw Error(body.message);
return body;
};
// A Submit handler to proxy
handleSubmit = async e => {
e.preventDefault();
const response = await fetch('/api/myrequest', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ post: this.state.post }),
});
const body = await response.text();
this.setState({ responseToPost: body });
};
it all works.
So I'm running it on port 8080. Port forwarding has been set up and it is working.
Every time I type in my no-ip domain, I get the response on the screen but when I'm making a request from my website, it logs the request on the Raspberry, yet, there is no response visible in the Chrome developer tools.
I also get this error message: POST "name of the api" net::ERR_EMPTY_RESPONSE
What could cause that? My routes worked perfectly when I was running my api locally.
module.exports = function(app) {
app.get('/', requireAuth, function(req, res) {
res.send({ message: 'OMG, You made it, you deserve a drink!' });
});
That's how my react app looks like:
const ROOT_URL = *"name of the api"/*;
.
.
.
export function fetchMessage() {
return function(dispatch) {
axios.get(ROOT_URL, {
headers: { authorization: localStorage.getItem('token') }
})
.then(response => {
dispatch({
type: FETCH_MESSAGE,
payload: response.data.message
});
});
}
};
Is it a typical problem of the Node.js, Express, React or maybe it's on the Raspi? Thanks a lot!
Possibly a CORS issue, since the problem only happens when trying to consume the API from the browser. A possible solution is to use the cors package in your Express application:
const express = require('express');
const cors = require('cors');
...
const app = express();
app.use(cors());
...
NOTE: this enables all CORS requests.