Build a News Article Search Engine with The New York Times, The Guardian, and Wikipedia APIs.

APIs are a hot topic for developers and entrepreneurs alike. An API is a way for different applications to communicate with each other by sending and receiving information. For example, your company may want to have a live feed of all tweets about itself displayed on its website. The way to access these tweets is to ask Twitter’s API for the relevant information. Twitter’s API will then send back the information your company wants and the code on your company’s website can display that information.

I recently had an idea for a web application that would return relevant search results by asking the APIs of various trusted news sources for any articles they had on the topic. Before beginning development, I scoured the internet for tutorials on how to use the three APIs I wanted use — The New York Times API, The Guardian API, and the The MediaWiki API. I did not find anything that showed me exactly how to implement what I wanted to do so now, after finishing my application, I am writing a quick tutorial that should get you up and running with these three free news APIs.

In the first section, I will throw up my HTML, CSS, and JavaScript code so that developers can read it and take from it the parts they need. In the second section, I will highlight the parts of the code that are most relevant to querying the three APIs my application communicates with and explain how they work. My hope is that this post will help anyone comfortable with HTML, CSS, jQuery, and JavaScript make the leap into working with APIs. For anyone interested in working with APIs but unfamiliar with the aforementioned technologies you can click the following links to some great resources for learning them — HTML & CSS, jQuery, JavaScript. If you want all the code you can clone my project’s GitHub repository and if you want to see the site in action you can check out the live site.

Section 1 - The Code

HTML

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <title>Anchorbase | Find news better</title>
  5.     <link href="css/bootstrap.min.css" rel="stylesheet" media="screen">
  6.     <link rel="stylesheet" type="text/css" href="index.css">
  7. </head>
  8. <body>
  9.     <div class="container">
  10.         <h3 id="anchorbase-logo" class="text-center">Anchorbase</h3>
  11.         <div class = "row">
  12.             <div class="col-md-8 col-md-offset-2">
  13.                 <form id="search-bar" class="span4" role="form">
  14.                     <input id="user-search" class="form-control span3" placeholder="Search news better" autocomplete="off">
  15.                 </form>
  16.             </div>
  17.         </div>
  18.         <div class="row" id="sortable">
  19.             <div class="row-sort">
  20.                 <div class="col-md-4 text-center" id="NYT">
  21.                     <table id="NYT-results-table-header results-table-header" class="NYT-results-table-header table table-bordered">
  22.                         <tr class="NYT-results-header">
  23.                             <th class="text-center" >New York Times</th>
  24.                         </tr>
  25.                     </table>
  26.                     <div id="NYT-results-table-scrolable-container" class="results-table-scrolable-container" >
  27.                         <table id="NYT-results-table" class="NYT-results-table table">                                 
  28.                             <tr id='NYT-last-row'>
  29.                                 <td></td>
  30.                             </tr>
  31.                         </table>
  32.                     </div>
  33.                 </div>
  34.                 <div class="col-md-4 text-center" id="Guardian">
  35.                     <table id="G-results-table" class="G-results-table table table table-bordered">
  36.                         <tr class="guardian-results-header results-table-header">
  37.                             <th class="text-center" >The Guardian</th>
  38.                         </tr>
  39.                     </table>
  40.                     <div id="guardian-results-table-scrolable-container" class="results-table-scrolable-container" >
  41.                         <table id="guardian-results-table" class="guardian-results-table table">    
  42.                             <tr id='guardian-last-row'>
  43.                                 <td></td>
  44.                             </tr>
  45.                         </table>
  46.                     </div>
  47.                 </div>
  48.                 <div class="col-md-4 text-center" id="Wiki">
  49.                     <table id="G-results-table" class="G-results-table table table table-bordered">
  50.                         <tr class="G-results-header results-table-header">
  51.                             <th class="text-center" >Wikipedia</th>
  52.                         </tr>
  53.                     </table>
  54.                     <div id="wikipedia-results-table-scrolable-container" class="results-table-scrolable-container" >
  55.                         <table id="wikipeida-results-table" class="wikipeida-results-table table">    
  56.                             <tr id='wikipedia-last-row'>
  57.                                 <td></td>
  58.                             </tr>
  59.                         </table>
  60.                         <div>    
  61.                         </div>
  62.                     </div>
  63.                 </div>
  64.             </div>    
  65.         </div>
  66.     </div>
  67.     <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" ></script>
  68.     <script src="http://code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
  69.     <script type='text/javascript' src='js/jquery.js'></script>
  70.     <script>
  71.     $(document).ready(function() {
  72.         $('#sortable > .row-sort').sortable();
  73.     });
  74.     </script>
  75. </body>
  76. </html>

CSS custom

  1. #anchorbase-logo {
  2.     margin-top: 100px;
  3. }
  4. #search-bar {
  5.     margin-top: 30px;
  6.     margin-bottom: 40px;
  7. }
  8. .results-table-scrolable-container{
  9.     height: 500px;
  10.     overflow: hidden;
  11. }
  12. .results-table-scrolable-container:hover {
  13.     overflow: auto;
  14.     overflow-y: scroll;
  15. }
  16. .NYT-results-header{
  17.     height: 0px;
  18. }
  19. .NYT-results-header:hover {
  20. background-color: #f3fbfb;
  21. }
  22. .guardian-results-header:hover {
  23. background-color: #f3fbfb;    
  24. }
  25. .G-results-header:hover {
  26. background-color: #f3fbfb;
  27. }
  28. .form-control:focus {
  29. border-color: #D8F2F3;
  30. }
  31. ::-webkit-scrollbar {
  32. -webkit-appearance: none;
  33. width: 7px;
  34. }
  35. ::-webkit-scrollbar-thumb {
  36. border-radius: 4px;
  37. background-color: rgba(0,0,0,.5);
  38. -webkit-box-shadow: 0 0 1px rgba(255,255,255,.5);
  39. }

JavaScript/jQuery

  1. $(document).ready(function(){    
  2. var userSearchTerm = "",
  3. NYTapiKey = "--------------google New York Times API key to get your own free key and then paste it here as a string---------------------------",
  4. NYTtimesShown = 0,
  5. NYTresultsArray = [],
  6. guardianApiKey = "----------google Guardian API key to get your own free key and then paste it here as a string--------------------------",
  7. guardianTimesShown = 1,
  8. guardianResultsArray = [],
  9. wikipediaResultsArray =[],
  10. wikipediaContinueString = "";
  11. function ResultsObject(url, headline, leadParagraph, index) {
  12. this.url = url;
  13. this.headline = headline;
  14. this.leadParagraph = leadParagraph;
  15. this.index = index;
  16. }
  17. var apiCallNYT = function(){
  18. $.ajax({
  19. url: "http://api.nytimes.com/svc/search/v2/articlesearch.json?q=" + userSearchTerm.replace(' ', '+') + "&page=" + NYTtimesShown + "&api-key=" + NYTapiKey,
  20. dataType: 'json',
  21. success: function(results){
  22. var resultsString = JSON.stringify(results),
  23. resultsObject = results,
  24. documentsArray = resultsObject['response']['docs'];
  25. documentsArray.forEach(function(element, index, array){
  26. if (element['web_url'] == null || element['headline']['main'] == null || element['lead_paragraph'] == null) {
  27. } else {
  28. article = new ResultsObject(element['web_url'], element['headline']['main'], element['lead_paragraph'], NYTresultsArray.length);
  29. NYTresultsArray.push(article);
  30. }
  31. });
  32. resultsPrintNYT(NYTresultsArray);
  33. NYTtimesShown++;
  34. }
  35. });
  36. };
  37. var resultsPrintNYT = function(resultsArray){
  38. for (var i = 0; i < resultsArray.length; i++){
  39. if (resultsArray[i] !== undefined) {
  40. $("<tr id='NYTresult"+resultsArray[i].index+"'><td class='text-left NYTresult'><a href='" + resultsArray[i].url + "'>" + resultsArray[i].headline +"</a><p>" + resultsArray[i].leadParagraph + "</p></td></tr>").insertBefore('#NYT-last-row');
  41. }
  42. }
  43. };
  44. var apiCallGaurdain = function() {
  45. $.ajax({
  46. url: "http://content.guardianapis.com/search?api-key="+guardianApiKey+"&show-fields=all&page="+ guardianTimesShown + "&q="+userSearchTerm.replace(' ', '%'),
  47. dataType: 'jsonp',
  48. success: function( results ) {
  49. var results = results;
  50. //console.log(results);
  51. var documentsArray = results['response']['results'];
  52. //console.log(documentsArray);
  53. documentsArray.forEach(function(element, index, array){
  54. if (element['webUrl'] == null || element['webTitle'] == null || element['fields']['standfirst'] == null) {
  55. } else {
  56. article = new ResultsObject(element['webUrl'], element['webTitle'], element['fields']['standfirst'], guardianResultsArray.length);
  57. guardianResultsArray.push(article);
  58. }
  59. });
  60. resultsPrintGuardian(guardianResultsArray);
  61. guardianTimesShown++;
  62. }
  63. });
  64. };
  65. var resultsPrintGuardian = function(resultsArray){
  66. for (var i = 0; i < resultsArray.length; i++){
  67. if (resultsArray[i] !== undefined) {
  68. $("<tr id='guardianResult"+resultsArray[i].index+"'><td class='text-left guardianResult'><a href='" + resultsArray[i].url + "'>" + resultsArray[i].headline +"</a><p>" + resultsArray[i].leadParagraph + "</p></td></tr>").insertBefore('#guardian-last-row');
  69. }
  70. }
  71. };
  72. var apiCallWikipedia = function(){
  73. $.ajax({
  74. url: "http://en.wikipedia.org/w/api.php?format=json&action=query&generator=allpages&gaplimit=10&gapfrom="+ userSearchTerm.replace(' ', '+')+"&prop=pageimages|extracts&pilimit=max&exintro&explaintext&exsentences=1&exlimit=max&gapcontinue=" + wikipediaContinueString,
  75. dataType: 'jsonp',
  76. success: function(results) {
  77. var results = results;
  78. console.log(results);
  79. var documentsArray = results['query']['pages'];
  80. console.log(documentsArray);
  81. console.log(typeof documentsArray);
  82. wikipediaContinueString = results['query-continue']['allpages']['gapcontinue'];
  83. for (var prop in documentsArray) {
  84. if (documentsArray.hasOwnProperty(prop)){
  85. article = new ResultsObject( "https://en.wikipedia.org/wiki/"+ encodeURI(documentsArray[prop]['title']), documentsArray[prop]['title'], documentsArray[prop]['extract'], wikipediaResultsArray.length)
  86. wikipediaResultsArray.push(article);
  87. }
  88. }
  89. resultsPrintWikipedia(wikipediaResultsArray);
  90. console.log(wikipediaResultsArray);
  91. }
  92. });
  93. }
  94. var resultsPrintWikipedia = function(resultsArray){
  95. for (var i = 0; i < resultsArray.length; i++){
  96. if (resultsArray[i] !== undefined) {
  97. $("<tr id='wikipeidaResult"+resultsArray[i].index+"'><td class='text-left wikipediaResult'><a href='" + resultsArray[i].url + "'>" + resultsArray[i].headline +"</a><p>" + resultsArray[i].leadParagraph + "</p></td></tr>").insertBefore('#wikipedia-last-row');
  98. }
  99. }
  100. };
  101. $('#search-bar').bind('keypress',function (event){
  102. if (event.keyCode === 13){
  103. event.preventDefault();
  104. userSearchTerm = $('#user-search').val();
  105. NYTtimesShown = 0;
  106. NYTresultsArray = [];
  107. $('.NYTresult').remove();
  108. guardianTimesShown =1;
  109. guardianResultsArray = [];
  110. $('.guardianResult').remove();
  111. wikipediaContinueString = "";
  112. wikipediaResultsArray =[];
  113. $('.wikipediaResult').remove();
  114. apiCallWikipedia();
  115. apiCallNYT();
  116. apiCallGaurdain();
  117. }
  118. });
  119. $('#NYT-results-table-scrolable-container').bind('scroll', function() {
  120. if($(this).scrollTop() + $(this).innerHeight() >= this.scrollHeight) {
  121. guardianResultsArray = [];
  122. apiCallNYT();
  123. }
  124. });
  125. $('#guardian-results-table-scrolable-container').bind('scroll', function() {
  126. if($(this).scrollTop() + $(this).innerHeight() >= this.scrollHeight) {
  127. guardianResultsArray =[];
  128. apiCallGaurdain();
  129. }
  130. });
  131. $('#wikipedia-results-table-scrolable-container').bind('scroll', function() {
  132. if($(this).scrollTop() + $(this).innerHeight() >= this.scrollHeight) {
  133. wikipediaResultsArray =[];
  134. apiCallWikipedia();
  135. }
  136. });
  137. });

Section 2 - Explanation

The index.html and index.css files combined with Bootstrap control how the app appears on the the screen. It was important to set the container divs for the API results to have a set height on the page but to become scrollable when the user’s mouse hovers over them. This way I could ask the API, the results from which the user was scrolling through, to produce the next set of results once the user had almost scrolled down to the bottom of the div. I achieved this with the following custom CSS class and jQuery event handlers.

CSS

  1. .results-table-scrolable-container{
  2.     height: 500px;
  3.     overflow: hidden;
  4. }
  5. .results-table-scrolable-container:hover {
  6.     overflow: auto;
  7.     overflow-y: scroll;
  8. }

jQuery

  1. $('#NYT-results-table-scrolable-container').bind('scroll', function() {
  2. if($(this).scrollTop() + $(this).innerHeight() >= this.scrollHeight) {
  3. guardianResultsArray = [];
  4. apiCallNYT();
  5. }
  6. });
  7. $('#guardian-results-table-scrolable-container').bind('scroll', function() {
  8. if($(this).scrollTop() + $(this).innerHeight() >= this.scrollHeight) {
  9. guardianResultsArray =[];
  10. apiCallGaurdain();
  11. }
  12. });
  13. $('#wikipedia-results-table-scrolable-container').bind('scroll', function() {
  14. if($(this).scrollTop() + $(this).innerHeight() >= this.scrollHeight) {
  15. wikipediaResultsArray =[];
  16. apiCallWikipedia();
  17. }
  18. });

In the CSS we can see that all my results container divs have the same set height and hidden overflow when they are not being hovered over. This will keep the page organized and prevent different results div from becoming different heights depending on how many results are sent back by their corresponding API. Then when the user hovers a mouse over them, their overflow is set to scroll, allowing for the user to scroll down through all the results.

In the jQuery we can see that when an API result container is scrolled down to the bottom it will trigger a resetting of the API results array (the data structure that hold all the API results before they are added to the DOM) and then calls the function that asks that specific API for the next batch of results. The result is three “infinitely” scrolling divs that ask for more information from their corresponding API when and only when the user has scrolled down through the current batch of results. It may seem odd to reset the array that holds the search results from that API before calling the function that asks the API for results. This happens because my app does not store old API information on the backend. Instead I just print the results to the DOM and insert new results below. I do this to demonstrate how you could parse the information returned by an API into objects that you could then store permanently in a database but there is no reason to store results for the purpose of my app.

Now let us examine the functions that actually ask for, and then parse into objects, information from the APIs. These are the functions that the three jQuery event handlers above call when a user has scrolled to the bottom of one of them.

  1. var userSearchTerm = "",
  2. NYTapiKey = "--------------google New York Times API key to get your own free key and then paste it here as a string---------------------------",
  3. NYTtimesShown = 0,
  4. NYTresultsArray = [],
  5. guardianApiKey = "--------------google Guardian API key to get your own free key and then paste it here as a string---------------------------",
  6. guardianTimesShown = 1,
  7. guardianResultsArray = [],
  8. wikipediaResultsArray =[],
  9. wikipediaContinueString = "";
  10. var apiCallNYT = function(){
  11. $.ajax({
  12. url: "http://api.nytimes.com/svc/search/v2/articlesearch.json?q=" + userSearchTerm.replace(' ', '+') + "&page=" + NYTtimesShown + "&api-key=" + NYTapiKey,
  13. dataType: 'json',
  14. success: function(results){
  15. var resultsString = JSON.stringify(results),
  16. resultsObject = results,
  17. documentsArray = resultsObject['response']['docs'];
  18. documentsArray.forEach(function(element, index, array){
  19. if (element['web_url'] == null || element['headline']['main'] == null || element['lead_paragraph'] == null) {
  20. } else {
  21. article = new ResultsObject(element['web_url'], element['headline']['main'], element['lead_paragraph'], NYTresultsArray.length);
  22. NYTresultsArray.push(article);
  23. }
  24. });
  25. resultsPrintNYT(NYTresultsArray);
  26. NYTtimesShown++;
  27. }
  28. });
  29. };
  30. var apiCallGaurdain = function() {
  31. $.ajax({
  32. url: "http://content.guardianapis.com/search?api-key="+guardianApiKey+"&show-fields=all&page="+ guardianTimesShown + "&q="+userSearchTerm.replace(' ', '%'),
  33. dataType: 'jsonp',
  34. success: function( results ) {
  35. var results = results;
  36. //console.log(results);
  37. var documentsArray = results['response']['results'];
  38. //console.log(documentsArray);
  39. documentsArray.forEach(function(element, index, array){
  40. if (element['webUrl'] == null || element['webTitle'] == null || element['fields']['standfirst'] == null) {
  41. } else {
  42. article = new ResultsObject(element['webUrl'], element['webTitle'], element['fields']['standfirst'], guardianResultsArray.length);
  43. guardianResultsArray.push(article);
  44. }
  45. });
  46. resultsPrintGuardian(guardianResultsArray);
  47. guardianTimesShown++;
  48. }
  49. });
  50. };
  51. var apiCallWikipedia = function(){
  52. $.ajax({
  53. url: "http://en.wikipedia.org/w/api.php?format=json&action=query&generator=allpages&gaplimit=10&gapfrom="+ userSearchTerm.replace(' ', '+')+"&prop=pageimages|extracts&pilimit=max&exintro&explaintext&exsentences=1&exlimit=max&gapcontinue=" + wikipediaContinueString,
  54. dataType: 'jsonp',
  55. success: function(results) {
  56. var results = results;
  57. console.log(results);
  58. var documentsArray = results['query']['pages'];
  59. console.log(documentsArray);
  60. console.log(typeof documentsArray);
  61. wikipediaContinueString = results['query-continue']['allpages']['gapcontinue'];
  62. for (var prop in documentsArray) {
  63. if (documentsArray.hasOwnProperty(prop)){
  64. article = new ResultsObject( "https://en.wikipedia.org/wiki/"+ encodeURI(documentsArray[prop]['title']), documentsArray[prop]['title'], documentsArray[prop]['extract'], wikipediaResultsArray.length)
  65. wikipediaResultsArray.push(article);
  66. }
  67. }
  68. resultsPrintWikipedia(wikipediaResultsArray);
  69. console.log(wikipediaResultsArray);
  70. }
  71. });
  72. }

At the top of this snippet from my jquery.js file, I define a number of variables that are essential for getting our API querying functions to work properly. Let’s take a look at them —

  1. var userSearchTerm = "",
  2. NYTapiKey = "--------------google New York Times API key to get your own free key and then paste it here as a string---------------------------",
  3. NYTtimesShown = 0,
  4. NYTresultsArray = [],
  5. guardianApiKey = "--------------google Guardian API key to get your own free key and then paste it here as a string---------------------------",
  6. guardianTimesShown = 1,
  7. guardianResultsArray = [],
  8. wikipediaResultsArray =[],
  9. wikipediaContinueString = "";

userSearchTerm is exactly what it sounds like, it will eventually contain the search term that the user enters into the search bar at the top of the page. We need the search term so we can pass it into our API call and get only results that are relevant to that search. Most APIs requre you to pass a key along with your request for information. This allows the API to identify who is asking for information and how many times that person asks for information. APIs that require a key will not work without one so be sure to get your own API key and enter it as a string value for the NYTapiKey and guardianApiKey variables before trying to run the above code. When you ask for data from an API it will often return only the first n results that match your request. The reason for this is efficiency. If every time a program asked the New York Times API for results about ‘Obama’ the New York Times API returned all matching results it would be constructing and sending way more information that is necessary. All that response construction costs server processing power which in turn costs lots of money. So, though some APIs allow you to specify exactly how many matching results you would like most will return the first ten or so and then provide you with a way to ask for the next ten and the next ten when and if you need to. This is the reason for initializing the NYTtimesShown and guardianTimesShown variables at their respective values. These numbers will go into our API queries along with our API key and userSearchTerm to get not only results that match our query but correct packet of results that match our query. The MediaWiki Api does not require a key and has a different way of interating over its packets of results that has to to with the wikipediaContinueString variable. As the users make more API calls by scrolling through the results divs, variables will automatically increment so that our API requests update to ask for the next and then next packet of information. Every time the wikipedia API is called, the wikipediaContineString will be updated with a piece of information that wikipedia includes in its response that indicates how to get the next packet of results. The results array variables will eventually contain objects that are filled with the information that we want from the response we get back from our API calls. In most cases the API you query will respond with more information than you need. It is helpful to construct objects from the information provided by the API that include only the information you are interested in. You can then do whatever you want with these objects without having to worry about the irrelevant information provided by the API’s response.

Now let us look at how we use these variables to build our API query and the process the results.

  1. var apiCallNYT = function(){
  2. $.ajax({
  3. url: "http://api.nytimes.com/svc/search/v2/articlesearch.json?q=" + userSearchTerm.replace(' ', '+') + "&page=" + NYTtimesShown + "&api-key=" + NYTapiKey,
  4. dataType: 'json',
  5. success: function(results){
  6. var resultsString = JSON.stringify(results),
  7. resultsObject = results,
  8. documentsArray = resultsObject['response']['docs'];
  9. documentsArray.forEach(function(element, index, array){
  10. if (element['web_url'] == null || element['headline']['main'] == null || element['lead_paragraph'] == null) {
  11. } else {
  12. article = new ResultsObject(element['web_url'], element['headline']['main'], element['lead_paragraph'], NYTresultsArray.length);
  13. NYTresultsArray.push(article);
  14. }
  15. });
  16. resultsPrintNYT(NYTresultsArray);
  17. NYTtimesShown++;
  18. }
  19. });
  20. };
  21. var apiCallGaurdain = function() {
  22. $.ajax({
  23. url: "http://content.guardianapis.com/search?api-key="+guardianApiKey+"&show-fields=all&page="+ guardianTimesShown + "&q="+userSearchTerm.replace(' ', '%'),
  24. dataType: 'jsonp',
  25. success: function( results ) {
  26. var results = results;
  27. //console.log(results);
  28. var documentsArray = results['response']['results'];
  29. //console.log(documentsArray);
  30. documentsArray.forEach(function(element, index, array){
  31. if (element['webUrl'] == null || element['webTitle'] == null || element['fields']['standfirst'] == null) {
  32. } else {
  33. article = new ResultsObject(element['webUrl'], element['webTitle'], element['fields']['standfirst'], guardianResultsArray.length);
  34. guardianResultsArray.push(article);
  35. }
  36. });
  37. resultsPrintGuardian(guardianResultsArray);
  38. guardianTimesShown++;
  39. }
  40. });
  41. };
  42. var apiCallWikipedia = function(){
  43. $.ajax({
  44. url: "http://en.wikipedia.org/w/api.php?format=json&action=query&generator=allpages&gaplimit=10&gapfrom="+ userSearchTerm.replace(' ', '+')+"&prop=pageimages|extracts&pilimit=max&exintro&explaintext&exsentences=1&exlimit=max&gapcontinue=" + wikipediaContinueString,
  45. dataType: 'jsonp',
  46. success: function(results) {
  47. var results = results;
  48. console.log(results);
  49. var documentsArray = results['query']['pages'];
  50. console.log(documentsArray);
  51. console.log(typeof documentsArray);
  52. wikipediaContinueString = results['query-continue']['allpages']['gapcontinue'];
  53. for (var prop in documentsArray) {
  54. if (documentsArray.hasOwnProperty(prop)){
  55. article = new ResultsObject( "https://en.wikipedia.org/wiki/"+ encodeURI(documentsArray[prop]['title']), documentsArray[prop]['title'], documentsArray[prop]['extract'], wikipediaResultsArray.length)
  56. wikipediaResultsArray.push(article);
  57. }
  58. }
  59. resultsPrintWikipedia(wikipediaResultsArray);
  60. console.log(wikipediaResultsArray);
  61. }
  62. });
  63. }

The three apiCall functions above comunicate with their respective APIs by creating a JavaScript XMLHttpRequest object. This allows our application to ask for and receive information from the API’s server without refresing our page. This technology is called AJAX which stands for Asynchronous JavaScript and XML. To learn more about AJAX, check out this AJAX introduction on w3 schools. Essentially what is happening in the three apiCall functions above is a request is constructed by the url: properties on lines three. twenty-five, and forty-eaight, and then the results of that request, a JSON object, are processed in the success functions that start on lines five, twenty-seven, and fifty. The results argument that is passed into these functions is the JSON object that in turn is the response sent back by the API. The url is the query which defines what the response will be. I have left some console.logs in the code so that if you open up a console while running the application you can see what the results object is before we process it into our objects. Let us first examine the url properties of our three JavaScript XMLHttpRequest objects.

  1. url: "http://api.nytimes.com/svc/search/v2/articlesearch.json?q=" + userSearchTerm.replace(' ', '+') + "&page=" + NYTtimesShown + "&api-key=" + NYTapiKey,
  2. url: "http://content.guardianapis.com/search?api-key="+guardianApiKey+"&show-fields=all&page="+ guardianTimesShown + "&q="+userSearchTerm.replace(' ', '%'),
  3. url: "http://en.wikipedia.org/w/api.php?format=json&action=query&generator=allpages&gaplimit=10&gapfrom="+ userSearchTerm.replace(' ', '+')+"&prop=pageimages|extracts&pilimit=max&exintro&explaintext&exsentences=1&exlimit=max&gapcontinue=" + wikipediaContinueString,

The urls above are requests sent to the servers of the API we are querying. Notice that these queries are dynamic (they are the result of concatenating our variables into the generic url request used to talk to the APIs). You could replace userSearchTerm.replace(‘ ‘, ‘+’) above with your own hard coded search query, but then every time you called the API call function you would get results only for that query. As the user sets this variable by hitting enter on the user search bar our queries will ask for results that are relevant to that query. You need to call replace(‘ ‘, ‘+’) on the userSearch term to remove blank space from the query and instead use the recognized blank space substitute, a ‘+’’ sign. We also pass variables in for the apiKey and the number of times shown. The Api key will validate our request with the API and the times shown will tell the API which packet of results we want to see. Wikipedia wants you to set a string equal to a property of the object it sends back and then append that string the to the continue field of the request ignored to get the next packet of information.

Now let’s look at what what we do with the results sent back to us once we have successfully constructed and sent our queries.

  1. success: function(results){
  2. var resultsString = JSON.stringify(results),
  3. resultsObject = results,
  4. documentsArray = resultsObject['response']['docs'];
  5. documentsArray.forEach(function(element, index, array){
  6. if (element['web_url'] == null || element['headline']['main'] == null || element['lead_paragraph'] == null) {
  7. } else {
  8. article = new ResultsObject(element['web_url'], element['headline']['main'], element['lead_paragraph'], NYTresultsArray.length);
  9. NYTresultsArray.push(article);
  10. }
  11. });
  12. resultsPrintNYT(NYTresultsArray);
  13. NYTtimesShown++;
  14. }
  15. success: function(results) {
  16. var results = results;
  17. //console.log(results);
  18. var documentsArray = results['response']['results'];
  19. //console.log(documentsArray);
  20. documentsArray.forEach(function(element, index, array){
  21. if (element['webUrl'] == null || element['webTitle'] == null || element['fields']['standfirst'] == null) {
  22. } else {
  23. article = new ResultsObject(element['webUrl'], element['webTitle'], element['fields']['standfirst'], guardianResultsArray.length);
  24. guardianResultsArray.push(article);
  25. }
  26. });
  27. resultsPrintGuardian(guardianResultsArray);
  28. guardianTimesShown++;
  29. }
  30. success: function(results) {
  31. var results = results;
  32. console.log(results);
  33. var documentsArray = results['query']['pages'];
  34. console.log(documentsArray);
  35. console.log(typeof documentsArray);
  36. wikipediaContinueString = results['query-continue']['allpages']['gapcontinue'];
  37. for (var prop in documentsArray) {
  38. if (documentsArray.hasOwnProperty(prop)){
  39. article = new ResultsObject( "https://en.wikipedia.org/wiki/"+ encodeURI(documentsArray[prop]['title']), documentsArray[prop]['title'], documentsArray[prop]['extract'], wikipediaResultsArray.length)
  40. wikipediaResultsArray.push(article);
  41. }
  42. }
  43. resultsPrintWikipedia(wikipediaResultsArray);
  44. console.log(wikipediaResultsArray);
  45. }

Here, in all three of the success functions we create a documentsArray that is filled with only the parts of the response object that we are interested in. Note that while lines four, twenty, thirty-seven all accomplish this they do so in different ways. This is because the responses sent back from the three different APIs are formatted differently. You can console.log out the results object sent back to you as I do for the results from the wikipedia API on line thirty eight to inspect it and see what parts of it are relevant to what you want to display on the page. In my case, the responses all contained an array of the title text, web url, and text summary or lead paragraph that I wanted to display so I simply set this information equal to my own documentsArray. Then the success functions iterate over the documentsArray parsing the relevant information from those arrays into objects and then pushing those objects into my own array. While it may seem convoluted to parse an array of objects that is given to you into your own array of slightly different objects it would be very useful if you wanted to store these objects in your database or perform more operations on them than my app currently does. The constructor for the results object is below. You will see that that lines nine, twenty-six, and forty-four of the above code create new objects with only the properties we want from each of the objects in the documentsArray.

  1. function ResultsObject(url, headline, leadParagraph, index) {
  2. this.url = url;
  3. this.headline = headline;
  4. this.leadParagraph = leadParagraph;
  5. this.index = index;
  6. }

Now, every time we run our apiCall functions they will create objects from the responses that contain only the information we want from those responses and place those objects into arrays that correspond to the API that the responses are from. All that’s left is to iterate over our arrays of objects and append them to the correct place in the DOM. This operation is accomplished in the print results functions which are called by the apiCall functions after they have constructed their resultsArrays. The results print functions are below.

  1. var resultsPrintNYT = function(resultsArray){
  2. for (var i = 0; i < resultsArray.length; i++){
  3. if (resultsArray[i] !== undefined) {
  4. $("<tr id='NYTresult"+resultsArray[i].index+"'><td class='text-left NYTresult'><a href='" + resultsArray[i].url + "'>" + resultsArray[i].headline +"</a><p>" + resultsArray[i].leadParagraph + "</p></td></tr>").insertBefore('#NYT-last-row');
  5. }
  6. }
  7. };
  8. var resultsPrintGuardian = function(resultsArray){
  9. for (var i = 0; i < resultsArray.length; i++){
  10. if (resultsArray[i] !== undefined) {
  11. $("<tr id='guardianResult"+resultsArray[i].index+"'><td class='text-left guardianResult'><a href='" + resultsArray[i].url + "'>" + resultsArray[i].headline +"</a><p>" + resultsArray[i].leadParagraph + "</p></td></tr>").insertBefore('#guardian-last-row');
  12. }
  13. }
  14. };
  15. var resultsPrintWikipedia = function(resultsArray){
  16. for (var i = 0; i < resultsArray.length; i++){
  17. if (resultsArray[i] !== undefined) {
  18. $("<tr id='wikipeidaResult"+resultsArray[i].index+"'><td class='text-left wikipediaResult'><a href='" + resultsArray[i].url + "'>" + resultsArray[i].headline +"</a><p>" + resultsArray[i].leadParagraph + "</p></td></tr>").insertBefore('#wikipedia-last-row');
  19. }
  20. }
  21. };

The results print functions above will take our resultsArrays and iterate over them creating new table rows for our results display divs and then inserting them. The result is that the relevant properties of all the objects in our results arrays are displayed in the correct position in the DOM. And we’re done! A fully functioning AJAX powered API search engine!

If you have reached this point and are looking to make improvements here are a few suggestions:

  • Right now the results from the different API calls display as soon as they are processed. As the different API calls take different amounts of time to complete it means that front end is not as clean as it could be. It could look much better if all the results displayed at the same time. Look into jQuery's When Then to accomplish this though there are a number of other ways!
  • Add another API… or ten other API’s … or look into YQL which might be able to let you return results not from a specific API but from YAHOO’s search engine.
  • Add a feature where the users can add and delete APIs from the results they see. Maybe some people want results from The New York Times but not from Wikipedia for example.