How to set React app and API on same port? - javascript

I've got a React app that via an API pulls data from a separate database.
When I run it locally, the app is one port and the API is on another port.
Since when I make AJAX calls in the app to the API, I need to include the URL where the API can connect.
It works if I hardcode the separate port (e.g., the app is on http://localhost:3000 and the API on http://localhost:3100, making the AJAX url call to the API http://localhost:3100/api/trusts).
However, since the app and API are on different ports, I can't make the AJAX url a relative path because it erroneously sends the AJAX call to http://localhost:3000/api/trusts and not http://localhost:3100/api/trusts.
How do I get them to run on the same port?
Thanks!
Here's my server.js:
var express = require('express');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var path = require('path');
var app = express();
var router = express.Router();
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
//set our port to either a predetermined port number if you have set it up, or 3001
var port = process.env.PORT || 5656;
//db config
var mongoDB = 'mongodb://XXX:XXX!#XXX.mlab.com:XXX/XXX';
mongoose.connect(mongoDB);
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));
//body parsing
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// allow cross-browser
app.use(function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
next();
});
// handling static assets
app.use(express.static(path.join(__dirname, 'build')));
// api handling
var TrustsSchema = new Schema({
id: String,
name: String
});
var Trust = mongoose.model('Trust', TrustsSchema);
const trustRouter = express.Router();
trustRouter
.get('/', (req,res) => {
Trust.find(function(err, trusts) {
if (err) {
res.send(err);
}
res.json(trusts)
});
});
app.use('/api/trusts', trustRouter);
//now we can set the route path & initialize the API
router.get('/', function(req, res) {
res.json({ message: 'API Initialized!'});
});
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.listen(port, function() {
console.log(`api running on port ${port}`);
});
Below is the AJAX call I'm trying to make that doesn't work because the relative path is appended to the app's port (i.e., http://localhost:3000/) and not the API's port (i.e., http://localhost:3100/):
axios.get("/api/trusts")
.then(res => {
this.setState({trusts: res.data});
})
.catch(console.error);

To tell the development server to proxy any unknown requests to your API server in development, add a proxy field to your package.json, for example:
"proxy": "http://localhost:4000",
This way, when you fetch('/api/todos') in development, the development server will recognize that it’s not a static asset, and will proxy your request to http://localhost:4000/api/todos as a fallback. The development server will only attempt to send requests without text/html in its Accept header to the proxy.
"Keep in mind that proxy only has effect in development (with npm start), and it is up to you to ensure that URLs like /api/todos point to the right thing in production."
Note: this feature is available with react-scripts#0.2.3 and higher.
More details here: https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#proxying-api-requests-in-development

Related

How to deploy express.js server to Netlify

I am attempting to deploy a Vue.js, Node, Express, MongoDB (MEVN) stack application to Netlify. I successfully deployed the front end of the application to Netlify, and am now attempting to deploy the express server, based on the following serverless-http example: https://github.com/neverendingqs/netlify-express/blob/master/express/server.js
I configured my server to include the serverless-http package:
server.js
const express = require('express');
const app = express();
const serverless = require('serverless-http');
const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');
const config = require('./DB.js');
const postRoute = require('./routes');
mongoose.connect(config.DB, { useNewUrlParser: true, useUnifiedTopology: true }).then(
() => { console.log('Database is connected') },
err => { console.log('Can not connect to the database'+ err)}
);
app.use(cors());
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use('/messages', postRoute);
app.use('/.netlify/functions/server', router); // path must route to lambda
app.use('/', (req, res) => res.sendFile(path.join(__dirname, '../public/index.html')));
module.exports = app;
module.exports.handler = serverless(app);
routes.js
const express = require('express');
const postRoutes = express.Router();
// Require Post model in our routes module
let Post = require('./post.model');
// Defined store route
postRoutes.route('/add').post(function (req, res) {
let post = new Post(req.body);
post.save()
.then(() => {
res.status(200).json({'business': 'business in added successfully'});
})
.catch(() => {
res.status(400).send("unable to save to database");
});
});
// Defined get data(index or listing) route
postRoutes.route('/').get(function (req, res) {
Post.find(function(err, posts){
if(err){
res.json(err);
}
else {
res.json(posts);
}
});
});
module.exports = postRoutes;
I then re-deployed my application to Netlify, but the server does not seem to run in Netlify. This server is in a folder in project root of my vue.js app. Should I instead run the server as a separate site in Netlify? If not, what should I do in order to get the server to run when deployed in Netlify?
It's been a while, but here goes.
Netlify hosting is for the Jamstack, as they say, i.e. only static files, no processing on the server. The idea is to make use of other mechanisms to get your data dynamically, such as APIs hosted elsewhere, which you query straight from the browser, or when you build your site.
Most likely you actually had to deploy your express.js app as a Netlify Function, instead. Check Netlify's blog post on running express apps on their functions.
I had a similar issue, just that my server wouldn't connect to the routes locally, the major difference between my code and yours was that I had to do
const router = express.Router()
and then switched app.use() with router.use()
Like I said, that's for when the localhost says "cannot GET /* a defined path */"
P.S. As a side note, you don't need explicit bodyParser in recent express, express.json() works fine instead.

express-subdomain handling any subdomain

I'm trying to use https://github.com/bmullan91/express-subdomain for subdomain routing in express. The following are the contents of my main.js and src/routes/site files.
const express = require('express');
const bodyParser = require('body-parser');
const subdomain = require('express-subdomain');
const siteRouter = require('./src/routes/site');
const app = express()
app.use(express.json() );
app.use(express.urlencoded());
app.use(express.static('public'));
app.use(subdomain('*.www', siteRouter));
app.get('/', function(req, res) {
res.send('Homepage');
});
const server = app.listen(80,'x3.loc', function () {
var host = server.address().address;
var port = server.address().port;
console.log('X3 listening at http://%s:%s', host, port);
});
const express = require('express');
let router = express.Router();
router.get('/', function(req, res) {
res.send('Welcome to site');
});
module.exports = router;
This way of doing app.use(subdomain('*.www', siteRouter)); has been suggested in https://github.com/bmullan91/express-subdomain/issues/33 but does not work.
I have also tried just * as the subdomain aswell, but that caused the homepage w/o a subdomain, to get treated like one aswell. How could I get this to work?
We know that / matches any base path regardless of subdomain. So I made your homepage middleware "subdomain-aware" like so:
app.get('/', function(req, res,next) {
/* If there are any subdomains, skip to next handler, since request is not for the main home page */
if (req.subdomains.length > 0) {
return next();
}
res.send('Homepage');
});
Then I placed the middleware for subdomains below the homepage middleware like so:
app.use(subdomain('*', siteRouter));
This makes homepage middleware to serve requests for x3.loc and the subdomain middleware to serve requests for any subdomain like api.x3.loc or api.v1.x3.loc.
But in my opinion, the real fix should be done in the module. I think it should be changed so that either the case where req.subdomains being empty is handled, or * is matched against an actual string, instead of skipping the iteration.
I am surprised that the fix suggested in bug 33 worked as-is for the reporter. In my testing, it works the opposite way i.e. www.example.com goes to subdomain middleware while stat1.example.com goes to the homepage middleware. Perhaps the reporter saw this and swapped the middleware bodies.

Why am I getting "name undefined" when trying to post using Express and Body Parser

I'm attempting to build a MEAN app and trying to test POSTing with POSTMAN. When I do, I keep getting the dreaded "TypeError: Cannot read property 'name' of undefined". If I type in a simple string, the POST goes through fine. But when I use "req.body.name" I get the error. I've looked in every place and I'm not seeing my mistake. I even followed the suggestions on this thread with no luck. Any help or suggestions would be greatly appreciated.
Here's the code I am currently working with in my server.js file:
const express = require('express');
var bodyParser = require('body-parser');
var Bear = require('./models/bear')
var path = require('path');
var mongoose = require('mongoose');
var router = express.Router();
var app = express();
var staticAssets = __dirname + '/public';
app.use(express.static(staticAssets));
app.use('/api', router)
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
// Routes for my API
//===================================
// middleware to use for all requests
router.use(function(req,res,next){
// logging happens here
console.log('Something will happen.');
next(); // Head to the next router...don't stop here
});
// Test router to make sure everything is working (accessed at GET http://localhost:3000/api)
router.get('/', function(req, res){
res.json({message: 'hooray! welcome to our api!'})
})
//More routes will happen here with routes that end in "/bears"
router.route('/bears')
//Create a bear (accessed at POST http://localhost:3000/api/bears)
.post(function(req,res){
var bear = new Bear(); // Create a new instance of the bear model
console.log(req);
bear.name = req.body.name; // set the bears name (comes from the request)
//res.send(200, req.body);
bear.save(function(err){
if (err)
res.send(err);
res.json({message: 'Bear Created!!'});
});
});
//======================================
//var Products = require('./products.model.js');
var Product = require('./models/product.model');
var db = 'mongodb://localhost/27017';
mongoose.connect(db);
var server = app.listen(3000);
console.log("App is listening on port 3000");
Thanks.
Also, the url I'm trying to use inside of POSTMAN is http://localhost:3000/api/bears
Express processes requests Top-Down, meaning if you require a piece of functionality to be applied to all routes via middleware, than that middleware needs to be added to your app before any routes that require it. This is usually the case for middleware such as body-parser.
When using Router Middleware, you don't typically construct the router in the same file as the actual Express app that will use it as middleware. Instead, place it in a separate file and/or directory for organization purposes, this is considered a best practice.
Express Apps can be structured like so
/lib
/models
bear.js
product.js
/node_modules
/public
/css
/routes
api.js
package.json
server.js
The routes directory is where you would place any applicable Router Middleware files such as your api router. server.js is your main Express App and public is where your static assets are stored. lib is directory that contains any business logic files and models.
The actual Express app and Router files should look something like this
server.js
'use strict';
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const apiRouter = require('./routes/api');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, public)));
app.use(/api, apiRouter);
app.listen(port, () => {
console.log(`Listening on port ${port});
});
module.exports = app;
routes/api.js
'use strict';
const router = require('express').Router();
const Bear = require('./lib/models/bear');
router.use((req, res, next) => {
// logging happens here
console.log('Something will happen.');
next(); // Head to the next router...don't stop here
});
router.get('/', (req, res) => {
return res.json({ message: 'hooray! welcome to our api!'})
});
router.route('/bears')
//Create a bear (accessed at POST http://localhost:3000/api/bears)
.post((req, res) => {
var bear = new Bear(); // Create a new instance of the bear model
console.log(req);
bear.name = req.body.name; // set the bears name (comes from the request)
//res.send(200, req.body);
bear.save((err) => {
if (err)
return res.send(err);
return res.json({message: 'Bear Created!!'});
});
});
module.exports = router;
To note, you could break up your API even further to increase the amount of decoupling. An example of this would be to move the /api/bear route to its own router middleware and into its own route file. Then simply add it to your routes/api.js router as a middleware like you would in server.js. If your app is going to have a decent sized API, then this would be the best approach because it would allow the most flexibility when it comes to applying middleware to only certain routes and would make maintaining the source much easier.

Cannot access req.session variables in Express/NodeJS

I've seen many variations of this question, but none seemed to solve my issue. I'm trying to set up a Node.js server using Express. Here is my server configuration:
var express = require('express'),
RedisStore = require('connect-redis')(express);
var app = express();
app.use(express.urlencoded());
app.use(express.json());
app.use(express.cookieParser());
app.use(express.session({
store: new RedisStore(),
secret: APP_SECRET
}));
// Initialize redis connection
var client = redis.createClient();
client.on('connect', function() {
console.log('Connected to Redis server')
})
client.on('error', function (err) {
console.log('Error ' + err);
});
// Enable cross-origin resource sharing
app.all('*', function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'X-Requested-With');
next();
});
var api = require('./controllers/api.js');
app.post('/login', api.login);
app.get('/auth', api.auth);
app.listen(3000);
And here are some simple routes:
exports.login = function(req, res) {
var user = new User(req.username, req.password);
req.session.user = user;
console.log(req.session.user); //works
res.json({user:user});
}
exports.auth = function(req, res) {
console.log(req.session.user); //doesn't work
res.json(req.session.user);
}
So in my login route, I can print the session variable as expected. But if I visit the auth route after visiting the login route, the session variable is undefined. How can I get Express sessions to work?
In a typical web application, the credentials used to authenticate a user will only be transmitted during the login request. If authentication succeeds, a session will be established and maintained via a cookie set in the user's browser.
Each subsequent request will not contain credentials or all user data, but rather the unique cookie that identifies the session. In order to support login sessions, You have to serialize and deserialize user instances to and from the session in every request.
In your case, you have assigned req.session.user = user; only in /login request. It will not be available for further requests(/auth).
You have to get user information in /auth request too by session id. (Or) Better you can use passport for authentication.
I think maybe your redis client is not connecting well, try something like this and be sure to start the redis service
sudo service redis-server start
or the way you are calling The RedisStore variable look at the example
example:
var express = require('express');
var app = express();
var cookieParser = require('cookie-parser');
var session = require('express-session');
var RedisStore = require('connect-redis')(session);
app.set('port',process.env.PORT || 3000);
app.use(cookieParser());
app.use(session({
resave: true,
saveUninitialized: true,
store: new RedisStore({
host: 'localhost',
port: 6379
}),
secret: 'some string/hash secret'
}));
var counter=0;
app.get('/', function(request, response){
//adding some value to request.session
counter = counter+1;
request.session.color = {'anyValue': counter};
//
console.log('Session ID: ', request.sessionID);
console.log('Session: ', request.session);
response.send('some text counter: '+request.session.color['anyValue']);
});
app.listen(app.get('port'));
The currently accepted answer didn't realize that express.session already handles cookie based sessions with the req.session object. I tried a trimmed down version of yours not using redis and it worked. Looking at the connect-redis docs it looks like you need to pass a session to connect-redis. You are currently passing it express. I believe changing that will fix your problem.
P.S. I would update your node/express versions as current versions of express no longer have the built in middleware along with other improvements.
Newer versions of express:
var session = require('express-session');
var cookieParser = require('cookie-parser');
var json = require('express-json');
var bodyParser = require('body-parser')
Rather than:
express.session
express.cookieParser
express.json
express.bodyParser

Trouble using express.session with https

I have to use authentication and then create a session for this i have a HTTPS static website running on expressjs
Code :
app.js:
//created the https server
var express = require('express');
var https = require('https');
var http = require('http');
var fs = require('fs');
var mongo = require('mongodb');
var monk = require('monk');
var db = monk('localhost:27017/svgtest1');
// This line is from the Node.js HTTPS documentation.
var options = {
key: fs.readFileSync('privatekey.pem'),
cert: fs.readFileSync('certificate.pem')
};
// Create a service (the app object is just a callback).
var app = express();
app.use(express.static(__dirname + '/public'));
app.use(express.cookieParser());
app.use(express.session({cookie: { httpOnly: false , maxAge: 24*60*60*1000}, secret: '1234567890QWERT'}));
app.use(express.urlencoded());
app.use(express.json());
// middle ware to check auth
function checkAuth(req, res, next) {
if (!req.session.user_id) {
res.send('You are not authorized to view this page');
} else {
next();
}
}
app.get('/', function(req, res) {
console.log('First page called');
res.redirect('xyz.html');
res.end();
});
app.post('/login', function(req, res) {
console.log('login called');
var usrfield = req.body.usrfield;
var passfield = req.body.passfield;
// Play with the username and password
if (usrfield == 'xxx' && passfield == 'xxx') {
req.session.user_id = '123';
res.redirect('abc.html');
} else {
res.send('Bad user/pass');
}
console.log(usrfield);
console.log(passfield);
res.end();
});
// Create an HTTPS service.
https.createServer(options, app).listen(8888);
When I visits https://localhost:8888 it continuously loads the page and does not redirect to xyz.html where i have to enter the credentials to authenticate the user ?
When I comment out
app.use(express.session({cookie: { httpOnly: false , maxAge: 24*60*60*1000}, secret: '1234567890QWERT'}));
Then the page loads correctly but when i post the form to /login then it says req.session cannot be written. For this i know because i have comment out the express.session, but the strange thing is that connect.sid cookie is created. Why ?
I am confused regarding question 1 and 2.
I tried your example, and it works for me. Are you sure you're using the https protocol in the browser? By default, a browser will try to connect with HTTP protocol unless you redirect to HTTP. With your set, navigating to this URL will just spin:
http://localhost:8888
However, if you navigate to this URL:
https://localhost:8888
It will work as expected. Most servers that use HTTPS automatically redirect to an HTTPS connection if the user came in over HTTP, but you still have to have two servers: one accepting HTTP request, and the other accepting HTTPS requests. For example, you could do this:
// create an HTTPS service
https.createServer(options, app).listen(443);
// create HTTP service to redirect to HTTPS
http.createServer(express().use(function(req,res){
res.redirect('https://localhost:443' + req.url);
})).listen(8888);
Note that if you use ports below 1024 (such as 443, which is common for HTTPS), you'll probably have to have elevated privileges depending on your server set up. In OSX/Linux, you would just do sudo node app.js. Of course you don't have to run on port 443: you could have your HTTPS server run on 8887 and your HTTP redirect server run on 8888.

Categories