# Rendering React on Node.js server? #
================================================================================================
## Intro ##
Hello, I am trying to build a node.js server, and render React with it. I have been trying to follow some online tutorials, in the process getting a little lost. I understand GET/POST routes. I know my server.js need to render my index.js'. My React code runs fine with the React environment with npm start` in the shell/terminal.
## The Error ##
My Error happens when I attempt to run my server via "nodemon server.js" in the terminal.
[nodemon] starting `node server.js`
(node:10748) ExperimentalWarning: The ESM module loader is experimental.
file:///C:/Users/The%20core%20unit/Desktop/Code%20file%20storage/React/react-app/server.js:42
const app = ReactDOMServer.renderToString(<Index />);
^
SyntaxError: Unexpected token '<'
at Loader.moduleStrategy (internal/modules/esm/translators.js:88:18)
at async link (internal/modules/esm/module_job.js:41:21)
[nodemon] app crashed - waiting for file changes before starting...
I know this Error is telling me that my server.js can not read the React code.
### server.js ###
const express = require("express");
const bodyParser = require("body-parser");
const ejs = require("ejs");
const mongoose = require("mongoose");
const https = require("https");
const { Server } = require("http");
const app = express();
import Index from "./src/index.js"
import ReactDOMServer from 'react-dom/server'
import path from 'path';
import fs from 'fs';
// Error List //
/*Server is starting, however not rendering any data. */
//Mongoose connection to MongoDB//
mongoose.connect("mongodb://localhost:27017/watchesDB",{ useUnifiedTopology: true, useNewUrlParser: true});
//Mongoose Schema//
const watchesSchema = {
Company: String,
Model: String,
Year: Number
};
const Watch = mongoose.model("Watch", watchesSchema);
//Boiler Plate Code for Server//
app.set("view engine", "ejs");
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(express.static("public"));
// API call Routes //
//Home APi call
app.get('/', (req, res) => {
const app = ReactDOMServer.renderToString(<Index />);
const indexFile = path.resolve("./public/index.htlm");
fs.readFile(indexFile, 'utf8', (err, data) => {
if (err) {
console.error('Something went wrong:', err);
return res.status(500).send('Oops, better luck next time!');
}
return res.send(
data.replace(`<div id="root"></div>`, `<div id="root">${app}</div>`)
);
});
});
app.use(express.static('./build'));
//Watches API call
app.get("/watches", function(req, res){
Watch.find(function(err, foundWatches) {
console.log(foundWatches)
});
});
// Server listing //
app.listen(3000, function(){
console.log("Server accessfully booted...")
});
### index.js ###
import ReactDOM from 'react-dom';
import Heading from "./Heading";
import Watch from "./Watch";
function Index() {
ReactDOM.render(<div>
<Heading />
<Watch /></div>,
document.getElementById('root')
);
};
export default Index
[Pic of index.js][1]
[Pic of server.js][2]
Related
I am very new to React Native. As part of my learning i was trying to connect a Mongodb Atlas cluster to my RN App. But I am very confused in developing the code for a post request to the Mongodb cluster. I was following a tutorial to develop a sample taxi booking app. It would be great if someone helps me to understand the code.
So my server code is as follows:
var express = require("express");
var path = require("path");
var bodyParser = require("body-parser");
var index = require("./routes/index");
var bookings = require("./routes/bookings");
var app = express();
var port = 3000;
app.listen(port,function(){
console.log("Server running on port",port);
})
//views
app.set("views", path.join(__dirname,"views"));
app.set("view engine","ejs");
app.engine("html", require("ejs").renderFile);
//Body Parser MW
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:true}))
//Routes
app.use("/",index);
app.use("/api",bookings);
Then i have a bookings.js file as follows:
var express = require("express");
var router = express.Router();
var MongoClient = require("mongodb").MongoClient ;
var uri = "mongodb+srv://TheCarAdmin:admin0000#thecarcluster-3hqxd.mongodb.net/test?&w=majority";
router.post("/bookings",function(req,res,next){
var bookings =req.body.data;
if(!bookings.username){
res.status(400);
res.json({
error:"Bad data"
});
}
else {
MongoClient.connect(uri)
.then(client=>{
const db = client.db("TheCar");
const coll = db.collection("bookings");
coll.save(bookings,function(err,savedBooking){
if(err){
res.send(err);
}
res.json(savedBooking);
});
})
.catch(err=>console.log(err));
}
});
module.exports = router;
In my App code i have a function that handles logging the booking request to the MongoDB collection as follows:
import update from "react-addons-update";
import constants from "./actionConstants";
import Geolocation from "react-native-geolocation-service";
import {Dimensions} from "react-native";
import RNGooglePlaces from "react-native-google-places";
import request from "../../../util/request";
import calculateFare from "../../../util/fareCalaculator";
export function bookCar(){
return(dispatch,store)=>{
const payload = {
data:{
username:"eman",
pickUp:{
address :store().home.selectedAddress.selectedPickUp.address,
name : store().home.selectedAddress.selectedPickUp.name,
latitude: store().home.selectedAddress.selectedPickUp.location.latitude,
longitude: store().home.selectedAddress.selectedPickUp.location.longitude
},
dropOff:{
address :store().home.selectedAddress.selectedDropOff.address,
name : store().home.selectedAddress.selectedDropOff.name,
latitude: store().home.selectedAddress.selectedDropOff.location.latitude,
longitude: store().home.selectedAddress.selectedDropOff.location.longitude
},
fare: store().home.fare,
status:"pending"
}
}
request.post("http://172.20.10.2:3000/api")
.send(payload)
.finish((err,res)=>{
dispatch({
type : BOOK_CAR,
payload : res.body
});
console.log(err.message);
console.log(res.status);
});
};
}
So as shown above the Request is imported from the following file:
const request = require("superagent");
const defaultAjaxTimeout = 30000;
//const cookie = require("cookie");
request.Request.prototype.finish = function (callback) {
// this replaces superagent's .end() function to include our custom error handling (see above)
this.end((err,res)=>{
callback(err,res);
});
};
var requestWrapper = function(method) {
// this is here so that we can append the .timeout call to all of our ajax requests with the default value.
return function(url) {
return request[method](url)
.type("form")
.timeout(defaultAjaxTimeout);
};
};
export default {
get: requestWrapper("get"),
put: requestWrapper("put"),
post: requestWrapper("post"),
del: requestWrapper("del"),
};
When i was debugging I realised that its not getting redirected to the booking.js code to process the POST request and hence throwing error. Could anyone help me to understand how this gets redirected to bookings.js or correct me if the written code is wrong.
Thanks in advance!!!!!
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",
I know my problem may seem not very specific, but I'm having problem describing what's happening because I don't understand it :(
So I've written small express app with ssr (for react) and jwt authentication. The SSR part works nice but the rest is crap.....
import 'babel-polyfill';
import express from 'express';
import bodyParser from 'body-parser';
import logger from 'morgan';
import authRouter from './auth/authRouter';
const app = express();
app.use(express.static('public'));
app.use(logger('dev'));
app.use(bodyParser.json({ type: '*/*' }));
app.use(bodyParser.urlencoded({ extended: false }));
//a lot of irrelevant ssr code
authRouter(app);
app.listen(3000, function(err) {
if (err) {
console.log(err);
}
console.log('main server on port 3000');
});
This is my main server file. My first problem is I don't see ANY console.logs from my files. There's nothing in my terminal. That's the reason I can't see how does my requests look like in my app. I'm testing it using postman like that:
And that's the authRouter I'm using above in main server file:
import express from 'express';
import { signup, signin } from '../controllers/authentication';
import { jwtLogin, localLogin } from '../services/passport';
import passport from 'passport';
passport.use(jwtLogin);
passport.use(localLogin);
//if user is auth'd do not create session for him
const requireAuth = passport.authenticate('jwt', { session: false });
const requireSignin = passport.authenticate('local', { session: false });
const authRouter = function(app) {
app.get('/auth', requireAuth, function(req, res) {
res.send({ hi: 'there' });
});
app.post('/auth/signin', requireSignin, signin); // irrelevant right now
app.post('/auth/signup', signup);
};
export default authRouter;
And that's signup function I'm using in router:
const signup = (req, res, next) => {
console.log('reqqqqqqq', req);
const { email, password, passwordCheck } = req.body; //code crashes here
//some code I deleted for everyones convenience
};
Every request I make my app crashes because req.body is undefined. I can't log req because I can't see any logs. I also tried sending back stringified version of my request body but every time i get "TypeError: Converting circular structure to JSON".
I'll be happy to add any information you may need
EDIT:
I'm gonna check that later at home but now I'm thinking there is something wrong with ssr part of my app because I don't even see that 'main server on port 3000' log..... At the same time server responds with right html, js files and routing works well so.....anyway I'm gonna look it up later
Try using util.inspect from node:
const util = require('util');
// usage in your code
const signup = (req, res, next) => {
console.log(util.inspect(req, { showHidden: true, depth: null }));
const { email, password, passwordCheck } = req.body;
...
};
I solved it.....unexpectedly the problem was lying in my package.json file.
My start script looked like this:
"scripts": {
"start": "nodemon build/server.bundle.js | nodemon build/api.bundle.js",
...
}
And because of that I had these weird errors. Only api.bundle.js file was running correctly......
Anyway thanks for your help ;)
I am using angular 6 and implemented the angular universal for server side page rendering,
Everything works fine but I am getting continues error in my console.
ERROR [Error]
ERROR [Error]
And I have declared window as global variable still I am getting errors like this
Unhandled Promise rejection: window.some_function is not a
function
my server.ts file
//
import 'zone.js/dist/zone-node';
import 'reflect-metadata';
import { enableProdMode } from '#angular/core';
import * as express from 'express';
import { join } from 'path';
// Express Engine
import { ngExpressEngine } from '#nguniversal/express-engine';
// Import module map for lazy loading
import { provideModuleMap } from '#nguniversal/module-map-ngfactory-loader';
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();
// node run port
const port = 6065;
// Express server
const app = express();
const PORT = port || process.env.PORT;
const DIST_FOLDER = join(process.cwd(), 'dist');
const domino = require('domino');
const fs = require('fs');
const MockBrowser = require('mock-browser').mocks.MockBrowser;
const mock = new MockBrowser();
const template = fs
.readFileSync(join(DIST_FOLDER, 'browser', 'index.html'))
.toString();
// Make all Domino types available as types in the global env.
Object.assign(global, domino.impl);
(global as any)['KeyboardEvent'] = domino.impl.Event;
const win = domino.createWindow(template);
win.Object = Object;
win.Math = Math;
global['window'] = win;
global['document'] = win.document;
global['navigator'] = mock.getNavigator();
global['branch'] = null;
global['object'] = win.object;
global['HTMLElement'] = win.HTMLElement;
global['DOMTokenList'] = win.DOMTokenList;
global['Node'] = win.Node;
global['Text'] = win.Text;
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main');
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP)
]
}));
app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'));
// TODO: implement data requests securely
app.get('/api/*', (req, res) => {
res.status(404).send('data requests are not supported');
});
// Server static files from /browser
app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));
// All regular routes use the Universal engine
app.get('*', (req, res) => {
res.render('index', { req });
});
// Start up the Node server
app.listen(PORT, '0.0.0.0', () => {
console.log(`Node server listening on http://localhost:${PORT}`);
});
I had the same problem and started debugging server.js in my Visual Studio Code and what I had found: an error messages was caused by http requests from TranslateHttpLoader (I'm using ngx-translate), which was trying to load .json-files with translations. After some googling I found this thread where you can find how to enable server-side translation loading. After that error messages disappeared.
I am trying to separate responsibilities, dividing in different files and modules the different actions that I will carry out in my application on the server side.
I ran into a problem, which I can not understand. I try to export from the file which starts the server, the variable app, in which I store express in the following way:
server.js
import express from 'express';
import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackConfig from '../webpack.config';
import path from 'path';
const app = express();
app.set('port', process.env.PORT || 3000);
app.use(webpackDevMiddleware(webpack(webpackConfig)));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '..', 'public', 'index.html'));
});
app.get('/api', (req, res) => {
res.json({api: "Woks Fine"});
});
app.listen(app.get('port'), () => {
console.log("App Start in Port", app.get('port'));
});
export default app;
apiGoogleMaps.js
import app from '../server.js';
export function respuestaMensaje(apiUrl, app) {
console.log(apiUrl);
app.post(apiUrl, (req, res) => {
console.log(req.body);
});
}
adressjs
import React, { Component } from 'react';
import { render } from 'react-dom';
import request from 'superagent';
import { respuestaMensaje } from '../../../src/handlers/apiGoogleMap.js';
class AddressInput extends Component{
constructor(){
super();
this.state = {
address: "",
api:"http://maps.google.com/maps/api/geocode/json?address=",
direccion: "",
latitud: "",
longitud:""
};
}
render(){
return(
<div>
<form>
<input type="text" value={this.state.address} onChange={this.updateAdress.bind(this)}/>
<button onClick={this.getAddressGeo.bind(this)}>Consultar</button>
</form>
<ul>
<li><label>Direccion:</label>{this.state.direccion}</li>
<li><label>Latitud:{this.state.latitud}</label></li>
<li><label>Longitud:{this.state.longitud}</label></li>
</ul>
</div>
)
}
updateAdress(event){
this.setState({
address: event.target.value
});
}
getAddressGeo(e){
e.preventDefault();
const apiUrl = this.state.api + this.state.address;
respuestaMensaje(apiUrl);
}
}
export default AddressInput;
Project Structure
Many errors like this appear:
[1] WARNING in ./node_modules/uglifyjs-webpack-plugin/node_modules/uglify-es/tools/node.js
[1] 18:11-32 Critical dependency: the request of a dependency is an expression
[1] # ./node_modules/uglifyjs-webpack-plugin/node_modules/uglify-es/tools/node.js
[1] # ./node_modules/uglifyjs-webpack-plugin/dist/uglify/minify.js
[1] # ./node_modules/uglifyjs-webpack-plugin/dist/uglify/index.js
[1] # ./node_modules/uglifyjs-webpack-plugin/dist/index.js
[1] # ./node_modules/uglifyjs-webpack-plugin/dist/cjs.js
[1] # (webpack)/lib/WebpackOptionsDefaulter.js
[1] # (webpack)/lib/webpack.js
[1] # ./src/server.js
[1] # ./src/handlers/apiGoogleMap.js
[1] # ./src/ui/components/address.js
[1] # ./src/ui/app.js
[1] # ./src/ui/index.js
[1]
Full error log
This error is caused by the webpack import in server.js, used as middleware:
import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackConfig from '../webpack.config';
import path from 'path';
const app = express();
app.set('port', process.env.PORT || 3000);
app.use(webpackDevMiddleware(webpack(webpackConfig)));
The telling frames from the stack trace are:
[1] # (webpack)/lib/WebpackOptionsDefaulter.js
[1] # (webpack)/lib/webpack.js
[1] # ./src/server.js
It appears you can't include webpack in a webpack bundle, at least not by importing it, thanks to the dynamic imports that webpack uses.
The same errors appear with express (specifically express.view), explained in this issue:
There's a dynamical requirement in ExpressJS: https://github.com/expressjs/express/blob/master/lib/view.js#L78
You can mark express (and/or webpack) as an external dependency to prevent them from being included in the bundle (I have not used this library, it is linked from the issue).
Instead of exporting your app, which the main backbone of your entire server, use a router in your apiGoogleMaps.js file and export it.
Use the router in your app.js file successfully! :D
birds.route.js
var express = require('express')
var router = express.Router()
router.get('/', function (req, res) {
res.send('Birds home page')
})
router.get('/:id', function (req, res) {
res.send(req.params.id);
})
module.exports = router
app.js
const myBirds = require('./birds');
...
...
app.use('/birds', myBirds);