Netflix, jQuery, JSONP, and OData
- by Stephen Walther
At the last MIX conference, Netflix announced that they are exposing their catalog of movie information using the OData protocol. This is great news! This means that you can take advantage of all of the advanced OData querying features against a live database of Netflix movies. In this blog entry, I’ll demonstrate how you can use Netflix, jQuery, JSONP, and OData to create a simple movie lookup form. The form enables you to enter a movie title, or part of a movie title, and display a list of matching movies. For example, Figure 1 illustrates the movies displayed when you enter the value robot into the lookup form. Using the Netflix OData Catalog API You can learn about the Netflix OData Catalog API at the following website: http://developer.netflix.com/docs/oData_Catalog The nice thing about this website is that it provides plenty of samples. It also has a good general reference for OData. For example, the website includes a list of OData filter operators and functions. The Netflix Catalog API exposes 4 top-level resources: Titles – A database of Movie information including interesting movie properties such as synopsis, BoxArt, and Cast. People – A database of people information including interesting information such as Awards, TitlesDirected, and TitlesActedIn. Languages – Enables you to get title information in different languages. Genres – Enables you to get title information for specific movie genres. OData is REST based. This means that you can perform queries by putting together the right URL. For example, if you want to get a list of the movies that were released after 2010 and that had an average rating greater than 4 then you can enter the following URL in the address bar of your browser: http://odata.netflix.com/Catalog/Titles?$filter=ReleaseYear gt 2010&AverageRating gt 4 Entering this URL returns the movies in Figure 2. Creating the Movie Lookup Form The complete code for the Movie Lookup form is contained in Listing 1. Listing 1 – MovieLookup.htm <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Netflix with jQuery</title>
<style type="text/css">
#movieTemplateContainer div
{
width:400px;
padding: 10px;
margin: 10px;
border: black solid 1px;
}
</style>
<script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.js" type="text/javascript"></script>
<script src="App_Scripts/Microtemplates.js" type="text/javascript"></script>
</head>
<body>
<label>Search Movies:</label>
<input id="movieName" size="50" />
<button id="btnLookup">Lookup</button>
<div id="movieTemplateContainer"></div>
<script id="movieTemplate" type="text/html">
<div>
<img src="<%=BoxArtSmallUrl %>" />
<strong><%=Name%></strong>
<p>
<%=Synopsis %>
</p>
</div>
</script>
<script type="text/javascript">
$("#btnLookup").click(function () {
// Build OData query
var movieName = $("#movieName").val();
var query = "http://odata.netflix.com/Catalog" // netflix base url
+ "/Titles" // top-level resource
+ "?$filter=substringof('" + escape(movieName) + "',Name)" // filter by movie name
+ "&$callback=callback" // jsonp request
+ "&$format=json"; // json request
// Make JSONP call to Netflix
$.ajax({
dataType: "jsonp",
url: query,
jsonpCallback: "callback",
success: callback
});
});
function callback(result) {
// unwrap result
var movies = result["d"]["results"];
// show movies in template
var showMovie = tmpl("movieTemplate");
var html = "";
for (var i = 0; i < movies.length; i++) {
// flatten movie
movies[i].BoxArtSmallUrl = movies[i].BoxArt.SmallUrl;
// render with template
html += showMovie(movies[i]);
}
$("#movieTemplateContainer").html(html);
}
</script>
</body>
</html>
The HTML page in Listing 1 includes two JavaScript libraries:
<script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.js" type="text/javascript"></script>
<script src="App_Scripts/Microtemplates.js" type="text/javascript"></script>
The first script tag retrieves jQuery from the Microsoft Ajax CDN. You can learn more about the Microsoft Ajax CDN by visiting the following website:
http://www.asp.net/ajaxLibrary/cdn.ashx
The second script tag is used to reference Resig’s micro-templating library. Because I want to use a template to display each movie, I need this library:
http://ejohn.org/blog/javascript-micro-templating/
When you enter a value into the Search Movies input field and click the button, the following JavaScript code is executed:
// Build OData query
var movieName = $("#movieName").val();
var query = "http://odata.netflix.com/Catalog" // netflix base url
+ "/Titles" // top-level resource
+ "?$filter=substringof('" + escape(movieName) + "',Name)" // filter by movie name
+ "&$callback=callback" // jsonp request
+ "&$format=json"; // json request
// Make JSONP call to Netflix
$.ajax({
dataType: "jsonp",
url: query,
jsonpCallback: "callback",
success: callback
});
This code Is used to build a query that will be executed against the Netflix Catalog API. For example, if you enter the search phrase King Kong then the following URL is created:
http://odata.netflix.com/Catalog/Titles?$filter=substringof(‘King%20Kong’,Name)&$callback=callback&$format=json
This query includes the following parameters:
$filter – You assign a filter expression to this parameter to filter the movie results.
$callback – You assign the name of a JavaScript callback method to this parameter. OData calls this method to return the movie results.
$format – you assign either the value json or xml to this parameter to specify how the format of the movie results.
Notice that all of the OData parameters -- $filter, $callback, $format -- start with a dollar sign $.
The Movie Lookup form uses JSONP to retrieve data across the Internet. Because WCF Data Services supports JSONP, and Netflix uses WCF Data Services to expose movies using the OData protocol, you can use JSONP when interacting with the Netflix Catalog API. To learn more about using JSONP with OData, see Pablo Castro’s blog:
http://blogs.msdn.com/pablo/archive/2009/02/25/adding-support-for-jsonp-and-url-controlled-format-to-ado-net-data-services.aspx
The actual JSONP call is performed by calling the $.ajax() method. When this call successfully completes, the JavaScript callback() method is called.
The callback() method looks like this:
function callback(result) {
// unwrap result
var movies = result["d"]["results"];
// show movies in template
var showMovie = tmpl("movieTemplate");
var html = "";
for (var i = 0; i < movies.length; i++) {
// flatten movie
movies[i].BoxArtSmallUrl = movies[i].BoxArt.SmallUrl;
// render with template
html += showMovie(movies[i]);
}
$("#movieTemplateContainer").html(html);
}
The movie results from Netflix are passed to the callback method. The callback method takes advantage of Resig’s micro-templating library to display each of the movie results. A template used to display each movie is passed to the tmpl() method. The movie template looks like this:
<script id="movieTemplate" type="text/html">
<div>
<img src="<%=BoxArtSmallUrl %>" />
<strong><%=Name%></strong>
<p>
<%=Synopsis %>
</p>
</div>
</script>
This template looks like a server-side ASP.NET template. However, the template is rendered in the client (browser) instead of the server.
Summary
The goal of this blog entry was to demonstrate how well jQuery works with OData. We managed to use a number of interesting open-source libraries and open protocols while building the Movie Lookup form including jQuery, JSONP, JSON, and OData.