I have this AngularJS SPA with ui-router which works perfectly. It uses parse.com as the backend, and I have it working on a regular Apache server.
Now I want to move it to a node.js server-app, and I want node.js to handle all the CRUDs for parse.com.
I set up a nice little node.js app to act as server, and it works.
My question is: How do I handle requests between my node.js server-app and my AngularJS SPA?
I've included my server.js file, in case anyone can use it.
// set up =====================================================================================================
var express = require('express'),
path = require('path'),
morgan = require('morgan'),
bodyParser = require('body-parser'),
methodOverride = require('method-override'),
routes = require('routes'),
keys = require('./config/keys'),
port = 80;
var app = express();
var Parse = require('parse/node').Parse;
// view engine setup ==========================================================================================
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// configuration ==============================================================================================
app.use(require('prerender-node').set('prerenderToken', keys.prerender));
app.use(morgan('dev'));
app.use(bodyParser.urlencoded({'extended':'true'})); // parse application/x-www-form-urlencoded
app.use(bodyParser.json()); // parse application/json
app.use(bodyParser.json({ type: 'application/vnd.api+json' })); // parse application/vnd.api+json as json
app.use(methodOverride());
app.use(express.static(path.join(__dirname, 'public_html')));
Parse.initialize(keys.app, keys.js);
// routing ====================================================================================================
app.use(function(req, res) {
"use strict";
// use res.sendfile, as it streams instead of reading the file into memory. ===============================
res.sendfile(__dirname + '/public_html/index.html');
});
app.use('/', routes);
// catch 404 and forward to error handler =====================================================================
app.use(function(req, res, next) {
"use strict";
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// listen (start app with node server.js) =====================================================================
app.listen(port);
console.log("App listening on port %d", port);
File structure:
- public_html/ <-- the angularjs app -->
- node_modules/ <-- installed modules for node -->
- config/
- keys.js
- server.js
- package.json
The current setup
So, currently, I would deal with parse.com data in my (angularjs) app controller - and some of it in parse's cloud-code.
Now, I want to move all parse.com dealings to my node.js server-app, so that my angularjs "calls" the node.js server-app, which in turn "calls" parse.com for the data I need, and then return it to the angularjs app so I can update my views (with the new data from parse.com).
Example of what I want
parse.com <--> node.js server-app <--> angularjs SPA (views)
A simple thing I do in my controller is something like
var Profile = Parse.Object.extend('Profile');
var query = new Parse.Query(Profile);
query.equalTo('objectId', $stateParams.authorPermaLink);
query.find().then(function(results){
var object = results[0];
$scope.authorObj = results[0];
$scope.template.pageName = object.get('screenname');
$scope.template.pageAuthor = object.get('screenname');
$scope.template.pagePublished = object.createdAt;
$scope.template.pageLastEdit = object.updatedAt;
$scope.$apply();
}, function(error){
// error-handling
console.log("Error: " + error.code + " " + error.message);
});
Now, moving this snippet of code to node.js is simple because I can use the parse.com SDK in node.js directly. But how do I get the angularjs app to communicate with the node.js server-app?
Okay, so I managed to brain my way out of this.
The solution is to make routes that will handle http-requests from my angularjs app.
routes.js (node):
app.get('/api/article/:permalink', function(req, res) {
blog.getArticle(req.params.permalink, function(data) {
res.json(data);
});
});
// everything else
app.use(function(req, res) {
// use res.sendfile, as it streams instead of reading the file into memory.
res.sendfile('./public_html/index.html');
});
Then create a model with the blog object and methods, which are called above.
model.js (also node):
module.exports = {
getArticle : function(permalink, callback) {
"use strict";
var Article = Parse.Object.extend('Article');
var query = new Parse.Query(Article);
query.include('category');
query.include('profile');
query.equalTo('permalink', permalink);
query.find().then(function(results) {
var result = results[0];
var object = {
title: result.get('title'),
screenname: result.get('profile').get('screenname'),
profileID: result.get('profile').id,
content: result.get('content'),
publishedAt: result.get('publishedAt'),
updatedAt: result.updatedAt,
categoryName: result.get('category').get('categoryName'),
categoryPermaLink: result.get('category').get('categoryPermaLink'),
articleID: result.id
};
callback(object);
}, function(error) {
callback({error: error});
});
}
};
And then finally in mu angularjs app, simply make a http-request (get in this example but the other rest verbs all work as well).
controller.js (angularjs):
$http.get('/api/article/' + $stateParams.articlePermaLink
).then(function successCallback(response) {
response = response.data;
$scope.articleTitle = response.title;
$scope.template.pageName = response.title;
$scope.articleAuthor = response.screenname;
$scope.template.pageAuthor = response.screenname;
$scope.profilePermaLink = response.profileID;
$scope.articleContent = response.content;
$scope.publishDate = response.publishedAt;
$scope.template.pagePublished = response.publishedAt;
$scope.template.pageLastEdit = response.updatedAt;
$scope.category = response.categoryName;
$scope.categoryPermaLink = response.categoryPermaLink;
$scope.currentArticle = response.articleID;
console.log(response.commentObj);
}, function errorCallback(response) {
console.log("Error: " + response.code + " " + response.message);
});
The callbacks are the magic!
Related
Trying to set up a basic Express server with a basic pug template.
Can you please tell me what I'm doing wrong here?
'use strict';
//Require Express
var express = require('express');
var app = express();
//Require Pug
var pug = require('pug');
//Require Twitter
var Twitter = require('twitter');
//Set view engine to serve middleware
app.set('view engine', 'pug');
//Set where to look for templates
app.set('views', __dirname + '/templates');
//Set up style sheets
app.use('/static', express.static(__dirname + '/public'));
//Access keys to access twitter account
var config = {
"consumerKey": "",
"consumerSecret": "",
"accessToken": "",
"accessTokenSecret": ""
};
//instantiate twitter client
var client = new Twitter(config);
//Log whether
var error = function (err, response, body) {
console.log('ERROR [%s]', err);
};
var success = function (data) {
console.log('Data [%s]', data);
};
//Set up server on Port 3000
app.listen(3000, function() {
console.log("The frontend server is running on port 3000!");
});
//Render when appropriate
//Tell app to render template
app.get('/'), function(req, res){
res.render('index', {title: 'Hey', message: 'Hello there!'});
}
I'm getting back The frontend server is running on port 3000! in the console.
What am I missing?
I'd really appreciate any help please
You're calling app.get() wrong. You're doing
app.get('/'), function(req, res){
...
Which is two statements separated by the comma operator. The correct syntax is to pass the function as the second argument:
app.get('/', function(req, res){
...
});
I am a newbie to Node.JS and began looking for best practice articles, etc. to make sure my node.js code was unit test friendly and followed the node.js best practices. While researching I came across the following code examples:
app.js:
module.exports = function (flights) {
var express = require('express');
var routes = require('./routes')(flights);
var path = require('path');
var app = express();
// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(function (req, res, next) {
res.set('X-Powered-By', 'Flight Tracker');
next();
});
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
// development only
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}
app.get('/flight/:number', routes.flight);
app.put('/flight/:number/arrived', routes.arrived);
app.get('/list', routes.list);
return app;
}
server.js:
var http = require('http'),
flights = require('./data'),
app = require('./app')(flights);
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
module(index.js):
var Flight = function () {
this.data = {
number: null,
origin: null,
destination: null,
departs: null,
arrives: null,
actualDepart: null,
actualArrive: null
};
this.fill = function (info) {
for(var prop in this.data) {
if(this.data[prop] !== 'undefined') {
this.data[prop] = info[prop];
}
}
};
this.triggerDepart = function () {
this.data.actualDepart = Date.now();
};
this.triggerArrive = function () {
this.data.actualArrive = Date.now();
};
this.getInformation = function () {
return this.data;
};
};
module.exports = function (info) {
var instance = new Flight();
instance.fill(info);
return instance;
};
routes(index.js):
module.exports = function (flights) {
var flight = require('../flight');
for(var number in flights) {
flights[number] = flight(flights[number]);
}
var functions = {};
functions.flight = function(req, res){
var number = req.param('number');
if (typeof flights[number] === 'undefined') {
res.status(404).json({status: 'error'});
} else {
res.json(flights[number].getInformation());
}
};
functions.arrived = function (req, res) {
var number = req.param('number');
if (typeof flights[number] === 'undefined') {
res.status(404).json({status: 'error'});
} else {
flights[number].triggerArrive();
res.json({status: 'done'});
}
};
functions.list = function (req, res) {
res.render('list', {
title: 'All Flights',
flights: flights});
};
return functions;
};
While I like how this is organized alot, I do not see an efficient way to handle multiple modules in this scenario. Let's say I have additional modules for User, Initerary, etc. This seems like it could become chaotic quickly as I began to add routes to the app.js file, not to mention how many potential parameters would need to be passed into app.js. What if I have a large rest layer that required 20 or more modules? I am looking for any doc or links or examples demonstrating a clean way I should do this in node.js.
Granularize and encapsulate using sub routers with express.Router(). Each router then becomes it's own module that you pull in making it so that you don't have to add every single route explicitly in app.js.
User.js
"use strict";
// Create a router
var express = require("express"),
router = express.Router();
router.get("/", function (req, res, next) {
// All your User logic
res.send([
{
name: "Bob"
},
{
name: "John"
}
]);
});
router.get("/:number", function (req, res, next) {
res.send({
name: "Bob"
});
});
router.post("/", function (req, res, next) {
res.send({
name: "New Guy"
});
});
module.exports = router;
App.js
var app = express();
app.use("/user", require("./User"));
Now you can GET /user, GET /user/SOME_ID, and POST to /user. It's also simple if you want to move the sub route around to somewhere else because the methods defined for the user route are relative.
I too have a large enterprise app called campaign_service, consisting of many many modules.
This is how, I organize the routes in a file campaign_router.js.
var express = require('express');
var router = express.Router();
var cm = require('./campaign_manager.js');
var qsc = require('./converters/qs_converter.js');
var jsc = require('./converters/json_converter.js');
var xmlc = require('./converters/xml_converter.js');
var fc = require('./converters/f_converter');
var fc2 = require('./converters/fjt2_converter');
var cv = require('./campaign_validator.js');
var templates = require('./template_list.js');
var AuthDecoder = require('authdecoder');
var adc = AuthDecoder.middleware;
router.get ('/campaigns/qs', qsc, adc, cv, cm.createCampaign); // Creates a new campaign using query string params
router.post ('/campaigns/b/xml', xmlc, adc, cv, cm.createCampaign); // Creates a new campaign using xml payload
router.post ('/campaigns/file', fc, adc, cv, cm.createCampaign); // Creates a new campaign using uploaded file
router.post ('/campaigns/file/type2', fc2, adc, cv, cm.createCampaign); // Creates a new campaign using uploaded file
router.post ('/campaigns/js', jsc, adc,cv, cm.createCampaign); // Creates a new bulk campaign using json payload
router.get ('/campaigns/', adc, cm.getCampaigns); // Returns a list of bulk campaigns accessible for the invoking user.
router.get ('/campaigns/date', adc, cm.getCampaignsByRange); // Returns a list of bulk campaigns accessible for the invoking user.
router.get ('/campaigns/b/:id', adc, cm.getCampaign); // returns the details of a specific campoaign
router.put ('/campaigns/b/id', cm.updateCampaign); // Updates a specified campaign
router.delete ('/campaigns/b/:id', cm.deleteCampaign); // deletes a campaign
router.post ('/pause', adc, cm.pauseJob); // Pauses an ongoing campaign
router.post ('/resume', adc, cm.resumeJob); // Resumes a paused campaign
router.post ('/cancel', adc,cm.cancelJob); // Resumes a paused campaign
//router.post ('/reschedule', adc,cm.rescheduleCampaign); // Resumes a paused campaign
//router.get ('/', cm.pingCampaignServer);
router.get ('/templates', adc, templates.listTemplates); // gives the campaign server templates
Salient points:
All campaign functions, go into another js file (which is a middleware), called campaign_manager.js
All payload converters go into a sub-folder called "converters", and this folder has many files called 'qs_converter.js', 'json_converter.js', ..., blah blah.
My app performs authentication, before any campaign request is accepted, so there is a middleware loaded into the route.
If I have other mount points apart from "/campaigns", I would create another router module and define additional .js files.
_ I would use index.js (or app.js), to load these routers.
Essentially, it is very easy for me to scale my code into multiple modules and import them via the router file.
EDIT
Added usage scenario
Index.js
var routes = require('./campaign_router.js');
...
app.use('/campaignservice', routes);
I have my own rest API, that internally calls an NLP API, for which I have to post some data on their URL.
I am using needle to achieve this, but there's some error that I cannot catch, and my own api is returning 500 to the frontend.
Here is that part of my server.js code:
app.post('/api/get',function(req,res) {
//console.log(req);
console.log("here in post ");
if(!req.body){
return res.send(400);
}
//console.log(req.body.msg);
var searchQuery = req.body.msg;
var options = { 'api-key' : '3080a0e0-1111-11e5-a409-7159d0ac8188' };
needle.post('http://api.cortical.io:80/rest/text/keywords?retina_name=en_associative',searchQuery,options,function(err, resp){
if(err){
console.log('something went wrong :' + err);
}
console.log('Got :'+resp );
});
I reach here in post every time, but nothing after that. I am also curious that is this correct way to specify my api-key for the external API.
Thanks.
if you are using express 4.x, I am not sure whether you configured your express server but you need install body-parser and add the following lines in your express configuration:
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
If you are using express 3.x version, you don't need to install body-parser:
var express = require('express');
var app = express();
app.use(express.json());
app.use(express.urlencoded());
Now regarding your post route I edited it a little bit:
var config = require('./config');
app.post('/api/get', function (req, res) {
var searchQuery = {
q: req.body.msg
};
var NEEDLE_API_KEY = config.needle.API_KEY;
var NEEDLE_URL = config.needle.URL;
var options = {
'api-key': NEEDLE_API_KEY
};
needle.post(NEEDLE_URL, searchQuery, options, function (err, needleResponse) {
console.log(err || needleResponse.body);
res.json(err || needleResponse.body);
});
});
So I added a new file called config.js for purposes of having a reference for all your api keys, urls of your third party services.
module.exports = {
needle: {
API_KEY: process.env.NEEDLE_API_KEY,
URL: 'http://api.cortical.io:80/rest/text/keywords?retina_name=en_associative'
}
};
So when you run your server at the console, you should pass setting a value to your global environment variable called NEEDLE_API_KEY:
NEEDLE_API_KEY=666 node app.js
So in this way you are not saving any keys on your source code, you are saving keys in global environment variables that available only on the server machine.
I have set set my node.js server, using express 3.1.4 througth https. I am trying to update and lookup my session object (using sessionStore) directly with socket.io. I used extended exemple and explanation for the handshake authorisation process. But after is set the authorisation process, socket.io suddently stop to start connections.
I made research and tried many solution, including redis, mongoStore and session.socket.io, but i seem to always end up with socket not connecting, i am pretty sure its related to beeing in https.
Does anyone have an idea why this setup does not work ? If we get rid of io.set , socket suddently restart connecting ..
Thank you in advance !
var io = require('socket.io'),
https = require('https'),
express = require('express');
var path = require('path');
var express = require('express');
var http = require('http');
var mongoose = require('mongoose');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var fs = require('fs');
var passphrase = "";
var options = {
key: fs.readFileSync('var/keys/server.key'),
cert: fs.readFileSync('var/keys/server.crt')
};
if(passphrase) {
options.passphrase = passphrase;
}
// We define the key of the cookie containing the Express SID
var EXPRESS_SID_KEY = 'express.sid';
// We define a secret string used to crypt the cookies sent by Express
var COOKIE_SECRET = 'some secret';
var cookieParser = express.cookieParser(COOKIE_SECRET);
// Create a new store in memory for the Express sessions
var sessionStore = new express.session.MemoryStore();
var app = express();
// Configure Express app with :
// * Cookie Parser created above
// * Configure Session Store
app.configure(function () {
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.set('view options', { layout: false });
app.use(express.logger());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(cookieParser);
app.use(express.session({
store: sessionStore,
cookie: {
httpOnly: true
},
key: EXPRESS_SID_KEY
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
app.use("/app", express.static(__dirname + "/app"));
});
//passport config
var Account = require('./models/account');
passport.use(new LocalStrategy(Account.authenticate()));
passport.serializeUser(Account.serializeUser());
passport.deserializeUser(Account.deserializeUser());
//mongoose
mongoose.connect('mongodb://localhost/passport_local_mongoose');
// Configture routes
require('./routes')(app);
// Create https server, register socket.io as listener
server = https.createServer(options, app);
app.set('port', process.env.PORT || 3000);
console.log(("Express server listening on port " + app.get('port')));
io = io.listen(server);
// We configure the socket.io authorization handler (handshake)
io.set('authorization', function (data, callback) {
if(!data.headers.cookie) {
return callback('No cookie transmitted.', false);
}
cookieParser(data, {}, function(parseErr) {
if(parseErr) { return callback('Error parsing cookies.', false); }
// Get the SID cookie
var sidCookie = (data.secureCookies && data.secureCookies[EXPRESS_SID_KEY]) ||
(data.signedCookies && data.signedCookies[EXPRESS_SID_KEY]) ||
(data.cookies && data.cookies[EXPRESS_SID_KEY]);
// Then we just need to load the session from the Express Session Store
sessionStore.load(sidCookie, function(err, session) {
// And last, we check if the used has a valid session and if he is logged in
if (err || !session || session.isLogged !== true) {
callback('Not logged in.', false);
} else {
// You can access it later with "socket.handshake.session"
data.session = session;
callback(null, true);
}
});
});
});
io.on('connection', function (socket) {
console.log("socket.io started on port"+ app.get('port'));
socket.on("request",function(data){
console.log("socket answer = "+ data);
socket.emit("response", [ "docA" ,"docB"]);
});
socket.on("save",function(data){
console.log("socket save = "+ data);
memory += data + " ";
console.log("memory = "+ memory);
});
});
server.listen(3000);
Okay, well i found a solution to my problem. I still do not know why the socket handshake does not work properly in https. I decided to ude connect-mongoose with MongoDb. It works perfectly in https and can be used outside of req res function call, as needed.
I also can use it along my socket.on, so it ruled out my problem.
I have a node.js application and I am stuck getting an error message when I try to load the homepage. I will do my best at laying out my architecture below. It goes index.js --> server.js --> router.js --> requestHandlers.js
I am using a combination of express (www.expressjs.com) and nodebeginner.org. Sorry for the long question.. just wanted to get as much information down as possible.
index.js (creates handle object that contains pathname/requesthandler information, calls function to start server) I start with router.route here and pass it along each step
var server = require("./server");
var router = require('./router');
var requestHandlers = require('./requestHandlers');
// Routes
var handle = {}
handle['/'] = requestHandlers.home;
server.start(router.route, handle)
server.js (starts the server, THIS IS WHERE I WANT TO CONFIGURE THE SERVER, gets a the pathname from the URL, and passes it on to the route module)
var http = require("http");
var url = require('url');
var express = require('express');
function start (route, handle) {
var onRequest = function(request, res) {
var pathname = url.parse(request.url).pathname;
console.log("request for " + pathname + " recieved.");
route(handle, pathname, res);
}
var app = express.createServer(onRequest).listen(8888);
if (app.configure) {
console.log('app exists'); //logging correctly
}
// Configuration
app.configure(function(){
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(__dirname + '/public'));
});
app.configure('development', function(){
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
app.use(express.errorHandler());
});
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env); //logs correct with 8888 and development
}
exports.start = start;
router.js (the function route passed from index --> server which calls the route function in router.js, calls the requestHandler that matches the pathname in handle object)
function route (handle, pathname, res) {
console.log("About to route a request for" + pathname); //About to route a request for/stylesheets/style.css SEE BELOW******* this is the error
if (typeof handle[pathname] === 'function') {
handle[pathname] (res);
}
else {
console.log('no request handler found for' + pathname);
}
}
exports.route = route;
requestHandler.js (interacts with the res/req objects, functions are mapped to certain pathnames, only called when those pathnames are requested thanks to the router)
var home = function(res){
res.render('index', { title: 'WWYB?' });
console.log('homepage rendered'); //correctly logs for pathname = '/'
//click();
};
exports.home = home;
***when i go to request localhost:8888 it tries to make a bunch of requests. first it requests "/" correctly but then keeps going through logging everything saying "About to route a request for/stylesheets/style.css" Eventually the page loads with no css. The pathname as indicated in my layout.jade file is exactly that '/stylesheets/style.css'.
Why is the pathname ever evaluating to /stylesheets/style.css? I think node is doing something in the background and I dont fully understand it.
Let me know if you need more info. Thanks!
Like #TJHolowaychuk commented you really should check the manual, and follow a bunch of tutorials. Anyway, I'll try to help you a bit.
The is a very basic explanation. Express allows you to use sub applications, so you can have different part of you application in different files. It has it's own router too. If you need to do something with the request and/or the response before the route is processed, you can create a middleware. If you want you configurations in a different module, then return a function in it.
So an example of server.js :
var $express = require('express'),
app = module.exports = $express.createServer(),
subapp = require('./subapp'),
configure = require('./configure');
// Each of our own configure returns a function that will be
// called by app.configure
app.configure(configure.all(app));
app.configure('development', configure.devel(app));
app.configure('production', configure.prod(app));
// Use our sub application
app.use(subapp);
// Now listen
app.listen(3030)
subapp.js :
var $express = require('express'),
subapp = module.exports = $express.createServer();
// Add a handler for GET / to our sub-application
subapp.get('/', function (req, res) {
res.end('Hello world!');
});
Finally configure.js :
var $express = require('express');
exports.all = function (app) {
return function () {
// Global configurations
app.use($express.bodyParser());
app.use($express.methodOverride());
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use($express.static(__dirname + '/public'));
//...
// If you want to do something with/on the request/response
// you can create a middleware
app.use(function (req, res, next) {
console.log('caught request %s %s', req.method, req.path);
// Don't forget the callback
next();
});
};
};
exports.devel = function (app) {
return function () {
// Development configurations
};
};
//...
Go to localhost:3030 with your favorite browser, it displays "Hello world!", this is our request handler. If you look at the terminal, you'll see "caught request GET /", this is our middleware.
Your stylesheets, client-side javascripts, etc. should be in /public. app.use(express.static(__dirname + '/public')) will serve these.
Let's say you have /public/stylesheets/all.css, then in your jade template you can include it like this link(rel='stylesheet', href='/public/stylesheets/all.css')
Now, you'll have to experiment and learn more about node and express before even thinking to deploy something to production, these websites may help you:
howtonode
nodetuts
Hope this tiny-micro-tut helps you.
woah this is pretty confusing, you might want to start with the app express(1) can generate for you