React server side rendering on API port instead of client - node.js

I have a react app I built with an express API to interact with mongoDB. I am now trying to set up server side rendering in my server.js file. I can't figure out why but the server rendered string is only being sent in the browser over my API port localhost:3899/api instead of localhost:3000 where my client is being served.
When I curl http://localhost:3899 I get the html string in my console. When I curl http://localhost:3000 I get the public/index.html skeleton.
I have my client and server directories next to each other at the same level.
node_modules
react-ui
server
...
server.js:
import express from 'express';
import path from 'path';
import React from 'react';
import 'ignore-styles';
import ReactDOMServer from 'react-dom/server';
import render from './render';
import App from '../react-ui/src/App';
import mongoose from 'mongoose';
import cors from 'cors';
import bodyParser from 'body-parser';
import Appointment from './model/appointments';
//and create our instances
var app = express();
var router = express.Router();
app.use(express.static(path.resolve(__dirname, '../react-ui/build/static')));
//set our port to either a predetermined port number if you have set
//it up, or 3899
var port = process.env.PORT || 3899;
//db config
mongoose.connect('mongodb://josh11:josh11#ds133162.mlab.com:33162/heroku_tl016m5d');
app.use(cors());
//now we should configure the API to use bodyParser and look for
//JSON data in the request body
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
//now we can set the route path & initialize the API
router.get('/', function(request, response) {
response.render(
<!doctype html>
<html>
<header>
<title>My Universal App</title>
</header>
<body>
<div id='app'>${ReactDOMServer.renderToString(<App />)}</div>
<script src='bundle.js'></script>
</body>
</html>
);
});
//Use our router configuration when we call /api
app.use('/api', router);
//starts the server and listens for requests
app.listen(port, function() {
console.log('api running on port' + port);
});
//adding the /appointments route to our /api router
router.route('/appointments')
//retrieve all appointments from the database
.get(function(request, response) {
//looks at our Appointment Schema
Appointment.find(function(error, appointments) {
if (error)
response.send(error);
//responds with a json object of our database appointments.
response.json(appointments)
});
})
//post new appointment to the database
.post(function(request, response) {
var appointment = new Appointment();
//body parser lets us use the req.body
appointment.appointmentTitle = req.body.appointmentTitle;
appointment.appointmentDate = req.body.appointmentDate;
appointment.appointmentTime = req.body.appointmentTime;
appointment.appointmentDescription = req.body.appointmentDescription;
appointment.appointmentDestination = req.body.appointmentDestination;
appointment.appointmentOrigin = req.body.appointmentOrigin;
appointment.travelMode = req.body.travelMode;
appointment.save(function(error) {
if (error)
response.send(error);
response.json({ message: 'Appointment successfully added!' });
});
});
Any guidance would be much appreciated.

To run the application on port defined by environment, try
PORT=3000 node server.js
You have other issue in your code. You have defined the router and mounting the router /api by defining app.use('/api', router). Hence, you are getting 404 whenever you access http://localhost:3000.
To fix the issue, change the router mounting to api.use('/', router).

You can run your API and front-end on the same port by replacing react-scripts with react-app-tools - a slightly altered version of Create React App that adds support for server-side code.
Find more info here: https://github.com/kriasoft/react-app

Related

Express rest api working in postman and development, but not when deployed

I have a project with Vue frontend, Express backend and Mysql as database (using Sequelize as ORM)
In development, the frontend communicates with the backend API without any issues.
When backend deployed on heroku, i can get a valid response when i use postman to query the endpoints
but when i try to do the same from the frontend running on localhost:8080, i get a blocked error as shown in the screenshot
below is my server.js (express startup code) that works when hosted on localhost:5000 and can communicate with the frontend on localhost:8080 but when backend is deployed to heroku, also working through postman, the fontend can't access the API on heroku because it gets blocked
I dont know if this is a CORS issue as the error says nothing about CORS.
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
//routes
const apiRouter = require('./routes/apiRouter');
app.use('/api', apiRouter);
/* -------------------------
Middlewares
------------------------
*/
app.use(cookieParser());
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(passport.initialize());
app.use(morgan('dev'));
/* Port and start up */
const port = process.env.PORT || 5000;
app.listen(port);
The axios timeout was too short to work in production.
it was set to 50ms. Just had to increase it to 5s using interceptors
//from api.js
import axios from 'axios';
import store from '../store/store';
const token = store.getters.GET_WEBTOKEN;
export default axios.create({
baseURL: store.state.apiURL,
headers: {
Authorization: token,
},
});
//in main.js
import api from './service/api';
Vue.prototype.$http = api;
api.defaults.timeout = 1000 * 5; //I changed this
Now i solved the issue, unto other oones

When express and vue js are connected, the default address is accessed

Express and vue.js are linked.
I have set up a route and when I run Express, it will go to an address different from the vue setting.
Vue index router
import Vue from 'vue';
import Router from 'vue-router';
import Editor from '#/components/Editor';
Vue.use(Router);
export default new Router({
mode: 'history',
routes:[
{
path : '/board',
name : 'Editor',
component : Editor
}
]
})
Vue main.js
import Vue from 'vue';
import App from './App.vue';
import {router} from './routes/index.js';
import axios from 'axios';
import ElementUI from 'element-ui';
import { ElementTiptapPlugin } from 'element-tiptap';
import 'element-tiptap/lib/index.css';
Vue.use(ElementUI);
Vue.use(ElementTiptapPlugin, {
// lang: "zh",
// spellcheck: false,
});
Vue.config.productionTip = false;
Vue.prototype.$http = axios;
new Vue({
render: h => h(App),
router
}).$mount('#app');
Express index router
const express = require('express');
const router = express.Router();
const mssql = require('mssql');
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p>Index</p>
<a href='/board'> Go to Vue Page </a>
</body>
</html>
app.js
const createError = require('http-errors');
const fs = require('fs');
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const ejs = require('ejs');
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
const apiRouter = require('./routes/api');
const port = 8000; //changed port
app.use(express.static('public'));
app.use(express.static(path.join('node_modules', 'devexpress-richedit', 'dist', 'custom')));
app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
// view engine setup
app.set('views', path.join(__dirname, '/views'));
app.set('view engine', 'ejs');
app.use(cors());
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
// app.use(express.static(path.join(__dirname, '/public')));
app.use(express.static(path.join(__dirname, '/')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/api', apiRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`));
my project tree
Express is 8000 port
Connect to'localhost: 8000 /'and Vue's page will appear.
I want express's page to appear when connecting to'localhost:8000/'
And 'localhost:8000/board' doesn't work
I have one more question. How to call express's html file in Vue?
Thanks for your answer
We first need to know how the request flow works in whole express and vue.js.
In your project, you're not running express and vue with same port at the same time.
More clearly, you're using express with 8080 port to host vue.js content.
Express Router Flow
In express, your request match the rule app.get("/", (req, res)= >{res.render("index")})
then express will render the index page for you when you visit the http://localhost:{port}/
And after you add "other" word in your rule app.get("/other", (req, res)= >{res.render("other")})
express will render other page for you when you visit the http://localhost:{port}/other
What if request doesn't match rule?
Express will give you a not found page.
Vue.js Router Flow
When you host the vue output js (we call build.js here) in express server instead of webpack-server.
In your vue project, you setup the vue-router with / and /other-path.
In these paths, vue will render content of / and /other-page.
We assume you use express to host the build.js
And we need the html to include this build.js with following code.
<!-- put this html in project_root/public/{here}-->
<div id="app"></div>
<script src="/build.js"><script>
And we setup the following code to host this html.
app.use("/", express.static(path.resolve(__dirname, "public")))
When we type http://localhost:{port}/, express will give the index.html to browser.
Then you could use website, click link which is <router-link> go to "other-page" which is provide by vue.
BUT, what if you type http://localhost:{port}/other-page in the browser, then push enter button?
Express will give you not found page.
Because express will give you content when you first visit the website.
That means when a request is coming to express, express will give you content by your rule.
If rule doesn't match, express give you not found page.
If rule match, express give you content which you setup.
In the above example, express give you index.html because you setup app.use("/", express.static(path.resolve(__dirname, "public"))).
Then you see the content of index.html in browser.
And then there is a build.js to render the content of vue for you.
But, there is no rule about "other-page" so express give you not found page.
Sum Up
When request is coming ...
express decide the content by your rule.
Not Matched: express give you not found page.
Matched: express give you content by your rule
When request matched, it give you html (at above example).
bowser render the html
html include the build.js
build.js render the content of vue
When you click vue-router link go to other page
There is no real request sent by browser.
It's controlled by your build.js.
When you type url in browser, then push enter bottom.
It goes to step1.
So if your vue.js router and express router have same router path
your express will not give you the vue content when you type url in browser.
Example
We assume your vue output which is called build.js is located in project_root/public/{here}.
There are two routes path "/" and "/vue-content" in your vue-router.
In your express router, you setup like this.
app.use("/", express.static(path.resolve(__dirname, "public")))
app.use("/express-page", (req, res) => {res.render("express-page")})
Then in webpack.config.js
output.publicPath: "/"
output.path: path.resolve(__dirname, "public")
And don't forget the you must have a index.html in project_root/public.
index.html must have script tag including the build.js
Then try to put <a href="/express-page">go to express<a> into your vue content.
Finally, go to http://localhost:8080/, you will see html page which in public.
The build.js render the content for you.
After you click "go to express", browser send the request to express.
Express will render "express-page" for you instead of index.html page.
But, you will find express give you not found page after you type http://localhost:{port}/vue-router in browser and push enter button.
So, how to fix it?
Add the new rule in the end.
app.use("/", express.static(path.resolve(__dirname, "public")))
app.use("/express-page", (req, res) => {res.render("express-page")})
app.use(express.static(path.resolve(__dirname, "public"))) // here
According our sum up step flow, express doesn't have "vue-router" rule.
But, there is a rule which match any request.
app.use(express.static(path.resolve(__dirname, "public")))
So, express will give you index.html
Then build.js will render "vue-router" content for you because vue will get the url to render content by vue-router setting.

Third party redirects to browser, not backend

Hello and thank you in advance.
We are running our Node.js/Express application in App Engine which serves a React front end as static files. At one point the user is able to redirect to a third-party so that the user can sign in and authorize some of their data which is managed by that third-party. We provide the redirect URI to the third-party as a parameter in the querystring, which is https://www.example.com/api/thirdparty/OAuthCallback.
When the user submits the authorization on the third-party side, they should be redirected to that URI, which is an API endpoint in our application where we process the result, and then in our backend we redirect the user to their dashboard. Instead, the user gets redirected to that URI in the browser.
We know (we think) we are doing the routing correctly because we are able to hit that end point from Postman and simulate the process that way.
The question is, why is this redirection going to the browser and not our backend?
Below you will find our server entry point (index.js) along with the route we expect to hit (thirdparty.js)
index.js:
//index.js
import express from "express";
import thirdparty from "./routes/thirdparty";
import httpsRedirect from "./middlewares/https-redirect";
const app = express();
var httpsPort = app.get('https-port');
app.use(httpsRedirect({httpsPort: httpsPort}));
app.set('trust proxy', true);
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
app.use(express.static(path.join(__dirname, '../build')));
app.use("/api/thirdparty", thirdparty);
app.get("/*", (req, res, next) => {
console.log('new request from: '+req);
res.sendFile(path.join(__dirname, '../build', 'index.html'));
});
app.listen(8080, ()=>console.log('Listening on port 8080'));
thirdparty.js (route):
// thirdparty.js
import express from "express";
const router = express.Router();
var oauth2 = require('./lib/OAuth2.0')();
router.get('/OAuthCallback', function (req, res) {
console.log("CODE--------------------------- : " + req.query.code);
var params = {
code: req.query.code,
redirect_uri: 'https://www.example.com/api/thirdparty/OAuthCallback'
}
oauth2.authCode.getOAuthToken(params, saveToken);
function saveToken(error, result) {
// do things with the result
res.redirect(process.env.HOST+"/dashboard");
}
});
export default router;

How to access website content from onother server with express or http

How to access website content from another server with Express or HTTP
I have a website that holds all data like template website for example
and I have 3 more websites that get access this website template content HTML CSS everything inside website 2 3 and 4 the only defriend is the route like
mysite.com/template1/user1/index.html
mysite.com/template1/user2/index.html
mysite.com/template1/user3/index.html
I want to have inside website **(n)* only code that gets the HTML CSS and js content from the template server the master how I can do that?.
In PHP is something like
$url = $GET(www.masterserve.com/template1/ + user1 ) echo $url
Any example that I can do the same with node.js and express
// Get dependencies
const express = require('express');
const path = require('path');
const http = require('http');
const bodyParser = require('body-parser');
// Get our API routes
const api = require('./server/routes/api');
const app = express();
// Parsers for POST data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// Point static path to dist
app.use(express.static(path.join(__dirname, 'dist'))); <-- idont want static
file only a URL from the master server
// Set our api routes
app.use('/api', api);
// Catch all other routes and return the index file
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
/**
* Get port from environment and store in Express.
*/
const port = process.env.PORT || '3000';
app.set('port', port);
/**
* Create HTTP server.
*/
const server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port, () => console.log(`API running on localhost:${port}`));
If you're trying to get HTTP content from some other server from within your nodejs app, you can use the request module.
request.get('http://somesite.com/template1/user3/index.html', function(err, response, body) {
// access data from other web site here
});
If you're trying to stream that data to some other response, you can also .pipe() the data that you requested to another response. The documentation for that module shows lots of examples of how to do that.

express subdomain handler routes get mixed up

I'm trying to get my subdomains working with each a diffrent router in express but they get mixed up.
when i'm going to panel.localhost:3333/login i see the login page. But whenever i'm going to api.localhost:3333/login i get the same page... and i did not defined the login route in de api router. So is there anyone who know how to solve this? :)
import { renderer, listen, sessionConfiguration } from './server.express';
import { _static, scripts, modules, staticPages } from './server.static';
import { serverRegisterAPIs } from './server.apis';
import serverConfiguration from './server.configuration';
let express = require('express'),
app = express(),
mongoose = require('mongoose'),
routerPanel = express.Router(),
routerApi = express.Router();
serverConfiguration(app).then(function(configuration) {
mongoose.connect(process.configuration.mongo.url);
app.use( require('express-subdomain-handler')({ baseUrl: 'localhost', prefix: 'subdomain', logger: true }) );
// configure the session to ....
sessionConfiguration(app);
// configure the renderer
renderer(app);
// hook up a few static calls to the server
_static(routerPanel, express);
scripts(routerPanel, express);
modules(routerPanel, express);
// register static files
staticPages(routerPanel);
// register api
//serverRegisterAPIs(routerApi);
routerApi.get('/', (req, res)=>{
console.log("test");
});
//set subdomains
app.use('/subdomain/:panel',routerPanel);
app.use('/subdomain/:api', routerApi);
// start listening
listen(app);
});
Where I set the subdomain I've added a : to the route so that its parameter express will parse. So instead I removed the : and now it's working!

Resources