Google Search Books in Meteor

I converted our Google Search Books example from AM1 into a Meteor app. You can find the new app here.

At this point in time, the CSS of the app is not perfect, but that can be fixed without changing the HTML/JS code.

One thing that you'll notice is that the code in Meteor is simpler to understand, because of the templates in the HTML code. To make the app more user-friendly, I have added a spinner that shows whenever the app is waiting for the results from the API.

The HTML Code

The HTML code will contain a combination of HTML tags and Spacebars instructions that use Meteor session variables and properties from the API JSON data.

<head>
  <title>Search Google Books</title>
</head>

<body>
  <h1>Search Google books</h1>
                                       
  <form>
     <input type="text" name="queryPhrase" 
            placeholder="type a query">
    <input type="submit" value="Search" id="searchButton">
  </form>
  <div>    
    {{#if waitingForResults}}
      {{> spinner}}       
    {{/if}}
    
    {{#if total}}
      <p class="info">Total number of found books: 
         <span>{{total}}</span> for <span>{{phrase}}</span></p>
    {{/if}}
    
    {{#if allResults}}
      {{#each allResults}}
       <div class="book">
        <img src="{{imgUrl}}">
        <p class="title">{{volumeInfo.title}} - by 
               <span class="author">{{volumeInfo.authors}}</span>
        </p>
       </div>
      {{/each}}
    {{/if}}
  </div>
  
</body>       
       

Javascript Code

The Javascript code is divided into the client and server portion. Differently from AM1, the API request is performed on the server side, where we don't need an asynchrounus call, because the server can wait for the answer. However, the callback function is used in the client code, to make use of the results once they have been retrieved.

if (Meteor.isClient) {
  // we set these variables to their initial values
  Session.setDefault("totalResults", 0);
  Session.setDefault("queryPhrase", "");
  Session.setDefault("resultsList", []);
  Session.setDefault("waiting", false);
  
  Template.body.helpers({
    total: function(){
      return Session.get("totalResults");
    },

    phrase: function(){
      return Session.get("queryPhrase");
    },
    
    allResults: function(){
      return Session.get("resultsList");
    },
    
    imgUrl: function(){
      // we'll need to have an if/else here to check first that the
      // thumbnail field is present
      return this.volumeInfo.imageLinks.thumbnail;
  },
    
    waitingForResults: function(){
      return Session.get("waiting");
    }
    
  });
  
  Template.body.events({
    
    'submit form': function(event){
      // read user input
      var searchPhrase = event.target.queryPhrase.value;
      
      // update the session variables for new search term
      Session.set("queryPhrase", searchPhrase);
      Session.set("totalResults", 0);
      Session.set("resultsList", []);
      Session.set("waiting", true);
      
      // wait for the results from the server
      Meteor.call('callGoogleBooks', searchPhrase, function(error, result){
        Session.set("totalResults", result.totalItems);
        Session.set("resultsList", result.items);
        Session.set("waiting", false);
        event.target.queryPhrase.value = "";
      });
      
      return false; // prevent the form reload
    },
  });
}


if (Meteor.isServer) {

  var googleURL = "https://www.googleapis.com/books/v1/volumes?q=";
  
  Meteor.methods({
    
    callGoogleBooks: function(searchPhrase){
          console.log("looking for :", searchPhrase)
          var fullURL = googleURL + encodeURIComponent(searchPhrase) + "&maxResults=40";
          console.log(fullURL);
          var response = HTTP.call("GET", fullURL);
          console.log("got response:", response);
          return response.data;
    }
    
  });
}       
       

While for the Google Books API the strategy of server requests is not as important (because Google support CORS), other APIs that don't support CORS (for example New York Times API or GoodReads), or APIs that use authentication that shouldn't be part of client code (such as Twitter API) will benefit by having the API call run on the server.

Source Code for the Project

You can download the source code for this project here. This time the code for client and server are in a single file, but in the future, we'll break it into two separate files and different folders.

You you download and unzip the project folder, cd into the folder and then type the command: meteor list, you will notice two packages that were added to the project: http and sacha:spin. Keep that in mind when you're creating your own projects from scratch.

Your Turn

Use the code above as a model and try to write your AM1 as a Meteor app.