René van Mil

Publishing an External API With Meteor

Recently I’ve been playing Minecraft together with a couple of colleagues and we decided to build a system which, among other things, provides us with statistics of our world.
My task is to build the web interface, so I decided to make this my first project using the Meteor platform, since it has been on my list of stuff I want to learn for a while now.

The statistics data I need to render does not come from a local database but from an external API, so I had to figure out how to publish a Meteor record set with the external API as its source. This blog post describes how I got this to work, using a simple demo application and the iTunes API as the external API.

Summary

The Meteor app will have the server poll the iTunes api every 10 minutes and publish the results as a record set, which will then automatically update on all connected clients.

The example Meteor app created for this blog post can be found over here.

Step 1 - Create the Meteor app

Make sure you have Meteor installed and run the following commands from the folder in which you want to create the demo app.

Create Meteor App
1
2
3
4
5
6
7
meteor create demo
cd demo
meteor remove autopublish
meteor remove insecure
meteor add http
meteor add twbs:bootstrap
meteor add percolate:synced-cron

This will create a new Meteor app, remove the autopublish and insecure packages for security, and add the following packages:

  • http: required to communicate with the iTunes api
  • twbs:bootstrap: Bootstrap to add some style to our app
  • percolate:synced-cron: required to poll the iTunes api

Step 2 - Define a songs collection

Defining a collection with Meteor is done with just one line of code. The collections.js file is in its own folder, which will make sure the collection is defined on both the client and the server. This collection will store the iTunes api data and is published as a record set to the Meteor clients.

collections.js
1
TopSongs = new Mongo.Collection('topsongs');

Step 3 - Server scripts

In the application.js file a job which fetches the top songs from the iTunes api every 10 minutes is defined. The Meteor.startup function is used to schedule the job.

application.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Setup cron jobs
SyncedCron.options.collectionName = 'cronjobs';

SyncedCron.add({
    name: 'iTunes topsongs',
    schedule: function(parser) {
        return parser.text('every 10 minutes'); // parser is a later.parse object
    },
    job: function() {
        Meteor.call('updateTopSongs');
    }
});

// Startup
Meteor.startup(function() {
    // Start jobs
    SyncedCron.start();
});

The top songs collection must be published to the clients, so they are able to subscribe to its updates.

publications.js
1
2
3
Meteor.publish('topsongs', function() {
    return TopSongs.find();
});

Fetching the data from the iTunes api and storing the results inside the topsongs collection is done with the updateTopSongs function. It is defined with Meteor.methods, so it is possible to call this method from the client as well (for instance, in case you would like to trigger an update every time a new client subscribes to the top songs collection).

This function is also called by the job which is scheduled to fetch the data from the iTunes api.

The actual data from iTunes is not directly stored inside the topsongs collection, but it is parsed and only some parts (id, title, image, link and preview) are stored. A sort field is added to maintain the order of the top songs since this is not specified by the data returned by the iTunes api.

methods.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Meteor.methods({

    updateTopSongs: function() {
        // Get top songs from iTunes server
        HTTP.get('https://itunes.apple.com/nl/rss/topsongs/limit=10/json', function(error, topsongsResponse) {
            var topsongs = [];
            var entries = topsongsResponse.data.feed.entry;
            var sort = 0;
            _.each(entries, function(entry) {
                var topsong = {};
                topsong._id = entry.id.attributes['im:id'];
                topsong.sort = sort;
                topsong.title = entry.title.label;
                topsong.image = entry['im:image'][0].label;
                topsong.link = entry.link[0].attributes.href;
                topsong.preview = entry.link[1].attributes.href;
                // Add song to list
                topsongs.push(topsong);
                // Increase sort
                sort++;
            }, this);

            // Delete all existing songs from database
            TopSongs.find().forEach(function(topsong) {
                TopSongs.remove(topsong._id);
            });

            // Insert new songs into database
            _.each(topsongs, function(topsong) {
                TopSongs.upsert(topsong._id, topsong);
            }, this);
        });
    }

});

Step 4 - Client scripts

In the application.js file the client subscribed to the top songs collection.

application.js
1
2
3
4
5
6
7
// Startup
Meteor.startup(function() {
    // Nothing to do here
});

// Subscriptions
TopSongsSub = Meteor.subscribe('topsongs');

The topsongs helper function is defined to be used by all templates. It returns the entire top songs collection, sorted by the sort field.

helpers.js
1
2
3
4
// Global helpers
Template.registerHelper('topsongs', function() {
    return TopSongs.find({}, { sort: ['sort'] });
});

Step 5 - Template

For this simple app we use only a single html file, containing two templates. The topsongs template renders a list for each record from the top songs collection. The topsong template renders a single record.

The result is a sorted list which contains the song title, a link to the song on iTunes, an image and an audio control to play the preview.

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<body>
    {{> topsongs}}
</body>

<template name="topsongs">
    <div class="container">
        <div class="row">
            <div class="col-md-12">
                <div class="panel panel-info">
                    <div class="panel-heading">
                        <h3 class="panel-title">Top Songs</h3>
                    </div>
                    <ul class="list-group">
                        {{#each topsongs}}
                        {{> topsong}}
                        {{/each}}
                    </ul>
                </div>
            </div>
        </div>
    </div>
</template>

<template name="topsong">
    <li class="list-group-item">
        <div class="row">
            <div class="col-md-6">
                <img src="{{image}}"> <a href="{{link}}" target="_blank">{{title}}</a>
            </div>
            <div class="col-md-6">
                <audio controls preload="none" style="width:200px;" class="pull-right">
                    <source src="{{preview}}" type="audio/mp4">
                    </audio>
                </div>
            </div>
        </li>
</template>

Step 6 - Run the app

Run the app locally with the meteor command and open it at http://localhost:3000

Run Meteor App
1
meteor

Comments

Real Time Web Analytics