Notifying client when data comes via REST

I have a 3rd party software that sends data via POST to server-side REST route handled by iron router.

Then I put that data into DB and that’s all great.

Problematic part is how to provide that data to client, to notify client-side whenever new data comes so appropriate updates are performed.

I’ve tried with getting last document from database and publish & subscribe but client isn’t reacting to anything.

To make a note, on client side I’m recalculating D3 charts so it isn’t usual populating values into template.

I’ve read many articles and guides but I haven’t seen any example like that.

It sounds like you’ve done all the right things. Basically, as long as your client is subscribed to the collection(s) with your POST data, it will be getting updates as they happen.

Recalculating D3 charts is straightforward, so I think we need to see some code:

  • your publication (unless you’re still using autopublish).
  • your template and its related code (or your react components if you’re using react).

OK, here it is server side code:

Meteor.startup(() => {
  // code to run on server at startup

  const Users = new Mongo.Collection('users');

  Router.route('/test', {where: 'server'})
    .post(function () {
    	var req = this.request;
      	var res = this.response;
  	var data = JSON.parse(req.body);
  	
  	Users.insert({
  		name: data.name,
  		lastname: data.lastname,
  		age: data.age
  	});	
    });

    Meteor.publish("users", function(){
       return Users.find().limit(1); 
    });
});

And client side code:

const handle = Meteor.subscribe("users", function() {
    console.log("Data is ready"); // never triggered
});

Template.chart.onRendered(function () {
    Meteor.call('chart.update');
});

Is that sufficient?

Should be quite fine, the idea is good. What seems risky is that limit(1), I would remove that to test.

Server: This code does not have to be in a startup function, it will work it’s way in the loading order by itself. Also that collection: You need that collection in both server and client, so you need to share it.

Doesn’t this conflict with the Meteor.users() publication? Test with another name to see how that goes. Meteor by default publishes the current user (I assume you use accounts) so there might be an issue.

But foremost: You need a find() call on your client. Put in onRendered:
console.log(Users.find().fetch()) to see is you get data in.

1 Like
  1. Your comment suggests that const Users = new Mongo.Collection('users'); is only on the server (I don’t see any mention of this on the client). For publications to work you need this declaration in your client code and your server code. You can do that either by putting that line in a file within the lib/ folder (meteor classic) OR by putting it into a file somewhere within imports/ and then importing the name where you need it (but still in both client and server code):
// in imports/path/to/Users.js:
export const Users = new Mongo.Collection('users');

// in a server-side file and in a client-side file:
import { Users } from '/imports/path/to/Users`;
  1. As @lucfranken has indicated your choice of collection name will likely lead to confusion if nothing else. It may just be better to pick a name which isn’t baked in to Meteor. Nothing shown here needs to be (or should be) in a Meteor.startup().

  2. The syntax for your find should look like return Users.find({}, {limit: 1});: http://docs.meteor.com/api/collections.html#Mongo-Collection-find - although like @lucfranken, I suspect this will always return the same document - so no reactive changes.

  3. It’s much better to put your subscription into a template, because cleaning up is done for you, so:

Template.chart.onCreated(function () {
  this.subscribe("users");
});
  1. Your onRendered should not perform a Meteor.call - you are getting updates automatically through your subscription. However, this is where you would normally put your chart rendering code. So your chart template should contain a suitable <div> for rendering into and this must NOT be in a reactive part of the template. I like to keep things really simple here:
<template name="chart">
  <div id="mychart"></div>
</template>

Then you need to code your D3 within an autorun. The onRendered is not reactive by default, so you need to make it so:

Template.chart.onRendered(function() {
  this.autorun(() => {
    // Generate your chart here - data comes from Users.find(). It will re-run whenever the collection data is updated
  });
});
2 Likes

OK, your instructions are very clear and it worked! Thank both you and @lucfranken sooo much.

The most important thing is that everything is much clearer and makes much more sense overall.

1 Like