Nodejs - API and application on the same server issue - javascript

I have a server, which holds my API and my application.
/ - returns my application
/api - every request starts with that pre word api, e.g. /api/users and so on.
My issue: When I enter my app not by directly example.com, but e.g. example.com/users or even some random word, like example.com/stackOverflow it returns API response!! My app does not even start, the response is like Cannot GET /stackOverflow
I guess it's because it's on one, single server... Is there a way to fix it somehow in node?
var app = expres();
app.get("/api/users", function (req, res) {
getUsers(function (err, users) {
if (err) {
return res.status(500).send('Error');
}
return res.json(users);
});
});
app.get("/api/user/:_id", function (req, res) {
getUserById(req.params._id, function (err, user) {
if (err) {
return res.status(500).send('Error');
}
return res.json(user);
});
});

You could create different subdomains instead of using the same domain just changing URI.
Then with reverse proxy configuration on nginx/apache you could redirect the requests to different server ports. That way you could have your application running on port 80 and your NodeJS API on port 3000 let's say.
Example:
APP.domain.com redirects to localhost:80
API.domain.com redirects to localhost:3000
I hope I made myself clear.
You could also do that with the structure you already have not needing to run them on different ports.
Here is some documentation regarding how to configure that on nginx:
https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/

Related

Get 404 error When trying to fetch data from Node.js in React.js While server is running

I am not familiar with back-end and it is the first time to try it out.
Techset is React.js, Node.js, express.js, and mysql.
I want to request result of a query but 404 error occurs.
This is the code.
In React.js
const resident_name = 'sss';
let rates = [];
async function getData(){
const res = await fetch('http://localhost:3001', {
method: 'POST',
mode: 'cors',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({name: {resident_name}}),
});
rates = await res.result;
};
getData();
I want to execute the function as soon as the page that has it shows up.
In Node.js
app.post("/", (req,res)=>{
res.set('Content-Type', 'application/json');
console.log("i got "+req.body.name);
db.query(`SELECT r.*
FROM rate r INNER JOIN resident rs on r.resident_id = rs.resident_id
WHERE rs.name = ${req.body.name}`, (err,result)=>{
// result is an array that contains dictionaries.
if(err) throw err;
result.forEach(r => {
console.log(r)
});
res.send(result)
});
});
I am not sure what causes the error. Can I plz get solution if you guys know some info about it?
Thank you!
Edited: I am running both React.js on port 3000 and Node.js on port 3001. Once I type node index.js, I can see Server is listening to 3001 message on server-side console with this code below.
app.listen(PORT, () => {
console.log(`Server listening on ${PORT}`);
});
Result on the console:
Server listening on 3001
I'm working on making sure the server is running in the way the first answer suggested.
first, make sure your server's API endpoint is correct and running.
For that, you can use Vs code extension like Thunder client by
passing your port.
And check for any network issue using ping command or firewall
block.
Also, check your header request or use tool like curl. In
react.js while request from server, CORS also block to make request
on the browser (as i have face same issue on past). So, make sure you
check the server logs.
404 error occurs only if there is nothing is listening in the mentioned address:port.
Make sure you are requesting to the correct url with correct port number in which your express app is running.
Express app defaults to port 3000

Protect API routes in Node.js

I have some routes in my Node.js API sending data from a MongoDB database to an Angular 4 frontend.
Example:
Node.js route:
router.get('/api/articles', (req, res) => {
Article.find({}, (err, articles) => {
if(err) return res.status(500).send("Something went wrong");
res.status(200).send(articles);
});
});
Angular 4 service function:
getArticles() {
return this.http.get('http://localhost:3000/api/articles')
.map(res => res.json()).subscribe(res => this.articles = res);
}
The question is, how do I protect my Node.js API routes from browser access? When I go to http://localhost:3000/api/articles I can see all my articles in json format.
This is not a security measure, just a way to filter the request. For security use other mechanisms like JWT or similar.
If the angular app is controlled by you then send a special header like X-Requested-With:XMLHttpRequest (chrome sends it by default for AJAX calls) and before responding check for the presence of this header.
If you are really particular about exposing the endpoint to a special case use a unique header may be X-Request-App: MyNgApp and filter for it.
You can't really unless you are willing to implement some sort of authentication — i.e your angular user will need to sign into the api.
You can make it less convenient. For example, simply switching your route to accept POST request instead of GET requests will stop browsers from seeing it easily. It will still be visible in dev tool or curl.
Alternatively you can set a header with your angular request that you look for in your express handler, but that seems like a lot of work for only the appearance of security.
Best method is to implement an authentication token system. You can start with a static token(Later you can implement dynamic token with authorisation).
Token is just a string to ensure the request is authenticated.
Node.js route:
router.get('/api/articles', (req, res) => {
let token = url.parse(req.url,true).query.token; //Parse GET param from URL
if("mytoken" == token){ // Validate Token
Article.find({}, (err, articles) => {
if(err) return res.status(500).send("Something went wrong");
res.status(200).send(articles);
});
}else {
res.status(401).send("Error:Invalid Token"); //Send Error message
}
});
Angular 4 service function:
getArticles() {
return this.http.get('http://localhost:3000/api/articles?token=mytoken') // Add token when making call
.map(res => res.json()).subscribe(res => this.articles = res);
}
With Express, you can use route handlers to allow or deny access to your endpoints. This method is used by Passport authentication middleware (which you can use for this, by the way).
function isAccessGranted (req, res, next) {
// Here your authorization logic (jwt, OAuth, custom connection logic...)
if (!isGranted) return res.status(401).end()
next()
}
router.get('/api/articles', isAccessGranted, (req, res) => {
//...
})
Or make it more generic for all your routes:
app.use('*', isAccessGranted)

Need to acces Client IP not the server IP for a weather API

I want to create a server-side app using node.js with express. At the moment I am able to get the json document with the location and temperature but after I used pykite to get it online the application would only get the server location to the one who is accessing it. Here is the code in its current form:
var express = require('express');
var app = express();
var request = require('request');
app.get('/api/weather/current_temperature', function (req, res) {
request('http://api.wunderground.com/api/ebb3e0cf5059cce8/conditions/q/autoip.json', function(error, response, body){
if (!error && response.statusCode==200){
res.setHeader('Content-Type', 'aplication/json');
var result = JSON.parse(body);
res.json({ "location": result.current_observation.observation_location.city,"temperature": result.current_observation.temp_c });
}else{
console.log(error, response.statusCode, body);
}
res.end("")
});
});
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('The app is up.');
});
I admit I'm new to this but I wanna learn. I would like some help implementing it in this code, if it's possible, a way to access the user's location and determine the weather at his/her location.
Thank you in advance.
P.S.: I would also like some links into some documentation, don't wanna skip anything.
The URL you are linking to http://api.wunderground.com/api/YOUR_KEY/conditions/q/autoip.json (which doesn't work because you didn't provide your key) has autoip in the name.
That implies that it will return a result from whatever IP address the request came from.
So you have two options:
Don't make the request from NodeJS. Make the request from the browser. (This probably isn't advisable given the use of an API key and might run into Same Origin Policy issues).
Find a different API (possibly one from the same site) that lets you specify what IP address or location to use.

Hide an API key (in an environment variable perhaps?) when using Angular

I'm running a small Angular application with a Node/Express backend.
In one of my Angular factories (i.e. on the client side) I make a $http request to Github to return user info. However, a Github-generated key (which is meant to be kept secret) is required to do this.
I know I can't use process.env.XYZ on the client side. I'm wondering how I could keep this api key a secret? Do I have to make the request on the back end instead? If so, how do I transfer the returned Github data to the front end?
Sorry if this seems simplistic but I am a relative novice, so any clear responses with code examples would be much appreciated. Thank you
Unfortunately you have to proxy the request on your backend to keep the key secret. (I am assuming that you need some user data that is unavailable via an unauthenticated request like https://api.github.com/users/rsp?callback=foo because otherwise you wouldn't need to use API keys in the first place - but you didn't say specifically what you need to do so it is just my guess).
What you can do is something like this: In your backend you can add a new route for your frontend just for getting the info. It can do whatever you need - using or not any secret API keys, verify the request, process the response before returning to your client etc.
Example:
var app = require('express')();
app.get('/github-user/:user', function (req, res) {
getUser(req.params.user, function (err, data) {
if (err) res.json({error: "Some error"});
else res.json(data);
});
});
function getUser(user, callback) {
// a stub function that should do something more
if (!user) callback("Error");
else callback(null, {user:user, name:"The user "+user});
}
app.listen(3000, function () {
console.log('Listening on port 3000');
});
In this example you can get the user info at:
http://localhost:3000/github-user/abc
The function getUser should make an actual request to GitHub and before you call it you can change if that is really your frontend that is making the request e.g. by cheching the "Referer" header or other things, validate the input etc.
Now, if you only need a public info then you may be able to use a public JSON-P API like this - an example using jQuery to make things simple:
var user = prompt("User name:");
var req = $.getJSON('https://api.github.com/users/'+user);
req.then(function (data) {
console.log(data);
});
See DEMO

Adobe Brackets Live Preview can't reach Node.js server

I'm having trouble running a Node.js server with Adobe Brackets. Once in live preview (the URL is http://localhost:SOMERANDOMPORT/path/to/file.html), I start the server. If I type http://localhost:3000/test straight into another tab, it displays the correct JSON.
I then added an event function to an element in file.html that upon clicking it makes an AJAX request to my server and uses the response to change some of its inner HTML. However, clicking the element in live preview fails, and the error callback gets called instead.
How can I fix this? I suspect it has to do with the fact that the AJAX request sends to http://localhost:SOMERANDOMPORT/test rather than http://localhost:3000/test, but I can't seem to find a solution.
Everything runs locally. Below is my server:
var express = require('express');
var mysql = require('mysql');
var app = express();
app.get('/test', function(req, res){
var connection = mysql.createConnection(...);
connection.query("SELECT author FROM posts", function(err, results) {
if (err) {
console.log(err);
console.log('Error on retrieving data.');
res.send(err);
return;
}
console.log(results[results.length - 1]);
res.send(results[results.length - 1]); // return last row
});
connection.end();
});
app.listen(3000);
console.log('Listening on port ' + port);
And the event function:
function getAuthor() {
$.ajax({
type: 'GET',
url: '/test',,
success: function(data, status) {
$('.author').text('Authored by ' + data.author);
},
error: function(jqXHR, status, error) { // this always get called
$('.author').text('Something went wrong.');
}
});
}
I appreciate any help.
The simplest fix is to point Live Preview directly at your own Node server, letting it serve up the pages itself from the correct port number (rather than serving the pages from Brackets's built-in server that's on a different port). See instructions on the Brackets wiki under "Using your own backend."
The downside is that HTML live updating is disabled - but you'll still get CSS live updating, and Brackets falls back on a simpler "live reload" on save for HTML content.
To keep live HTML updating enabled you'd need to work around the port number difference somehow. You could hardcode a localhost:3000 base URL for testing, but you'll run same-origin problems due to the port numbers not matching. Working around that would be pretty involved (set up CORS on your Node server, etc.).
One other option for keeping the full Live Preview experience is to shim all your $.ajax() calls so they return hardcoded dummy data without hitting the server. If you're already doing some mocking for unit tests, you might be able to reuse that existing infrastructure for this.

Categories