{"id":884,"date":"2014-03-22T09:22:49","date_gmt":"2014-03-22T13:22:49","guid":{"rendered":"http:\/\/blog.agilityfeat.com\/?p=884"},"modified":"2014-03-22T09:22:49","modified_gmt":"2014-03-22T13:22:49","slug":"meetup-api-example","status":"publish","type":"post","link":"https:\/\/agilityfeatpanama.com\/en\/blog\/2014\/03\/meetup-api-example\/","title":{"rendered":"Using Meetup&#8217;s streaming API to mashup event registrations"},"content":{"rendered":"<p>Meetup provides an API for accessing streams of real-time data about its service which can be fun to play with.  In this post, we\u2019re going to consume one of those streams via their javascript interface and mash up event registrations with a google map.  When it\u2019s complete, the end result is going to look something like this handy little animated gif we created:<\/p>\n<p><a href=\"\/en\/realtimeweb.co\/\"><img loading=\"lazy\" src=\"http:\/\/rtbook.herokuapp.com\/img\/word-animation.gif\" width=\"450\" height=\"320\" class=\"alignleft\" style=\"max-width: 450px; max-height: 320px;\" \/><\/a><\/p>\n<p><br clear=\"left\"\/><\/p>\n<p>As people RSVP to meetup events around the world, we receive the stream of RSVP\u2019s and display them on the map in the appropriate location.  Depending on the time of day, it\u2019s fun to watch and see what parts of the world are awake and RSVP\u2019ing to events.<\/p>\n<p>Is this super useful?  Maybe not, but it lets us look at how the Meetup API uses WebSockets, so just come along for the ride.<\/p>\n<p>You can get all the code for this example here:  <strong><a href=\"https:\/\/github.com\/agilityfeat\/meetup-streaming\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/github.com\/agilityfeat\/meetup-streaming<\/a><\/strong><\/p>\n<p>To start, create an index.html file that looks like the following.  You\u2019ll notice it\u2019s pretty simple &#8211; we\u2019re just putting a div in place for google\u2019s map canvas, and then referencing some javascript files:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/asime\/9707016.js\"><\/script><\/p>\n<p>Underneath the map div, we\u2019re linking to a few javascript files that provide the real work.  First, we\u2019re just including a jquery library, and then the google maps api.<\/p>\n<p>Must.js is an important file provided by Meetup, which refers to \u201cMeet Up STreaming\u201d.  It\u2019s part of the <strong><a href=\"http:\/\/www.meetup.com\/meetup_api\/\" target=\"_blank\" rel=\"noopener noreferrer\">Meetup API<\/a><\/strong>, and the <strong><a href=\"https:\/\/github.com\/meetup\/must.js#readme\" target=\"_blank\" rel=\"noopener noreferrer\">must.js project<\/a><\/strong> provides a nice interface to the streaming API from Meetup.<\/p>\n<p>The simplest way to get a copy of just this file is to go directly to the <strong><a href=\"https:\/\/github.com\/meetup\/must.js\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub project for must<\/a><\/strong>.  But you should use <strong><a href=\"http:\/\/bower.io\" target=\"_blank\" rel=\"noopener noreferrer\">bower<\/a><\/strong> instead, which is a handy tool created by Twitter for managing any front end dependencies that your project might have, like third party javascript applications such as must.js.  Once you have bower installed, you can get must.js by running this from your project:<\/p>\n<p><em><strong>bower install must<\/strong><\/em><\/p>\n<p>If you take a look inside the must.js file, you\u2019ll notice wrappers for a variety of streaming API\u2019s:  event RSVP\u2019s, comments, check-ins, and photos.  We\u2019re just going to use the RSVP\u2019s endpoint in this example, but you can extend our example to also display comments from users about events, check-in\u2019s at events, or display photos as they are uploaded to Meetup in real-time.<\/p>\n<p>Here is the relevant part of must.js for event RSVP\u2019s:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/asime\/9707081.js\"><\/script><\/p>\n<p>Our code will need to pass a callback method to must.js so that they know where to send rsvp objects as they come in.  Must.js itself is just making a call to the root webservice for streaming rsvps, and passing them back to our callback method.<\/p>\n<p>By default must.js is doing this using WebSockets so that it is done in an efficient socket model, but if your browser doesn\u2019t support WebSockets, then must.js will automatically fall back to a less efficient long polling method.  Must.js checks to see if you can support WebSockets here:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/asime\/9707104.js\"><\/script><\/p>\n<p>Assuming that you can in fact support WebSockets, then the must.js code is going to drop down to this chunk of code for receiving call back events from the meetup API:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/asime\/9707157.js\"><\/script><\/p>\n<p>In this code snippet, the .onmessage event is fired whenever there is data to process from Meetup, and then the must.js code above will parse the JSON data returned, and as long as there are no errors, it calls a local method handleJson which will basically just pass the json data (now stored in the variable \u201cary\u201d) along to our callback method.<\/p>\n<p>Now that we see how must.js works, let\u2019s look at calling it ourselves.  Back in our index.html file, the last javascript we are including is our own:<\/p>\n<p><em><strong>js\/meetupmashup\/app.js<\/strong><\/em><\/p>\n<p>You can see the <strong><a href=\"https:\/\/github.com\/agilityfeat\/meetup-streaming\/blob\/master\/js\/meetupmashup\/app.js\" target=\"_blank\" rel=\"noopener noreferrer\">complete file here<\/a><\/strong>, but let\u2019s look at just a few key code snippets.<\/p>\n<p>At the very bottom of the file, the first javascript that is executed is this statement:<\/p>\n<p><em><strong>geo_code_app.init();<\/strong><\/em><\/p>\n<p>This calls to the init method, just above, where we see the two key actions that happen on page load:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/asime\/9707244.js\"><\/script><\/p>\n<p>In the init() function, we are referencing a local object we created called geo_code_app, which is at the top of the javascript file and is where we store our key methods.  The first thing we do is call the load_map function, which does pretty standard initialization of the google maps api so we get a full world map displayed on our page.  We won\u2019t explain that here, but you can see the code in the <strong><a href=\"https:\/\/github.com\/agilityfeat\/meetup-streaming\/blob\/master\/js\/meetupmashup\/app.js\" target=\"_blank\" rel=\"noopener noreferrer\">app.js<\/a><\/strong> file (link to file in repository)<\/p>\n<p>Below the call to the map initialization, is this statement:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/asime\/9707280.js\"><\/script><\/p>\n<p>This is the call we make to tell must.js to stream all rsvp callbacks to our method display_rsvp().   Display_rsvp lives in our app.js file, and let\u2019s look at the full method here:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/asime\/9707327.js\"><\/script><\/p>\n<p>This method will be called once for each rsvp that comes in from the Meetup streaming api.  An object named \u201crsvp\u201d will be passed into our method, and that object from Meetup contains information about the group name for the rsvp, the name of the meetup member who registered, the date and time of the meetup event, venue name, and more.   We take all that information and throw it into a few div tags for display as a marker on the map.<\/p>\n<p>The rsvp.venue object also contains latitude and longitude for the venue, which is obviously crucial for the google maps Marker object that we\u2019re going to create on the map.  Once the Marker and the associated InfoWindow are created, we call the google maps method to open those.<\/p>\n<p>We\u2019re dealing with real-time data from a very popular website, and so our map would fill up with markers if we didn\u2019t close them down automatically.  The last part of the display_rsvp method sets a timeout function on the Marker and InfoWindow so that they are removed from the map after a second to make space for the next set of RSVP\u2019s coming in from Meetup.<\/p>\n<p>This all makes for a fun and relatively simple way to see the power of WebSockets and real-time data streams.  What free and publicly available data streams do you want to see us build an example with?  Contact me at Arin@AgilityFeat.com and let me know, maybe we\u2019ll use it as the basis of a future blog post.<\/p>\n<p>You can see a live version of this example up at <strong><a href=\"http:\/\/RealTimeWeb.co\/geocode\" target=\"_blank\" rel=\"noopener noreferrer\">RealTimeWeb.co\/geocode<\/a><\/strong>, and you can learn more about building real-time web applications in our book at <strong><a href=\"http:\/\/RealTimeWeb.co\" target=\"_blank\" rel=\"noopener noreferrer\">RealTimeWeb.co<\/a><\/strong>.  In this book, our team shows you how to use publish\/subscribe networks and WebRTC to build real-time web applications that involve video, audio, and data applications.<\/p>\n<p>Many thanks go to Allan Naranjo for his work on this example.  Allan is a developer at AgilityFeat and lead developer on the example application for our <strong><a href=\"http:\/\/RealTimeWeb.co\" target=\"_blank\" rel=\"noopener noreferrer\">RealTimeWeb.co<\/a><\/strong> book.<\/p>","protected":false},"excerpt":{"rendered":"<p>Meetup provides an API for accessing streams of real-time data about its service which can be fun to play with. In this post, we\u2019re going to consume one of those streams via their javascript interface and mash up event registrations with a google map. When it\u2019s complete, the end result is going to look something [&hellip;]<\/p>","protected":false},"author":4,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"","_et_pb_old_content":"","_et_gb_content_width":""},"categories":[116],"tags":[108,114],"jetpack_featured_media_url":"","yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v15.7 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Using Meetup&#039;s streaming API to mashup event registrations - AgilityFeat Panama Software Test Center<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/agilityfeatpanama.com\/en\/blog\/2014\/03\/meetup-api-example\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Using Meetup&#039;s streaming API to mashup event registrations - AgilityFeat Panama Software Test Center\" \/>\n<meta property=\"og:description\" content=\"Meetup provides an API for accessing streams of real-time data about its service which can be fun to play with. In this post, we\u2019re going to consume one of those streams via their javascript interface and mash up event registrations with a google map. When it\u2019s complete, the end result is going to look something [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/agilityfeatpanama.com\/en\/blog\/2014\/03\/meetup-api-example\/\" \/>\n<meta property=\"og:site_name\" content=\"AgilityFeat Panama Software Test Center\" \/>\n<meta property=\"article:published_time\" content=\"2014-03-22T13:22:49+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/rtbook.herokuapp.com\/img\/word-animation.gif\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\">\n\t<meta name=\"twitter:data1\" content=\"6 minutes\">\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebSite\",\"@id\":\"https:\/\/34.200.113.64\/#website\",\"url\":\"https:\/\/34.200.113.64\/\",\"name\":\"AgilityFeat Panama Software Test Center\",\"description\":\"AgilityFeat Panama offers customized, multilevel web and mobile software testing for a variety of industries.\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":\"https:\/\/34.200.113.64\/?s={search_term_string}\",\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/agilityfeatpanama.com\/blog\/2014\/03\/meetup-api-example\/#primaryimage\",\"inLanguage\":\"en-US\",\"url\":\"http:\/\/rtbook.herokuapp.com\/img\/word-animation.gif\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/agilityfeatpanama.com\/blog\/2014\/03\/meetup-api-example\/#webpage\",\"url\":\"https:\/\/agilityfeatpanama.com\/blog\/2014\/03\/meetup-api-example\/\",\"name\":\"Using Meetup's streaming API to mashup event registrations - AgilityFeat Panama Software Test Center\",\"isPartOf\":{\"@id\":\"https:\/\/34.200.113.64\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/agilityfeatpanama.com\/blog\/2014\/03\/meetup-api-example\/#primaryimage\"},\"datePublished\":\"2014-03-22T13:22:49+00:00\",\"dateModified\":\"2014-03-22T13:22:49+00:00\",\"author\":{\"@id\":\"https:\/\/34.200.113.64\/#\/schema\/person\/c8d60d597071526db386b2b8a4afac64\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/agilityfeatpanama.com\/blog\/2014\/03\/meetup-api-example\/\"]}]},{\"@type\":\"Person\",\"@id\":\"https:\/\/34.200.113.64\/#\/schema\/person\/c8d60d597071526db386b2b8a4afac64\",\"name\":\"arin\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/34.200.113.64\/#personlogo\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/cc498e210512c707ed769986dd745896?s=96&d=mm&r=g\",\"caption\":\"arin\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","_links":{"self":[{"href":"https:\/\/agilityfeatpanama.com\/en\/wp-json\/wp\/v2\/posts\/884"}],"collection":[{"href":"https:\/\/agilityfeatpanama.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/agilityfeatpanama.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/agilityfeatpanama.com\/en\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/agilityfeatpanama.com\/en\/wp-json\/wp\/v2\/comments?post=884"}],"version-history":[{"count":0,"href":"https:\/\/agilityfeatpanama.com\/en\/wp-json\/wp\/v2\/posts\/884\/revisions"}],"wp:attachment":[{"href":"https:\/\/agilityfeatpanama.com\/en\/wp-json\/wp\/v2\/media?parent=884"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/agilityfeatpanama.com\/en\/wp-json\/wp\/v2\/categories?post=884"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/agilityfeatpanama.com\/en\/wp-json\/wp\/v2\/tags?post=884"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}