I want to submit a request from my Meteor client to the server, that has the server make an HTTP request to a website, and then return the response to the client.
On a REST web server, I would make an HTTP GET from the client to the server, which would then make its own request and respond to the client.
I haven't added a REST interface to my Meteor app and don't want to add this overhead for just this one need. However, using collections to get this done is unweildy and not the right tool.
Is there any way for the Meteor client to securely ask the server to do something and get a response without using collections? I'm messing with meteor Methods such as:
Meteor.methods({
'/http/get'(name, cbk) {
cbk = cbk || function() {};
HTTP.get('http://www.google.com', {}, (err, data) => {
cbk(err, data);
});
},
});
However this isn't seeming to work. The call is being made on the Client side.
This is exactly what Meteor methods are for.
Meteor methods docs
Server
First define your method on your server:
Meteor.methods({
// Namespace for clarity
'make.rest_call'(callback) {
HTTP.get('http://www.google.com', {}, (err, data) => {
callback(err, data);
});
}
});
OR
If you need the client to do something with the data then return a promise here (promise docs)
Meteor.methods({
// Namespace for clarity
'make.rest_call'(callback) {
return new Promise((resolve, reject) => {
HTTP.get('http://www.google.com', {}, (err, data) => {
if (err) { reject(err); }
resolve(data);
});
}
}
});
Client
Then call it from your client:
// Simple call (just makes the call, does nothing on the client)
Meteor.call('make.rest_call');
OR
// Promise based call
Meteor.call('make.rest_call', (error, result) => {
if (error) { /* do something with error */ }
// result contains your promise.
result.then((data) => {
// do something with returned data
});
});
Related
Axios DELETE works when I send a request through postman but on my react app it doesn't. I'm passing the _id that MongoDB assigns the entry. I'm initiating ObjectId and it still doesn't work. I also double checked if I was using the correct route, which I was.
In my app I have click function that calls SaveBook. That part I feel okay about. Let me know if I need to share something else.
SaveBook in AuthActions.js on the front end
export const saveBook = ({books, user, book, _id}) => {
return function () {
console.log(`This is id ${JSON.stringify(_id)}`)
const savedIndex = books.indexOf(book);
if (savedIndex >= 0) {
console.log(savedIndex)
axios
.delete("/api/users/wishlist", {_id})
} else {
console.log(savedIndex)
// console.log(`Adding ${book.book.title} to faves...`);
axios
.post("/api/users/dashboard", {book, user})
.then(console.log("success"))
.catch (err =>
json(err)
);
}
}
};
In users.js the delete operation on the server side
router.delete('/wishlist', (req, res) => {
const db = mongoUtil.getDb();
db.db("mern-auth-2").collection("savedbooks")
.deleteOne({_id:ObjectId(req.body._id)})
.then(res.json(res.data))
});
I realized req.body wasn't the correct choice for the Delete method and used url params/ req.params to send the _id. This works well.
Fixed this line in authActions.js
axios
.delete("/api/users/wishlist/" + _id,)
Fixed these few lines in Users.js
router.delete('/wishlist/:id', (req, res) => {
const db = mongoUtil.getDb();
db.db("mern-auth-2").collection("savedbooks")
// .deleteOne({_id:ObjectId(req.body._id)})
.deleteOne({_id:ObjectId(req.params.id)})
Is it possible to send a redirect from server side to client in Meteor framework.
I can't find out this functionality.
Is so, could you please show an example.
There are two ways to communicate between your client side and your server side:
Collections
Meteor.methods
Let's assume you have:
- FlowRouter
- Blaze
- FlowRouter Blaze
- autopublish
Using collections
You can do the following:
Collection file /collections/Events.js:
// Create a global collection
Events = new Meteor.Collection("Events");
Somewhere on the server side:
Events.insert({
type: 'redirect',
route: 'home'
// require user_id if you want this to work with multi users
params: {}
});
Client side route:
FlowRouter.router('/some/url', {
action: function () {
BlazeLayout.render('Layout', {main: 'displayedTemplate'});
},
name: 'home'
});
Client side layout:
Template.Layout.onCreated(function () {
// suscriptions to Events if autopublish have been removed...?
this.autorun(function() {
const event = Events.findOne();
switch (event.type) {
case 'redirect':
FlowRouter.go(event.route, event.param);
Events.delete(event);
...
}
});
});
Using methods
When the user does something and you want to redirect him:
Template.Layout.events({
'click h2': function (event, template) {
Meteor.call('getCoffee', function (err, event) {
if (err) {
//handle error
} else {
if (event.type === 'redirect') {
FlowRouter.go(event.route, event.param);
}
}
});
}
})
my sails.js app is embedded in php project where php generate some date for sails model. Can't find the way to fetch this date in beforeCreate callback or some custom method. I don't want use db to sync 2 models (model from sails & model from php). And i need to send some date to remote php app.
sails.js v.0.9.x
here is my controller:
module.exports = {
index: function (req, res) {},
create: function (req, res) {
if ( !req.param('login') )
throw new Error('Login is undefined');
if ( !req.param('message') )
throw new Error('Initial message is undefined');
var user, thread;
User.create({
id: 1,
name: req.param('login'),
ip: req.ip
}).done( function (err, model) {
user = model;
if (err) {
return res.redirect('/500', err);
}
user.fetch(); // my custom method
});
return res.view({ thread: thread });
}
};
and model:
module.exports = {
attributes: {
id: 'integer',
name: 'string',
ip: 'string',
fetch: function (url) {
var app = sails.express.app;
// suggest this but unlucky :)
app.get('/path/to/other/loc', function (req, res, next) {
console.log(req, res)
return next()
});
}
}
};
UPD My solution
Model:
beforeCreate: function (values, next) {
var options = 'http://localhost:1337/test',
get = require('http').get;
get(options, function (res) {
res.on('data', function (data) {
_.extend( values, JSON.parse( data.toString() ) );
next();
});
}).on('error', function () {
throw new Error('Unable to fetch remote data');
next();
});
}
Yikes--app.get is nowhere near what you need. That's binding a route handler inside of your app and has nothing to do with requesting remote data. Don't do this.
The easiest way to fetch remote data in Node is using the Request library. First install it in your project directory using npm install request. Then at the top of your model file:
var request = require('request');
and in your fetch method:
fetch: function(url, callback) {
request.get(url, function(error, response, body) {
return callback(error, body);
});
}
Note the added callback parameter for fetch; this is needed because the request is an asynchronous operation. That is, the call to fetch() will return immediately, but the request will take some time, and when it's done it will send the result back via the callback function. Seeing as fetch is at this point just a wrapper around request.get, I'm not sure why it's necessary to have it as a model method at all, but if the URL was based on something within the model instance then it would make sense.
I believe the approach to Sockets within sails.js has changed over the last several months. A little confused on what the best practice should be.
socket.get('/comment/subscribe', function () {
socket.on('comment', function(obj) {
if (obj.verb == 'created') {
var data = obj.data;
if (data.project == 1) {
// This adds it to a visual list.
addComment("data.content");
}
}
});
});
I believe this approach is deprecated. What is the best syntax socket subscription?
Also - am I able to subscribe to a particular criteria of the model, in this case a particular comment or other attribute?
By default when you request a model, you will be subscribed to it. That is the default if you are using blueprints. There is no need to explicitly subscribe.
However, you if you are making your own controller methods. You will need to subscribe socket requests to record updates manually on the server side.
Assuming you have a User model...
To have a socket be notified of all created events call User.watch(req)
For updated and destroyed call User.subscribe(req, records)
Here is a copy of my User controller for a project, I have put comments on the lines that handle subscriptions. hope it helps....
UserController = {
find: (req, res) ->
User.watch(req) // Subscription
if req.params.all().id
query = User.findOne(id: req.params('id'))
else
query = User.find()
query.exec((err, records) =>
return res.send(500, err) if (err)
User.subscribe(req, records) // Subscription
return res.json(records)
)
create: (req, res) ->
User.create(req.body).exec((err, record) =>
return res.send(500, err) if (err)
User.publishCreate(record, record.toJSON()) // Subscription
return res.json(record.toJSON())
)
update: (req, res) ->
User.update(req.params('id'), req.body).exec((err, records) =>
return res.send(500, err) if (err)
return res.notFound() if _.isEmpty(records)
record = _.first(records)
User.publishUpdate(record.id, record.toJSON()) // Subscription
return res.json(record.toJSON())
)
destroy: (req, res) ->
User.destroy(req.params('id')).exec((err, records) =>
return res.send(500, err) if (err)
return res.notFound() if _.isEmpty(records)
record = _.first(records)
User.publishDestroy(record.id) // Subscription
return res.send(204, null)
)
me: (req, res) ->
User.findOne(
id: req.session.user_id
).done((err, user) =>
User.subscribe(req, user) // Subscription
return res.json(user)
)
}
module.exports = UserController
Assuming you want to get some data from the server via socket the code should look something like this on frontend
socket.get('/comment/subscribe', function serverResponse(data) {
// use data
})
on server side in commentController
subscribe: function(req, res) {
//some logic
res.json({
data: 'some random data'
});
}
it depends on what exactly you want to do but this is the basic structure. Also note that sails uses blueprints which is a way to make some basic CRUD routes available without coding them in the controller More info here: http://irlnathan.github.io/sailscasts/blog/2014/01/17/sailscasts-answers-ep8-how-do-blueprint-actions-and-blueprint-routes-work-in-sails/
Regarding the part of the code in your question that starts with socket.on this is an event listener on the client side listening for server to send some data. For example
socket.on('room', function messageReceived(message) {
switch (message.verb) {
case 'created':
addRoom(message.data);
break;
case 'addedTo':
postStatusMessage('room-messages-'+message.id, $('#user-'+message.addedId).text()+' has joined');
increaseRoomCount(message.id);
break;
case 'removedFrom':
postStatusMessage('room-messages-'+message.id, $('#user-'+message.removedId).text()+' has left');
decreaseRoomCount(message.id);
break;
case 'destroyed':
removeRoom(message.id);
break;
case 'messaged':
receiveRoomMessage(message.data);
default:
break;
}
});
I'm using the Node.JS driver for MongoDB, and I'd like to perform a synchronous query, like such:
function getAThing()
{
var db = new mongo.Db("mydatabase", server, {});
db.open(function(err, db)
{
db.authenticate("myuser", "mypassword", function(err, success)
{
if (success)
{
db.collection("Things", function(err, collection)
{
collection.findOne({ name : "bob"}, function(err, thing)
{
return thing;
});
});
}
});
});
}
The problem is, db.open is an asychronous call (it doesn't block), so the getAThing returns "undefined" and I want it to return the results of the query. I'm sure I could some sort of blocking mechanism, but I'd like to know the right way to do something like this.
ES 6 (Node 8+)
You can utilize async/await
await operator pauses the execution of asynchronous function until the Promise is resolved and returns the value.
This way your code will work in synchronous way:
const query = MySchema.findOne({ name: /tester/gi });
const userData = await query.exec();
console.log(userData)
Older Solution - June 2013 ;)
Now the Mongo Sync is available, this is the right way to make a synchronous MongoDB query in Node.js.
I am using this for the same. You can just write sync method like below:
var Server = require("mongo-sync").Server;
var server = new Server('127.0.0.1');
var result = server.db("testdb").getCollection("testCollection").find().toArray();
console.log(result);
Note: Its dependent on the node-fiber and some issues are there with it on windows 8.
Happy coding :)
There's no way to make this synchronous w/o some sort of terrible hack. The right way is to have getAThing accept a callback function as a parameter and then call that function once thing is available.
function getAThing(callback)
{
var db = new mongo.Db("mydatabase", server, {});
db.open(function(err, db)
{
db.authenticate("myuser", "mypassword", function(err, success)
{
if (success)
{
db.collection("Things", function(err, collection)
{
collection.findOne({ name : "bob"}, function(err, thing)
{
db.close();
callback(err, thing);
});
});
}
});
});
}
Node 7.6+ Update
async/await now provides a way of coding in a synchronous style when using asynchronous APIs that return promises (like the native MongoDB driver does).
Using this approach, the above method can be written as:
async function getAThing() {
let db = await mongodb.MongoClient.connect('mongodb://server/mydatabase');
if (await db.authenticate("myuser", "mypassword")) {
let thing = await db.collection("Things").findOne({ name: "bob" });
await db.close();
return thing;
}
}
Which you can then call from another async function as let thing = await getAThing();.
However, it's worth noting that MongoClient provides a connection pool, so you shouldn't be opening and closing it within this method. Instead, call MongoClient.connect during your app startup and then simplify your method to:
async function getAThing() {
return db.collection("Things").findOne({ name: "bob" });
}
Note that we don't call await within the method, instead directly returning the promise that's returned by findOne.
While it's not strictly synchronous, a pattern I've repeatedly adopted and found very useful is to use co and promisify yield on asynchronous functions. For mongo, you could rewrite the above:
var query = co( function* () {
var db = new mongo.Db("mydatabase", server, {});
db = promisify.object( db );
db = yield db.open();
yield db.authenticate("myuser", "mypassword");
var collection = yield db.collection("Things");
return yield collection.findOne( { name : "bob"} );
});
query.then( result => {
} ).catch( err => {
} );
This means:
You can write "synchronous"-like code with any asynchronous library
Errors are thrown from the callbacks, meaning you don't need the success check
You can pass the result as a promise to any other piece of code