I would like to serve up a ReactJS Single Page App from a nodeJS server and pass up some JSON data at the same time.
I have user credentials and some other user specific data that i would like pre-populated into my page and avoid making multiple round trips to the server.
How do i pass a JSON object to the client at request time and have it available to my React app
var path = require('path');
const express = require('express');
const app = express();
const port = process.env.PORT;
app.use(express.static('dist'));
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname, '../../dist/index.html'));
});
app.listen(port, () => console.log(`Running on port ${port}.`));
I can suggest you add a script tag into your index.html file. Like below
<script>
window._DATA_={ key: 'value', .....}
</script>
Now in your react application, use the key window._DATA_ to get the data that you sent from the server. In this approach, the problem is that you can't send dynamic data.
To achieve that you may need to use the template libraries. For example pug, nunjucks, ejs, etc.
Below is the example of using pug.
Your express route will look like this.
app.get('/*', function(req, res) {
res.render('index', data);
});
// End of your pug file looks like
...
...
script.
var window._DATA_= !{JSON.stringify(data)}
If you want to add scripts files dynamically then you can use html-webpack-pug-plugin.
For more info
https://www.npmjs.com/package/html-webpack-pug-plugin
Related
I'm trying to weather Api app using node, express and Axios in backend part without using any framework like Angular or react.
I have 3 main file for my codes.
index.html
customer.js (for front end part)
server.js (for backend part)
My backend part like below;
const express = require('express');
const app = express();
const axios = require('axios').default;
API_KEY = "***";
const PORT =3000;
// app.use("/static", express.static(__dirname + '/customer'));
app.get('/', (req, res) =>{
axios
.get(`http://api.openweathermap.org/data/2.5/forecast?q=amsterdam&appid=${API_KEY}`)
.then(resp => {
let weatherDetail = resp.data;
console.log('a single country details: ', weatherDetail);
res.send(weatherDetail);
})
.catch(err => console.log(err));
});
app.listen(PORT, () => console.log(`My app listening on port ${PORT}! `));
When I write localhost:3000 on browser, I can see the weather api's data. However I want to see html file with functions in customer.js and api's data. Therefore I tried to write res.sendFile((__dirname + '/index.html')); inside app.get('/', (req, res)) function. However, in this situation I can see only html page without getting data from backend.
How can I call data getting from backend part in frontend part inside customer.js file?
My codes in customer.js like below (but I'm not sure if I use axios agan inside this file)
const apiCall = cityName => {
let apiKey = "***";
let apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${cityName}&appid=${apiKey}&units=metric`
axios
.get(apiUrl)
.then(getWeather)
.catch(err => {
console.log(err);
err.response.status === 404 ? alert(`The country ${cityName} doesn't exist.`) : alert('Server error! Sorry.');
});
};
apiCall(amsterdam)
function getWeather (response) {
let city = document.querySelector("#city");
city.innerHTML = response.data.name;
.
.
.
.
}
I would recommend to use a templating engine like handlebars or ejs.There are tons of examples for it, and sending data from backend to frontend becomes a piece of cake when using any templating engine. my personal favourite is handlebars because of its simple syntax.
It is advisable not to use document.querySelector if you're using Angular or React. React/Angular will have the browser repaint the DOM by making updates in the "root" div element of the index.html file whenever there is new data available to update.
Also, why do you want to send a HTML file? You could have a route in Node like below
route.get('/weather', (req, res) => {
// do your api call with axios to get weather data
res.json(weatherData);
});
from your front-end you could make an API call to '/weather' route and consume the JSON data
axios.get('baseUrl/weather').then(res=>{
console.log("weather data", res);
}).catch(...);
You could also fetch weather data directly from front-end like above.
I am integrating a 3rd party template which has a slideshow written using Mootools. The nodejs is configured with express and ejs
The data for the slideshow comes from a few xml files. For example data.xml. I placed the data.xml in public folder and added the following code to server.js (the main file)
app.use(express.static(__dirname + '/public'));
app.post('/data.xml', function(req, res){
res.contentType('application/xml');
res.sendFile('/data.xml');
});
Unfortunately this does not seems to work. I can see the file if I type the url
http://localhost:8080/data.xml
But the response I see in firebug is
" Cannot POST /data.xml "
I am assuming Mootools is trying to access the file using some POST method. Any suggestions for this problem?
when you are sending the file with sendFile() you need to point to the absolute address check this. note that I have the data.xml in the main folder.
you can access the file with localhost:8080/data (not localhost:8080/data.xml) and also as this is a post, you cannot access it through browser. use postman instead. or if you need it to be accessible on browser you need to change the protocol to get.
var express = require('express');
var path = require('path');
var app = express();
// you don't need this line!
// app.use(express.static(path.join(__dirname)));
app.post('/data', function(req, res){
res.contentType('application/xml');
res.sendFile(path.join(__dirname , 'data.xml'));
});
var server = app.listen(8080, () => {
console.log('Started listening on 8080');
});
I am hosting a single page app with Node and Express. I use the static middleware for the client. I am using path URLs to navigate the app, for example domain.com/profile/134. I am using history.pushState to change pages internally on the client, and this works fine. What I am missing is a wildcard rule on the server to catch all possible paths when the user accesses my page directly to a path that is not root. If I try to access domain.com/profile/134 directly I get this: "Cannot GET /profile/134". I have tried to insert a wildcard get at the end of server.js, but it seems to be hit every time, also when I access the page root. This is my relevant code:
var express = require('express');
var app = express();
app.use(express.static(__dirname + '/../client'));
app.get('/*', function(req, res) {
console.log('wildcard');
});
Is this the correct GET wildcard rule to achieve what I need, and how can I serve the static client inside this handler? My client side will find the right page afterwards as long as the initial path is preserved. I basically want this wildcard rule to behave the same as the static rule, but keep the initial path.
You can use a hack
app.get('/:url', function(req, res) {
console.log('wildcard');
});
or try this one
app.get('/(.*)', function(req, res) {
console.log('wildcard');
});
[edited]: this should work as you expect:
var express = require('express');
var app = express();
app.get(/(.*)/, function(req, res) {
console.log("req.path", req.path);
res.send('success');
});
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('Example app listening at http://%s:%s', host, port);
});
I ended up using an npm module named express-history-fallback-api to solve this. Out of the box it solved both simple and advanced paths, like domain.com/settings and domain.com/profile/username
https://www.npmjs.com/package/express-history-api-fallback
I am building my first application using NodeJS & ExpressJS for the backend and AngularJS front end. I have all my front end routes working how I want, but I cannot figure out how to properly configure the Node backend so that when a specific URL is entered into the address bar, Node renders only the same Angular app every time.
For example, if I use About as a link to my about page in Angular, then the correct view will render and the address bar will show localhost:8080/about. However, if I just manually type in localhost:8080/about then Node responds with Cannot GET /about. Now I understand this is because my backend currently only handles the following:
var express = require('express');
var crypto = require('crypto');
var app = express();
app.set('views', __dirname + '/public');
app.engine('html', require('ejs').renderFile);
app.use(express.static(__dirname + '/public'));
app.get('/', function(req, res){
res.render('index.html');
});
// API
app.get('/api/sync', function(req, res){
// Here we generate a 32 byte string and make it the key
var num = Math.floor(Math.random()*100);
var key = crypto.createHash('md5').update(num.toString()).digest('hex');
key = key.slice(0, key.length/2);
console.log('Created key: ' + key);
res.send(key);
});
var server = app.listen(8080, function(){
console.log('Listening on port %d', server.address().port);
});
So what I want to do is make it so EVERY request to Node renders the same index.html page but then properly routes the view in Angular based on the URL. What is the best way to do this?
I just realized that using:
app.get('*', function(req, res){
res.render('index.html');
});
And placing this after all other routes I want to catch first will work.
Since I don't have enough reputation yet to just add a comment, it's worth noting that res.render() won't work if you're not using a server-side template rendering engine (as you are using EJS). You would instead want to use something like res.sendFile() if you were just serving a static HTML and Angular page with all the routing set up in Angular.
app.get( '*', function( req, res ) {
res.sendFile( __dirname + '/public/index.html' );
} );
The best way handle angular route in angular-app and backend route in backend.
Angular/Frontend:
sampleApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/', {
templateUrl: 'templates/home.html',
controller: 'MainController'
}).
when('/about', {
templateUrl: 'templates/about.html',
controller: 'AboutController'
}).
// >>> redirect other routes to
otherwise({
redirectTo: '/'
});
}]);
Backend:
For render static html you don't need app.get(...)
simple place index.html into:
public/index.html
and express serve it as html. Other not exists pages(routes) return 404 error and it is right.
In this case API fully separate and independent and angular fully single page app. Express serve static resources needed for angular.
I am using parse.com cloud code with express to setup my routes. I have done this in the past with node, and I have my routes in separate files. So, in node I do
app.js
express = require("express");
app = exports.app = express();
require("./routes/js/account");
account.js
app = module.parent.exports.app;
app.get("/api/account/twitter", passport.authenticate("twitter"));
All the examples on parses site https://parse.com/docs/cloud_code_guide#webapp show this being done as follows.
app.js
var express = require('express');
var app = express();
app.get('/hello', function(req, res) {
res.render('hello', { message: 'Congrats, you just set up your app!' });
});
So, I would like to change the bottom to include a routes folder with separate routes files, but am not sure how to do this in parse.
I know this post is a little old, but I just wanted to post a solution for anyone still looking to get this to work.
What you need to do, is create your route file, I keep them in 'routes' forlder, for example <my_app_dir>/cloud/routes/user.js
Inside user.js you will have something that looks like this:
module.exports = function(app) {
app.get("/users/login", function(req, res) {
.. do your custom logic here ..
});
app.get("/users/logout", function(req, res) {
.. do your custom logic here ..
});
}
Then, in app.js you just include your file, but remember that you need to append cloud to the path, and pass the reference to your app instance:
require('cloud/routes/user')(app);
Also, remember that express evaluates routes in order, so you should take that into consideration when importing several route files.
I'm using a different method, have the routes in app.js, but you can probably include them in file if you prefer. Take a look at the example app,
anyblog on github
The way it works:
Set up a controller:
// Controller code in separate files.
var postsController = require('cloud/controllers/posts.js');
Add the controller route
// Show all posts on homepage
app.get('/', postsController.index);
// RESTful routes for the blog post object.
app.get('/posts', postsController.index);
app.get('/posts/new', postsController.new);
And then in posts.js, you can use exports, ex.
var Post = Parse.Object.extend('Post');
// Display all posts.
exports.index = function(req, res) {
var query = new Parse.Query(Post);
query.descending('createdAt');
query.find().then(function(results) {
res.render('posts/index', {
posts: results
});
},
function() {
res.send(500, 'Failed loading posts');
});
};
// Display a form for creating a new post.
exports.new = function(req, res) {
res.render('posts/new', {});
};
Pass the app reference to the post controller, and add the routes from there