JavaScript
Article

Build a Real-Time Photo Update App with the Instagram API

By Wern Ancheta

HEADS UP! Instagram deprecated Realtime subscriptions for tags on Nov 17, 2015 so it’s no longer possible to follow along with this tutorial.

This article was peer reviewed by Jamie Shields, Edwin Reynoso and Tom Greco. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

Instagram is an online photo sharing network that enables its users to take pictures and videos, and share them on a variety of social networking platforms. Users can personalize each photo that they upload (for example by adding various effects) and categorize them with a hashtag.

In this tutorial we’ll be building a real-time photo update app. This will allow users to enter a hashtag to which they would like to subscribe. Then, every time a new photo with that hashtag is posted to Instagram, it will be sent to our app which will display it accordingly.

We’ll implement our app in Node.js using Express as the framework. The real-time part will be implemented using Instagram’s Real-time Photo Updates and Socket.io. As ever, the code for this tutorial is available on our Github repo.

How the Real-time Photo Updates Work

Instagram’s Real-time Photo Updates work by sending notifications to your server every time a new photo is posted to the subscription that you selected. Here’s a break down:

  1. First, your server sends a subscription request to Instagram.
  2. Instagram receives the request and verifies that you really want to subscribe by sending back a response which your server has to send back.
  3. Your server receives the data and sends it back to Instagram.
  4. If the data is the same, Instagram will start sending notifications to your server.

There are four types of subscriptions from which you receive real-time updates : users, tags, locations and geographies. You can read more about each of these in the documentation. In this tutorial, we’re only going to use the tags subscription. This allows you to receive notifications when a new photo is tagged with whichever tags you specify.

Register an App

The first thing that we need to do is to create an Instagram account then register as a developer.

Next, we need to register a new Instagram app. You can put any valid URL for the website and Redirect URL as they are not needed for our app to function properly.

Screenshot of registering a new client to use the Instagram API

Once the app is created, take note of the CLIENT ID and CLIENT SECRET as they will be needed later when making requests to the Instagram API.

The Server-side setup

The next thing to do is clone the repo and install the dependencies using npm.

git clone git@github.com:sitepoint-editors/express-instagramrealtime.git
cd express-instagramrealtime
npm install

This will pull in the following dependencies:

  • Express is the de facto standard web application server framework for Node.js. It is used for serving the public-facing side of the app as well as receiving photo notifications from Instagram.
  • Express Handlebars is used for implementing views in Express.js.
  • body-parser is used for parsing the form data submitted by the user. In this case, the data is the tag that the user wants to subscribe to.
  • instagram-node-lib is a Node.js library for working with the Instagram API. Once we have the tag provided by the user, this library is used to subscribe to the tag.
  • socket.io — after subscribing to a specific tag, Instagram sends notifications to the server every time a new photo is posted with the tag that we used. This is where socket.io comes in. It is used for sending the photo data to the front-end every time a new notification is received by the server.
  • moment is used for formatting the timestamp provided by the Instagram API.

Now we’re ready to look at the app. In app.js we first need to require the dependencies we installed.

var express = require('express');
var exphbs  = require('express-handlebars');
var moment = require('moment');
var bodyParser = require('body-parser');
var instagram = require('instagram-node-lib');
var app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// set the file name of the default layout
app.engine('handlebars', exphbs({defaultLayout: 'main'}));

// set the expressJS view engine to handlebars
app.set('view engine', 'handlebars');

// set the path to the front-end assets
app.use(express.static('public'));

Now, that we have required the necessary dependencies, we need to set the Instagram CLIENT ID and CLIENT SECRET. You will replace these values, with the values generated when you registered your app.

var instagram_client_id = 'YOUR-INSTAGRAM-CLIENT-ID';
var instagram_client_secret = 'YOUR-INSTAGRAM-CLIENT-SECRET';

instagram.set('client_id', instagram_client_id);
instagram.set('client_secret', instagram_client_secret);

Once we have our ID and Secret configured, the next thing we are going to do is create a server instance that will run on port 4000. You can check if the server has started running by using console.log to output the host and port.

var server = app.listen(4000, function(){
  var host = server.address().address
  var port = server.address().port

  console.log('Example app listening at http://%s:%s', host, port)
});

Next, have socket.io listen to the Express server. This binds socket.io to the same port as your Express server, so that later on you can use port 4000 when you connect to this socket on the client side.

var io = require('socket.io').listen(server);

Now let’s move on to creating a new route for the home page of the app. All it does is render the home template.

app.get('/', function(req, res){
  res.render('home');
});

Creating the Views

According to the default handlebars configuration, all view files should be stored in the views directory. The file home.handlebars will render the form controls into which the user will enter the hashtag that will be used for the real-time photo updates:

<div id="form-wrapper">
  <div class="form-group">
    <label for="tag" class="control-label">Hashtag</label>
    <input type="text" class="form-control input-lg" id="tag" name="tag" autofocus>
  </div>

  <div class="form-group">
    <button id="start" class="btn btn-lg btn-block btn-primary">Start</button>
  </div>
</div>

<div id="results" class="hidden">
  <div class="row"></div>
</div>

Every time a new photo which has that hashtag is posted by any Instagram user, it will be immediately displayed by the app inside the div with the id of results.

Earlier on the app.js file, we set the file name for the default layout with the following code:

app.engine('handlebars', exphbs({defaultLayout: 'main'}));

In handlebars, layout files are stored in the views/layouts. In this directory the file main.handlebars serves as the main layout. The main content is rendered inside the div with the id of wrapper:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Real-time Photo Updates</title>

    <link rel="stylesheet" href="/css/bootstrap.min.css">
    <link rel="stylesheet" href="/css/style.css">
  </head>
  <body>
    <div id="wrapper">
    {{{body}}}
    </div>
    <script src="/js/jquery.min.js"></script>
    <script src="/js/jquery.backstretch.min.js"></script>
    <script src="/js/jquery.imagepreload.min.js"></script>
    <script src="/js/vague.min.js"></script>
    <script src="/js/socket.io.min.js"></script>
    <script src="/js/handlebars.min.js"></script>
    <script src="/js/moment.min.js"></script>
    <script src="/js/livestamp.min.js"></script>
    <script src="/js/script.js"></script>
  </body>
</html>

As you can see, a few front-end dependencies are required. Here’s a brief description of each:

  • Bootstrap is a front-end framework. This is my personal choice for almost every web thing that I build. If you also want to use Bootstrap, you can find free themes at bootswatch.com.
  • jQuery is used for manipulating HTML and listening for click events on the page. It’s also the dependency of the 3 libraries below.
  • jQuery BackStretch is used for making the current photo into a full page background image.
  • The jQuery Image Preload plugin is used for preloading the image before showing it to the user.
  • Livestamp helps keep the timeago text current when no new photo is received from the server side for too long.
  • Vague.js applies a blur filter to the background image.
  • handlebars is used for generating the HTML to be used for displaying photos.
  • moment is used for displaying timeago text based on the timestamp.

These dependencies can be found in public/js and public/css. This is because we specified that this folder (public) should contain our front-end assets:

app.use(express.static('public'));

You could also use bower to download and manage these assets. If you so chose, be sure to update the .bowerrc file so that it uses the static directory that you specified.

With that out of the way, we’re now ready to create the script.js file.

The Meat and Potatoes

Inside of the file script.js is where all of the front-end action happens. Here, we need to use jQuery’s $.get function to fetch the handlebars template from the server. Once it has been fetched, it then must be compiled and stored in a variable. This is the template used for constructing the HTML for displaying the photo.

var template;
$.get('/templates/row.hbs', function(data){
  template = Handlebars.compile(data);
}, 'html');

And this is what the handlebars template looks like (public/templates/row.hbs):

<div class="row">
  <div class="photo-container">
    <img src="{{image}}" class="photo">
  </div>

  <div class="photo-details">
    <div class="timestamp" data-livestamp="{{created_time}}">{{human_time created_time}}</div>
    <img src="{{profile_pic}}" class="userphoto" alt="{{user}}">
    <a href="http://instagram.com/{{user}}" target="_blank" class="username">{{user}}</a>
    <div class="caption">{{caption}}</div>
  </div>
</div>

This contains the code for displaying our application’s photos.

Back in script.js we need to connect to the socket.io server.

var socket = io.connect('http://your-server.com:4000');

And register a helper to convert Unix timestamps into a human friendly form:

Handlebars.registerHelper('human_time', function(timestamp){
  return moment.unix(timestamp).fromNow();
});

When the start button is clicked, we need to send the hashtag entered by the user to the server. Once this is successful, we want to hide the form, and show the photo container.

$('#start').click(function(){
  var tag = $('#tag').val();
  $.post(
    '/tag/subscribe',
    { 'tag': tag },
    function(response){
      if(response.type == 'success'){
        $('#form-wrapper').addClass('hidden');
        $('#results').removeClass('hidden');
      }
    }
  )
});

Back on the server-side (in app.js), our app needs to unsubscribe to all current real-time subscriptions, and then subscribe to the new hashtag that the user has provided. We can do this by making use of the subscribe method in the tags object provided by the instagram-node-lib library. Once the server has received a valid response from Instagram, we then send a response that the subscription has been completed.

var current_tag;

app.post('/tag/subscribe', function(req, res){
  current_tag = req.body.tag;
  console.log('current tag: ' + current_tag);

  instagram.tags.unsubscribe_all({
    complete: function(unsubscribe_data) {
      if(unsubscribe_data == null){
        console.log('unsubscribed from everything!');
        instagram.tags.subscribe({
          object_id: current_tag,
          callback_url: 'https://xxxxxxxx.ngrok.io/subscribe',
          complete: function(subscribe_data){
            if(subscribe_data){
              res.send({type: 'success'});
            }
          }
        });
      }
    }
  });
});

When Instagram receives your request to subscribe to a new tag, it sends a GET request your callback URL. This request contains a query parameter. All the server has to do is to send it back to Instagram for the verification to pass.

app.get('/subscribe', function(req, res){
  res.send(req.query['hub.challenge']);
});

Every time a new photo with that tag is posted on Instagram, it automatically sends a notification to your server. This time its a POST request to the callback_url that you specified (you will have to change this in the deployment section). Note that this request doesn’t contain any data about the posted photo. It only contains data about the time and the subscription from which it originated. That is why you have to make a separate request to get the recently posted photo. Once a response is returned, create a new object named photo and then store all the data that you want to return in a new variable. In this case, only the following is needed: username, profile photo, the timestamp with which the image was posted, the URL of the photo, and the caption text. Lastly, inform the client-side that a new photo is available.

app.post('/subscribe', function(req, res){
  instagram.tags.recent({
    name: current_tag,
    count: 1,
    complete: function(data){
      var photo = {
        'user': data[0].user.username,
        'profile_pic': data[0].caption.from.profile_picture,
        'created_time': data[0].created_time,
        'image': data[0].images.standard_resolution.url,
        'caption': data[0].caption.text
      };
      io.sockets.emit('new_photo', photo);
    }
  });
});

Displaying the Result

Going back to the client-side (script.js), let’s use the jQuery Image Preloader Plugin to preload the image when a new photo comes in. This will fully download the image on the client-side before showing it to the user. Once the image has been preloaded, construct the new HTML using the template and the photo data. Next, we’re going to make use of the jQuery Backstretch plugin to set the image as the full page background image, as well as vague.js to blur the background. After that, you can append the HTML to the page and then show it with the fadeIn effect. Finally, remove the last image that was shown.

socket.on('new_photo', function(data){
  $.imgpreload(data.image, function()
  {
    console.log('loaded a new image');

    var first_row = $('#wrapper .row:first');
    var html = template(data);

    $.backstretch(data['image']);

    var vague = $('.backstretch').Vague({
      intensity: 10,
      forceSVGUrl: false
    });

    vague.blur();

    $(html).hide().insertBefore(first_row).fadeIn('slow');
    $('#wrapper .row:last').remove();
  });
});

As we begin to wrap things up, let’s quickly add some CSS to our application. You can see this in public/css/style.css. View the file on GitHub.

Deployment

At this point you can now run the app:

node app.js

However, when you navigate to http://localhost:4000/, enter a hashtag and click START, nothing will happen. And if you look at the console then you’ll see the following error:

APISubscriptionError occurred: Invalid response in _request

Hmm! What gives?

The problem is that the app should be accessible through the internet, so as to receive Instagram’s response. As we are running the app on localhost, this unfortunately won’t work. Luckily, we can use ngrok to expose our app the internet. Once you have downloaded and installed ngrok, you can run it by executing the following command in your terminal:

ngrok http 4000

This exposes the Express server to the internet. Be sure to change the callback_url in the app.js file, to use the https URL that ngrok returns. If you plan to deploy an app later on, it should also be an https URL.

instagram.tags.subscribe({
  object_id: tag,
  callback_url: 'https://xxxxxxxx.ngrok.io/subscribe',
  ...
});

Just copy the forwarding URL. Here’s a screenshot:

A screenshot of ngrok running and displaying the forwarding urls

Now, if you restart the server, things should work as planned:

A screenshot of the finished app

Once the user has subscribed, the app will start receiving photo data from the server via socket.io and then display it.

A screenshot of the finished app viewing photos for the #pokemon tag

Taking Things Further

If you want to experiment with this app and make some of your own changes, you might want to look into nodemon. This automatically restarts the server every time you make a change to your Node application and is very handy for development.

Then there’s the question of persistence. Once you’re happy with things, and if you’re on a Unix-based system, you can install Supervisor on your server. This allows you to run the app persistently. Simply running the app with nodemon wouldn’t suffice, because the process is terminated the moment you log out of the server.

Within your current terminal window, execute the following command:

sudo apt-get install supervisor

Create a configuration file for the app:

sudo nano /etc/supervisor/conf.d/instagram-realtime.conf
[program:instagram-realtime]
command=nodemon app.js
directory=/home/ubuntu/www
stdout_logfile=/home/ubuntu/logs/instagram-realtime.log
redirect_stderr=true

And then add it to Supervisor by executing the following commands:

sudo supervisorctl
reread
add instagram-realtime
start instagram-realtime

Final Thoughts

That’s it! In this tutorial, you’ve learned to work with the real-time capabilities of the Instagram API using socket.io. Just keep in mind the limits, namely the API calls that you can make to Instagram (which means that the number of users who can subscribe to different tags is limited — especially if the tags are popular). If this is the case, then the server is going to receive a lot of notifications from Instagram and the number of API calls that you can make are going to run out easily. Other than that, you’re free to use the Instagram API however you like.

There are lots of other possibilities with this API, you can embed Instagram posts or integrate it with your mobile applications. As for our app, a perfect use case would be at an event, where you ask the participants to post photos with a specific tag. The event organizer could subscribe to this tag and project the app to a screen, so that everyone can see the photos being shared.

I’d love to hear your thoughts about this app and the Instagram API in general in the comments below.

  • http://SalaryNet30.com Theresa Gay

    In today’s life everyone knows that money is very important, so I want to make everyone’s life easier and provide you financial freedom by telling you about a project that is paying me $10 k or more every month by doing simple tasks that anyone with basic computer skills can do and you need only good internet connection

    ~~check`my` ~~http website~~~listed~~~on~~~my~~~~{PrIvate}`~~~~~`page` ~~~~~~~!~

    BnnnnnnnnnnnGGHG

  • http://SalaryNet30.com Karen Bourgeois

    Anyone can understand that making good money is extremely important, so I want to Tell you you so that you can make your life easier and achive financial freedom.It’s about a project that is giving me $10 k or more every month by doing simple tasks that anyone with basic computer skills can do and you need only good internet connection.This is the best project I have ever worked in my life you will like it.

    look`my` http site~~~listed~~~on~~~my~~~~`PrIvate`~~~~~`page` ~~~~~~!55555trhtgh

  • romain

    The api just got deprecated on 17. november. Any workaround? see here : tag realtime subscription is deprecated. https://www.apichangelog.com/api/instagram

    • Wern_Ancheta

      hi romain. I’m afraid there’s no workaround when it comes to the API. They basically removed real-time features. But maybe you can resort to scraping if you really need the functionality for your app.

      • Ming Hann

        Did they removed the real time feature?U can still get recent tag right?

  • Marshal Simons

    @Wern_Ancheta:disqus Do you have anything like this for Twitter?

  • Marshal Simons

    @Wern_Ancheta:disqus Do you have anything like this for Twitter?

  • Ming Hann

    This article became irrelevant because instagram has changed their API.

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in JavaScript, once a week, for free.