13

Soapi.JS2

    var context = new Soapi.ApiContext(yourApiKey);

    context.initialize(function() {
        context.official.stackApps
        .Users().ById(14)
        .invoke(function(users) {
            alert(users[0].display_name);
        });
    });

About

Soapi.JS2 is being released separately from Soapi.JS as it is a completely different take on accessing the API via JavaScript.

Soapi.JS is a very lightweight library that provides full API coverage.

Soapi.JS2 takes it to version 11 with a complete paradigm shift. It is a bit heavier and revolves around a fluent, relational query approach similar to that of the Soapi.CS library, providing a seamless view on the data served by the API.

License

MIT-X11/GPL

Documentation

The documentation is available in the download or can be viewed online here: http://soapi.info/Code/JS2/latest/docs/index.htm (documentation data is 5 MB so you may wish to access the help file from the download).

Download

A beta version available at http://soapi.info/Downloads/Soapi.JS2-beta-09-17-2010.zip.

Known Issues:

  • JavaScript implementation differences break the throttle on webkit browsers on Linux and Mac OS X

ChangeLog:

Soapi.JS2-beta-09-17-2010

  • minor code and documentation improvements
  • added sync initialize with JSON
  • implemented live samples in documentation (Soapi.JS2.docs.htm) - just a key handful so far, but it is pretty helpful to see a code snippet that you can run right there in the documentation. Now that the major code churn is over, I will be sprinkling relevant live samples throughout the documentation.

Soapi.JS2-beta-09-09-2010

  • Minor code improvements
  • Added JS2+CS UserIndexer and UserFinder demo to Soapi.CS samples (the Soapi.JS2 source lives with Soapi.CS)
  • Added Soapi2.Contrib.js to support samples

Soapi.JS2-beta-09-06-2010

  • Implemented minification to build process. soapi2.js is minified, soapi2.debug.js is commented and also acts as a Visual Studio IntelliSense source. No need to reference it, just let it sit in the same directory as the references Soapi2.js.

  • The minified size is still around 75 KB. Sorry, there is not much I can do as it is mostly public function and type names. If your server supports serving content gzipped it comes down to about 15 KB which is not too bad.

Soapi.JS2-beta-09-05-2010

  • Replaced Soapi.enableLogging and Soapi.disableLogging with Soapi.set_logCallback to reduce coupling and enable programmatic processing of Soapi events. See Soapi.log in the documentation for more information.

Soapi.JS2-beta-08-29-2010

  • Removed linq.js integration. The convenience was outweighed by the limitations imposed. It was a bad design decision in the first place. Simply create Enumerable when you need it by Enumerable.From(item)
  • Implemented Soapi.Interop (JS2->CS upload) expect dedicated post shortly.
  • Completed Soapi API documentation - started adding content
  • Added SyntaxHighlighter functionality to the documentation
  • Added API compatible custom JSON implementation

Soapi.JS2-beta-08-25-2010

  • completed preliminary documentation (/docs/index.htm)
  • adjusted default throttle to 30 per 6.5 as per current API tolerance

Soapi.JS2-beta-08-19-2010

  • merged scripts
  • made throttle/cache optional
  • added RepReport sample

Soapi.JS2-alpha-08-21-2010

  • implemented ThrotteCache

Soapi.JS2-alpha-08-20-2010

  • implemented paging fully
  • implemented .total method

Soapi.JS2-alpha-08-19-2010

  • initial release

Platform

JavaScript - Any browser that doesn't burn oil.

  • Windows - all current major browsers tested
  • Linux - Firefox and Chrome test good. Opera has issues.
  • OSX - Firefox, Chrome and Safari test good. Opera has issues.

Apparently Opera on Mac OS X and Linux burns oil. The library performs as expected but the internal throttle does not so caveat emptor until notified otherwise.

Remember, we are still in beta. Soapi.JS2 will be fully compatible with all common browsers on all platforms before RC1.

If you would like to provide compatibility feedback, you may run the integration test suite and add a comment or answer.

Contact

code poet

Code

Included in the alpha ZIP file is a full coverage test suite and a rather detailed walkthrough.

QuickStart

  • ApiContext - the root of all queries <-- start here
  • Api Coverage - how Soapi.JS2 maps to the API
  • Relational Queries - how to treat the API like a database
  • Async usage pattern - how to call and what to expect
  • Parameterization And Paging - how to get exactly what you want from the API
  • Stubs - filling placeholder objects
  • Throttling and Caching - concentrate on features, not plumbing..

Sample Code

  • Basic Usage Example 01 - demonstrates how to retrieve related data from the API using Soapi.JS2
  • RepReport - an example of using Soapi.JS2 to provide a custom view on your reputation points data.
  • SiteMonitor - an example of using Soapi.JS2 to retrieve and display status and statistics for all of the sites in the Stack Exchange network.

Apps that use Soapi.JS2

  • StackCenter - Your One Stop for Everything Stack Exchange
  • insert your app here (or leave a comment and I will do it)

Reference

  • Linq.js - This is highly recommended reading for anyone working with Soapi.JS2. Most sample code I provide will leverage this amazing little library.

PLEASE NOTE:

I try to provide quality documentation and I do appreciate the approval indicated by upvotes, but the Stack Overflow anti-vote fraud system is silently reversing votes cast in rapid succession.

So, if you would like to upvote the documentation, please do so in moderation, perhaps no more three or four votes per day?


14
  • 1
    +8 (as of now ;) - that's freakin awesome and impressive: how do you keep to manage churning out code at that rate and still maintain the highest quality and most thorough documentation around? I'm literally jealous ... Aug 20, 2010 at 13:46
  • Btw.: your QuickStart documentation style with the Next link on each answer is a very nifty idea to get the most out of the limits imposed by the answer format here! A possible improvement: technically it would be sufficient and especially easier/nicer to use to have relative in page links to the anchors only (e.g. href="#1441") - I don't know whether that hurts SEO, but I doubt search engines will handle these different than simply adding the implied absolute URL. Aug 20, 2010 at 13:47
  • @steffen - glad you like it. i think my apparent delivery rate may be helped by my fundamental need to identify and mitigate salient risks which enables the rapid development of a stable core upon which to layer abstractions. So, in reality, a lot of the work has been done, in increments, in plain sight over the last few months. regarding the links: i totally see your point and will adjust the links. relative anchors will simply scroll the page as opposed to refetching. Germans and detail.... Aug 20, 2010 at 14:09
  • 1
    Wow. Just when I thought Soapi.JS couldn't get more awesome... would it be easy (in your opinion) to port a Soapi.JS 1.0 app to 2.0? Just wonderin'... Aug 21, 2010 at 1:33
  • @geo - yes, it will be painless. I have an upgrade path in mind, I just need to finalize the throttle-cache before I publish the guidance. expect more info this weekend. Like I said, it will be painless. Aug 21, 2010 at 1:56
  • Those closing braces remind me of Lisp Aug 21, 2010 at 14:58
  • @geo - I am pondering on an HTML5 add-on that uses WebWorkers so that queries can be synchronous, e.g. site.Tags().ForEach(function(tag){ }); it will be a novelty except for use in browser extensions but should be interesting. But the closures will be dramatically reduced. Aug 21, 2010 at 15:12
  • @code: That would be quite neat! Unfortunately I almost never have the opportunity to write HTML5 code :( Pretty much all my stuff has to work with HTML4 or XHTML1. Aug 21, 2010 at 15:15
  • @code: Good news and bad news. The bad news is that StackCenter is no longer a Soapi.JS app. The good news is that it is now a Soapi2.JS app. Feel free to list it as an example application developed with Soapi2! Sep 5, 2010 at 22:23
  • @geo - Remember that we are still in beta and that the throttle does not yet work in Opera on either OSX or Linux. I guess I also need to get a minified build going for you. Expect that later tonight or tomorrow. Sep 5, 2010 at 22:34
  • @code: Thanks! But I already minified it. (With JSCompress.) Sep 5, 2010 at 22:35
  • @geo - did you reference it in the tests.integration.html and run it? if it passes, you should host the test suite on your site and run it from all devices you plan to support, mobile devices especially, and report failing tests by name and reason as an answer so I can target them. I need ya to do this if you can. Actually it would be great if you can also run the uncompressed unit tests listed in the post and report results for mobile devices you support. Sep 5, 2010 at 22:48
  • beta download link is broken
    – mstram
    Aug 22, 2014 at 2:07
  • @NathanOsman I cannot download documentation
    – Danny
    Apr 6, 2016 at 17:33

12 Answers 12

3

ApiContext

Query based Soapi.JS2 usage is rooted on the ApiContext object. This object maintains the list of Site that are available for query and provides central services.

You gotta have one and it must be initialized.

var context = new Soapi.ApiContext(apiKey);

context.initialize(function onSuccess(){

   // proceed 

}, function onFailed(error){
   // handle error;
});

Once you have an intialized context, all query based activity will be rooted on a Site.

The ApiContext provides access to all Stack Exchange sites via the .context.find(apiendpoint) method which will return a Site object.

var site = context.find("api.stackapps.com");

The official sites, e.g. those not living in Area51, also have a convenience accessor, context.official.xxxx.

var site = context.official.stackOverflow;

Living on the Site object are the properties you would expect, with reference to the data structure returned by /sites, as well as query methods that provide full coverage of the Stack Exchange API.

NEXT: API Query Coverage

3

Api Coverage

Every path in the API has a reciprocal query rooted on the Soapi.Domain.Site object.

The query syntax has been constructed to be as intuitive as possible.

A complete map of Soapi.JS2 query path to API rest path follows.

Api Routes

  • /answers/{id}
    site.Answers().ById(704).invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/answers/704
     
  • /answers/{id}/comments
    site.Answers().ById(704).Comments().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/answers/704/comments
  • /badges
    site.Badges().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/badgeshttp://api.stackapps.com/1.0/badges
     
  • /badges/{id}
    site.Badges().ById(9).invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/badges/9http://api.stackapps.com/1.0/badges/9
     
  • /badges/name
    site.Badges().ByName().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/badges/name
     
  • /badges/tags
    site.Badges().ByTags().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/badges/tags
     
  • /comments/{id}
    site.Comments().ById(1489).invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/comments/1489
     
  • /errors/{id}
    errors has a path on the ApiContext but is not exposed via Site
     
  • /posts/{id}/comments
    site.Posts().ById(704).Comments().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/posts/704/comments
     
  • /questions
    site.Questions().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/questions
     
  • /questions/{id}
    site.Questions().ById(386).invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/questions/386
     
  • /questions/{id}/answers
    site.Questions().ById(386).Answers().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/questions/386/answers
     
  • /questions/{id}/comments
    site.Questions().ById(386).Comments().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/questions/386/comments
     
  • /questions/{id}/timeline
    site.Questions().ById(386).Timeline().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/questions/386/timeline
     
  • /questions/Unanswered
    site.Questions().Unanswered().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/questions/unanswered
     
  • /Revisions/{id}
    site.Posts().ById(386).Revisions().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/revisions/386
     
  • /Revisions/{id}/{revisionguid}
    site.Posts().ById(386).Revisions().ByRevisionGuid("e452e755-a3fe-45d0-976f-e319ec460200").invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/revisions/386/e452e755-a3fe-45d0-976f-e319ec460200
     
  • /search
    site.Search().intitle("soapi.js").invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/search?intitle=soapi.js
     
  • /stats
    site.Stats().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/stats
     
  • /tags
    site.Tags().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/tags
     
  • /users
    site.Users().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users
     
  • /users/{id}
    site.Users().ById(14).invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14
     
  • /users/{id}/answers
    site.Users().ById(14).Answers().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/answers
     
  • /users/{id}/badges
    site.Users().ById(14).Badges().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/badges
     
  • /users/{id}/comments
    site.Users().ById(14).Comments().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/comments
     
  • /users/{id}/comments/{toid}
    site.Users().ById(14).CommentsTo(18).invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/comments/18
     
  • /users/{id}/favorites
    site.Users().ById(14).Favorites().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/favorites
     
  • /users/{id}/mentioned
    site.Users().ById(14).Mentioned().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/mentioned
     
  • /users/{id}/questions
    site.Users().ById(14).Questions().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/questions
     
  • /users/{id}/reputation
    site.Users().ById(14).Reputation().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/reputation
     
  • /users/{id}/tags
    site.Users().ById(14).Tags().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/tags
     
  • /users/{id}/timeline
    site.Users().ById(14).Timeline().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/timeline
     
  • /users/moderators
    site.Moderators().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/moderators
     

StackAuth Routes

  • /sites - always available after ApiContext initialization
    ctx.sites;
    http://stackauth.com/1.0/sites
     
  • /users/{id}/associated
    site.Users().ByIdAssociated("e58345f5-0f7b-4261-b449-3959c596f91f").invoke(/*handlers here*/);
    http://stackauth.com/1.0/users/e58345f5-0f7b-4261-b449-3959c596f91f/associated
     

NEXT: Relational Queries

3

Relational Queries

When JSON results are received from the API, they are used to build domain objects from the Soapi.Domain namespace.

Upon these objects are strategically placed queries, the same queries you will find rooted on the Site object, that are pre-keyed to return data for that object.

NOTE:

Query methods are upper-cased to make it clear that it is a query method, not a property.

  • user.reputation (a json property)
  • user.Reputation() (an api query)
     
  • answer.comments (a json property)
  • answer.Comments() (an api query)

Soapi.Domain.User

  • user.Answers().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/answers
     
  • user.Badges().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/badges
     
  • user.Comments().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/comments
     
  • user.CommentsTo(18).invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/comments/18
     
  • user.Favorites().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/favorites
     
  • user.Mentioned().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/mentioned
     
  • user.Questions().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/questions
     
  • user.Reputation().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/reputation
     
  • user.Tags().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/tags
     
  • user.Timeline().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/users/14/timeline
     

Soapi.Domain.Answer

  • answer.Comments().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/answers/1305/comments
     
  • answer.Revisions().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/revisions/1305
     
  • answer.Revisions().ByRevisionGuid("0eabd918-e2c9-4176-8dbd-d56fc8bd1dd2").invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/revisions/1305/0eabd918-e2c9-4176-8dbd-d56fc8bd1dd2
     

Soapi.Domain.Question

  • question.Answers().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/questions/386/answers
     
  • question.Comments().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/questions/386/comments
     
  • question.Timeline().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/questions/386/timeline
     
  • question.Revisions().invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/revisions/386
     
  • question.Revisions().ByRevisionGuid("e452e755-a3fe-45d0-976f-e319ec460200").invoke(/*handlers here*/);
    http://api.stackapps.com/1.0/revisions/386/e452e755-a3fe-45d0-976f-e319ec460200
     

Where you see .invoke(/*handlers here*/) is where you would implement a standard asynchronous pattern as described in the next post.....

NEXT: Soapi.JS2 Async Pattern

2

Parameterization And Paging

Overview:

Soapi.JS2 facilitates query parameterization using 'fluent' or 'chained' setter methods and maintains full parity with the documention found on the API help pages.

For example: the route /answers/{id} is serviced by Soapi.JS2 as follows:

site.Answers()
 .ById(200)
 .withBody(true)
 .withComments(true)
 .fromdate("1/1/1970")           // <-- any parsable date value
 .todate(Date.parse("1/1/2040")) // <-- or a date (or number)
 .sort("votes")
 .min(0)
 .max(10)
 .order("desc")
 .page(2)
 .pagesize(100);

ID Handling:

For queries that require a 'vectorized id', you may specify a single number or string, an array of numbers or strings or simply provide a semi-colon delimited list as described on the help pages.

site.Questions().ById(1).invoke();
site.Questions().ById("1").invoke();
site.Questions().ById([1, 2]).invoke();
site.Questions().ById("1;2").invoke();

The same principal applies to any API parameter that calls for a list of string.

site.Questions().tagged(["app", "library"]).invoke();
site.Questions().tagged(["app;library"]).invoke();

Content Parameters

The boolean content flags are prefixed with 'with' to avoid collisions

site
.Questions()
.withAnswers(true)
.withBody(true)
.withComments(true)
.invoke();

Date Handling:

All dates in Soapi, in and out, are in JavaScript date format and are in Local time. If the UTC value is needed, simply use the methods on the Date object.

Any parameter that requires or will accept a Date (unix timestamp on the API side) values will accept any parsable representation of a date.

site
.Questions()
.fromdate("1/1/1980")
.todate(Date.parse("1/1/2020"))
.invoke();

Min/Max

Min/Max, the types and values, are contextually bound to the sort that is applied to the query.

When Date values are indicated by the selected sort, the same date parsing features described above apply.

For more information about min/max see: How to use min/max with sort

Paging:

In addition to the .page() and .pagesize() parameters, which behave as documented on the API help pages, Soapi.JS2 supports fetching of multiple pages using the .pagecount() parameter.

site.Questions()
.tagged("c#").pagecount(3)
.invoke(function(data) {
   // three pages worth of items
});

To retrieve all available pages, specify .pagecount(0).

Soapi.JS2 ALPHA does not yet provide a mechanism for cancellation of a paging operation, so you will want to be aware of the potential size of the dataset you are asking for before sending an 'all pages' requests, which makes the .total() method even more useful....

All queries expose an async .total() method that will return the total count of records that match the query as parameterized.

site.Questions().tagged("c#").total(function(total) {
    alert("there are " + total + " c# questions");
});

NEXT: Stubs (partially populated objects)

2

Stubs

Many of the json results coming from the api contain either 'stub' objects, typically user objects, or just an object reference id.

e.g.

{
 "total": 879225,
 "page": 1,
 "pagesize": 30,
 "questions": [
  {
   "tags": [
    "c#"
   ],
   "answer_count": 12,
   "accepted_answer_id": 7, // <-- reference
   "question_id": 4,
   "owner": {               // <-- stub
    "user_id": 8,
    "user_type": "registered",
    "display_name": "Eggs McLaren",
    "reputation": 193,
    "email_hash": "c7ec7dec48168d2a7d792e2b0acc78d1"
   },
   "creation_date": 1217540572,
   "last_edit_date": 1248550390,

Two measures have been taken to facilitate retrieval of these objects:

  1. Every Soapi.Domain.User object has a .Fetch() method which can be used to retrieve the full object, which you may use to overwrite the stub. e.g. question.user.Fetch().invoke(/**/);

  2. Appropriately named and keyed query objects are emplaced on objects that contain unambiguouous reference id fields.

    • Tag.User().invoke(/*handlers here*/);
    • UserTimeline.User().invoke(/*handlers here*/);
    • RepChange.User().invoke(/*handlers here*/);
    • Migrated.Question().invoke(/*handlers here*/);
    • Question.AcceptedAnswer().invoke(/*handlers here*/);
    • Answer.Question().invoke(/*handlers here*/);

If you need to programatically determine if an object is a 'stub', simply check if object._isStub === true.

NEXT: Throttling and Caching

2

Sample Code: SiteMonitor

alt text

SiteMonitor is an example of using Soapi.JS2 to retrieve and display status and statistics for all of the sites in the Stack Exchange network.

SiteMonitor.htm can be found in the Soapi.JS2 source download.

<!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></title>
    <script src="third-party/linq.js" type="text/javascript"></script>
    <script src="Scripts/Soapi2.js" type="text/javascript"></script>
    <script src="Scripts/Soapi2.Throttle.js" type="text/javascript"></script>
    <style type="text/css">
        html {
            font-family: Sans-Serif;
            font-size: 12px;
        }
        .stats {
            font-size: 9px;
            border: solid 1px silver;
            padding-right: 3px;
            text-align: right;
        }
        tr.header td {
            border-bottom: solid 2px black;
            padding: 3px;
        }
    </style>

    <script type="text/javascript">
        function doYourThang_DoWhatYaWannaDo() {

            var apiKey = "qgAq_KfDu0KYzlNG-qaTuw";

            var ctx = new Soapi.ApiContext(apiKey);

            ctx.initialize(function() {

                // all available sites have been loaded from stackauth

                // to track loading of stats
                var counter = 0;

                // now we load the .stats property of the Site object

                Enumerable.From(ctx.sites).ForEach(function(site) {

                    counter++;

                    site.Stats().invoke(function(data, site_as_context) {
                        // tack the stats onto the site object
                        site_as_context.stats = data.First();
                        document.getElementById("output").innerHTML
                        = "loaded stats for " + site_as_context.name;

                        soupYet();
                    },
                    function(error, site_as_context) { // site sent as userContext
                        // keep the user informed
                        document.getElementById("output").innerHTML
                        = "failed to loaded stats for " + site_as_context.name;

                        // the request failed but we must decrement the counter
                        soupYet();

                    }, site /* as context */);
                });

                // output code
                function soupYet() {

                    // check to see if all sites have been loaded
                    if (--counter == 0) {
                        // dump all to table

                        var output = "<table cellpadding='0' cellspacing='0' style='border:solid 1px silver;'> \
                                    <tr class='header'><td>Site</td><td>State</td><td>Tags</td><td>Logo</td><td>API</td> \
                                    <td>v.p.d</td><td>users</td><td>questions</td><td>q.p.m.</td><td>accepted</td> \
                                    <td>unanswered</td><td>answers</td><td>a.p.m</td><td>comments</td><td>votes</td> \
                                    <td>badges</td><td>b.p.m.</td></tr>";

                        Enumerable.From(ctx.sites).ForEach(function(site) {
                            if (!site.stats) {
                                // handle 503 errors. I wonder what rookie decided it was a good idea
                                // to deploy major UNTESTED infrastructure changes on the weekend.
                                site.stats = new Soapi.Domain.Stats(site);
                            }

                            output += "<tr>"
                                + "<td><img src='" + site.icon_url + "' style='width:32px;height:32px;'/>"
                                + "<a href='" + site.site_url + "' style='color:" + site.styling.link_color
                                + ";' target='_blank' title='" + site.description.replace("'", "`") + "'>" + site.name + "</a></td>"
                                + "<td class='stats'>" + site.state + "</td>"
                                + "<td class='stats'><span style='color:" + site.styling.tag_foreground_color + ";background-color:"
                                + site.styling.tag_background_color + ";'>[tags]</span></td>"
                                + "<td class='stats'>&nbsp;<a href='" + site.logo_url + "' target='_blank'>logo</a></td>"
                                + "<td class='stats'>&nbsp;<a href='" + site.api_endpoint + "/" + site.stats.api_version.version
                                + "/help' target='_blank'>" + site.stats.api_version.version + " " + site.stats.api_version.revision + "</a></td>"
                                + "<td class='stats'>" + Math.ceil(site.stats.views_per_day) + "</td>"
                                + "<td class='stats'>" + site.stats.total_users + "</td>"
                                + "<td class='stats'>" + site.stats.total_questions + "</td>"
                                + "<td class='stats'>" + Math.ceil(site.stats.questions_per_minute) + "</td>"
                                + "<td class='stats'>" + site.stats.total_accepted + "</td>"
                                + "<td class='stats'>" + site.stats.total_unanswered + "</td>"
                                + "<td class='stats'>" + site.stats.total_answers + "</td>"
                                + "<td class='stats'>" + Math.ceil(site.stats.answers_per_minute) + "</td>"
                                + "<td class='stats'>" + site.stats.total_comments + "</td>"
                                + "<td class='stats'>" + site.stats.total_votes + "</td>"
                                + "<td class='stats'>" + site.stats.total_badges + "</td>"
                                + "<td class='stats'>" + Math.ceil(site.stats.badges_per_minute) + "</td>"
                                + "</tr>";
                        });

                        output += "</table>";
                        document.getElementById("output").innerHTML = output;
                    }
                };
            }, function(error) {
                alert(error.message);
            });
        };
                
    </script>

</head>
<body onload="doYourThang_DoWhatYaWannaDo()">
    <div id="output">
    </div>
</body>
</html>
1

Basic Usage Example 01

var ctx = new Soapi.ApiContext(apiKey);
ctx.initialize(function() {  // ctx.initialize loads sites from stackauth

    var site = ctx.find("api.stackapps.com"); // site is Soapi.Domain.Site
    // if you are using Visual Studio, you can use the constructor of any domain '
    // object as a Cast operation to enable intellisense...
    site = new Soapi.Domain.Site(ctx.find("api.stackapps.com"));

    // data is fetched via intuitively named query methods rooted on the site
    // lets start out with a _simple_ example

    var example_01 = document.getElementById("example_01");
    var list = document.createElement("ul");
    example_01.appendChild(list);

    site.Users().invoke(function(users) { // get a page of users

        users.ForEach(function(user) {

            // add a list item for the user
            var userListItem = document.createElement("li");
            list.appendChild(userListItem);
            userListItem.innerHTML = user.display_name;

            // add a nested list to hold the user's questions
            var userQuestionsList = document.createElement("ul");
            userListItem.appendChild(userQuestionsList);

            // get a page of the user's questions
            user.Questions().invoke(function(questions) {

                questions.ForEach(function(question) {

                    // add the question
                    var questionListItem = document.createElement("li");
                    questionListItem.innerHTML = question.title;
                    userQuestionsList.appendChild(questionListItem);

                });

            });
        });
    });
});

Output

  • George Edison
    • My reputation graph is messed up.
    • How to retrieve notifications for a given user.
    • StackList - List Your Apps, Track Bugs, and More!
    • lots of questions ...
  • Kevin Montrose
    • World's Worst StackOverflow Clone
    • Getting your [app] to show up in the AppStore
    • a few questions ...
  • code poet
    • Soapi.JS2 - a whole nother thang. alpha preview.
    • /users/{id}/reputation != xxx.com/reputation
    • How to generate a list of users ranked by bug report submissions.
    • lots of questions ...
  • lfoust
    • Stacky - A .Net client library
    • nottagged parameter not documented
  • systempuntoout
    • questions method returns error 4000
    • StackPrinter: The Stack Exchange Printer Suite
    • lots of questions ...

... ... and on and on

1

Async usage pattern

All query methods use the following typical async pattern

//

site.Badges().invoke(function onSuccess(items, userContext, methodName) {

    // handle items array of domain objects
    // if linq.js is loaded the items array will be an instance of
    // Enumerable<domain object>

}, function onFailed(error, userContext, methodName) {

    // handle error - which looks like this:

    //    error : {
    //        message: "the message",
    //        code: 500, // the status code
    //        url: "the url that failed"
    //    };


}, { someUserContext: "foo" });

//

The static ApiProxy provides a place to hang default/global handlers. Certain application structures lend themselves well to a centralized response handler.

// on Soapi.Net.ApiProxy, you may set default callbacks. e.g.

Soapi.Net.ApiProxy
 .set_defaultSucceededCallback(function(data, userContext, methodName) {
    // default response handler
});

Soapi.Net.ApiProxy
 .set_defaultFailedCallback(function(error, userContext, methodName) {
    // default error handler
});

Once you are comfortable with the async pattern used by Soapi.JS2, a clear understanding of query parameterization is in order.....

NEXT: Parameterization and Paging

1

Sample Code: RepReport

alt text

RepReport is an example of using Soapi.JS2 to provide a custom view on your reputation data.

You can find RepReport.htm in the source download.

<!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>Soapi2.RepReport</title>
    <script src="third-party/jquery-1.4.1.js" type="text/javascript"></script>
    <script src="third-party/linq.js" type="text/javascript"></script>
    <script src="Scripts/Soapi2.js" type="text/javascript"></script>
    <script src="Scripts/Soapi2.Throttle.js" type="text/javascript"></script>
    <style type="text/css">
        body {
            font-family: Sans-Serif;
        }
        .month-row, .month-row td {
            font-size: 14px;
            background-color: #4682B4;
            color: White;
            font-weight: bold;
        }
        .day-row, .day-row td {
            font-size: 12px;
            background-color: #B0C4DE;
            color: black;
            font-weight: bold;
        }
        .event-row, .event-row td {
            border-bottom: dotted 1px #EFEFEF;
            font-size: 10px;
        }
        .post-cell {
            text-align: left;
            font-size: 10px;
        }
        .right td {
            text-align: right;
        }
    </style>

    <script type="text/javascript">
        // Soapi JavaScript Library V2-ALPHA
        // http://stackapps.com/questions/1433
        // http://soapi.info/
        //-------------------------------------------------------------------------------

        (function repReport() {

            var u$ = Soapi.Utilities;
            var d$ = Soapi.Domain;
            var ctx = new Soapi.ApiContext("qgAq_KfDu0KYzlNG-qaTuw");

            $(function() {
                ctx.initialize(function() {
                    $("#input")
                        .append("<br/><label for='site-dropdown'>Site:</label><br/>")
                        .append($("<select id='site-dropdown'/>")
                            .append(Enumerable.From(ctx.sites)
                                .OrderBy("$.name")
                                .Select(function(s) {
                                    return "<option value='" + s.api_endpoint + "'>" + s.name + "</option>\n";
                                }).ToString()))
                        .append("<br/><label for='input-user-id'>UserId:</label>")
                        .append("<br/><input type='text' id='input-user-id' title='enter user id'/><br/>")
                        .append($("<input type='button' id='input-draft-report' value='draft report'/>")
                            .click(function(e) {
                                chartReport($("#site-dropdown").val(), $("#input-user-id").val());
                            }));
                });
            });


            function chartReport(endpoint, userId) {

                ctx.find(endpoint).Users().ById(userId).invoke(function(users) {

                    var user = users.FirstOrDefault();
                    if (typeof (user) == 'undefined') {
                        $("#report").html("").append($("<h3/>").html("No user with id " + userId + " found on " + endpoint));
                        return;
                    }
                    $("#report").html("").append($("<h3/>").html(user.display_name));


                    user.Reputation().fromdate("1/1/1990").todate("1/1/2020").pagecount(0).pagesize(100)
                        .invoke(function(repchanges) {

                            repchanges = repchanges.Reverse();

                            var table = $("<table cellpadding='0' cellspacing='0' style='width:100%'></table>").appendTo("#report");

                            // first, we want to group by day
                            repchanges.GroupBy(function(r) {
                                return u$.formatTimeStamp(r.on_date, 2)
                            }) // then group thos groups by month
                            .GroupBy(function(g) {
                                return u$.formatTimeStamp(g.First().on_date, 1)
                            })
                            .ForEach(function(monthGroup) {
                                // add a row for the month with sum of month's rep
                                $("<tr class='month-row'><td>" + monthGroup.Key()
                                    + "</td><td class='right'>" + monthGroup.Sum(function(g) {
                                        return g.Sum(function(i) {
                                            return i.positive_rep - i.negative_rep;
                                        });
                                    })
                                    + "</td><td></td><td></td></tr>").appendTo(table);

                                monthGroup.ForEach(function(dayGroup) {
                                    // add a row for day with sum of day's rep
                                    $("<tr class='day-row'><td>" + dayGroup.Key()
                                        + "</td><td class='right'>" + dayGroup.Sum(function(i) {
                                            return i.positive_rep - i.negative_rep;
                                        }) + "</td><td></td><td></td></tr>")
                                        .appendTo(table);

                                    dayGroup.ForEach(function(r) {
                                        // add a row for each event
                                        $("<tr class='event-row' ><td>" + r.on_date.toTimeString()
                                            + "</td><td class='right'>+" + r.positive_rep
                                            + "</td><td class='right'>-" + r.negative_rep
                                            + "</td><td class='post-cell'>" + r.title
                                            + "</td></tr>")
                                            .appendTo(table);
                                    });
                                });
                            });
                        });
                });
            };
        })();
    </script>

</head>
<body>
    <h2>
        RepReport</h2>
    <p>
        powered by <a href="http://stackapps.com/questions/1433">Soapi.JS2</a></p>
    <div id="input">
    </div>
    <div id="report">
    </div>
</body>
</html>
1

I can't seem to find a ByIdMentioned method for Users objects. Am I missing something or is this a yet-to-be-added feature? Never mind - I found it!


Also, I'm having trouble getting Area51 sites to work with the find() method:

>> context.find("http://api.cooking.stackexchange.com")

returns:

undefined

In Google Chrome 6 beta. (On Ubuntu 10.04, of course :))

Never mind - I figured that one out too... I forgot to call initialize()

7
  • Boy... I'm having a bad day :) Sep 4, 2010 at 4:01
  • with only one exception, i believe, any time there is an id involved .ById(id).XXX is the pattern. I am not in front of the code right now but I believe the exception is Users.ByIdAssociated and I am still undecided about that. I would like to maintain convention but IIRC there was an issue. I am not in front of the code right now, but that may be the only functional change to the library that may be coming down the pike. It will be noted in the change log. Sep 4, 2010 at 4:31
  • Does Soapi2.JS have throttling enabled by default or do I have to call some method first? Just wonderin' because some of the queries are completely failing. (Like they're not getting a response from the server.) Sep 4, 2010 at 4:36
  • yes, in the current download the throttle is set to 30 per 7 seconds. If you are getting failures, make sure that it isn't one of those long running queries such as question with answers and comments or search which can take up to 40 seconds to complete. Try watching the traffic with an external tool. again, we may be running into a platform specific javascript implementation issue. I have yet to run tests on linux or apple. but this is not terribly likely (famous last words). are your failures on specific methods, if so, which? Sep 4, 2010 at 4:59
  • seems that the tests are failing in webkit so i would refrain from public usage at the moment. Sep 4, 2010 at 5:20
  • @code: the methods are: Users.ById().Mentioned and Users.ById().Reputation Sep 4, 2010 at 5:29
  • @code: Well, I finished the new version of StackCenter. I'm not releasing it into the wild quite yet - considering the throttling problems - but it's ready to go otherwise. You can get a sneak peek here. Sep 4, 2010 at 20:40
1

Throttling and Caching

In order to provide maximum data throughput and at the same time protect your application from, and obviate coding for, server errors due to throttle rate violations and/or polling abuse, Soapi.JS2 implements a caching throttle implementation, Soapi.Net.ThrottleCache, that throttles on a sliding window with a governor supplemented by a per route configurable request cache.

Throttling and caching is optional but strongly recommended and is enabled by default.

All http requests are handled by a static instance of ThrottleCache that lives @ Soapi.Net.Throttle.

e.g.

// internal code
Soapi.Net.Throttle = 
    new Soapi.Net.ThrottleCache(throttleWindowTime, throttleWindowCount, 
    maxActiveRequests, defaultCacheDuration);

// Allow [throttleWindowCount] requests per [throttleWindowTime] 
//       while active requests < maxActiveRequests 

// Cache all results [defaultCacheDuration] if an explicit cache rule for that route, 
// e.g. '/users/{id}' is not found.


Soapi.Net.Throttle.enqueue(request);

Throttling

As a request is accepted by the throttle, the internal request cache is checked for matching results, if found the request is satisified immediately and the workflow is complete.

If a cache hit is not found, the request is placed in a queue which is processed at a regular polling interval. The resolution of this polling timer is arbitrary and can be changed by stopping and restarting the throttle.

Soapi.Net.Throttle.suspend();
Soapi.Net.Throttle.start(pollingIntervalInMs);

At polling time, there are 3 conditions that must be met in order for a request to be sent to the API:

  1. that the cache has not received matching data while the request sat in the queue. If this is the case, the request is satsified from the cache.
  2. that the number of active requests does not exceed the configured maximum active request count. If the number of active requests meets or exceeds the max allowed, processing of the queue is deferred.
  3. that the interval, in ms, between now and the nth previous request, where nth is the window count, is equal to or greater than the configured window interval, otherwise processing of the queue is deferred.

When these conditions are met, a request is popped from the queue and sent to the API.

Caching

Each successful request, after the json has been converted into domain objects, it is placed in a request cache, keyed by the url.

Each cache item is scheduled to be removed from the cache [defaultCacheDuration] milliseconds after it is placed, unless an explicit cache rule has been added for that route, e.g.

// cache stackauth.com/1.0/sites for 5 minutes
Soapi.Net.Throttle.addCacheRule('/sites', 360000); 

You can 'seed' the cache with data for fault tolerance or testing purposes by using the same mechanism used internally when a successful response is received and processed, e.g.

Soapi.Net.Throttle.signal(manuallyConstructedSites, '/sites', 'http://stackauth.com/sites?key=yourkey')

The single static instance of ThrottleCache will be used by any and all ApiContext instances you may have in play on any single page.


Example:

Throttle-cache log from AlphaQuickStart.htm with 30 requests per 5 second window with max 15 active requests and a 60 second default cache duration - note the cache scavenging happening as the browser was left idle.

throttle: 2010-07-20 20:27:44.830 - request processed: interval: 1282361264830ms
throttle: 2010-07-20 20:27:45.027 - request processed: interval: 197ms
throttle: 2010-07-20 20:27:45.037 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.047 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.057 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.067 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.077 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.087 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.097 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.107 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.117 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.128 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:45.138 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.148 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.158 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.168 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:45.178 - active queue full
throttle: 2010-07-20 20:27:45.268 - request processed: interval: 100ms
throttle: 2010-07-20 20:27:45.278 - active queue full
throttle: 2010-07-20 20:27:45.568 - request processed: interval: 300ms
throttle: 2010-07-20 20:27:45.578 - active queue full
throttle: 2010-07-20 20:27:46.730 - request processed: interval: 1162ms
throttle: 2010-07-20 20:27:46.740 - active queue full
throttle: 2010-07-20 20:27:47.010 - request processed: interval: 280ms
throttle: 2010-07-20 20:27:47.020 - active queue full
throttle: 2010-07-20 20:27:47.040 - request processed: interval: 30ms
throttle: 2010-07-20 20:27:47.050 - active queue full
throttle: 2010-07-20 20:27:47.365 - request processed: interval: 325ms
throttle: 2010-07-20 20:27:47.376 - active queue full
throttle: 2010-07-20 20:27:47.449 - request processed: interval: 84ms
throttle: 2010-07-20 20:27:47.465 - active queue full
throttle: 2010-07-20 20:27:47.535 - request processed: interval: 86ms
throttle: 2010-07-20 20:27:47.546 - active queue full
throttle: 2010-07-20 20:27:47.746 - request processed: interval: 211ms
throttle: 2010-07-20 20:27:47.756 - active queue full
throttle: 2010-07-20 20:27:47.997 - request processed: interval: 251ms
throttle: 2010-07-20 20:27:48.007 - active queue full
throttle: 2010-07-20 20:27:48.097 - request processed: interval: 100ms
throttle: 2010-07-20 20:27:48.107 - active queue full
throttle: 2010-07-20 20:27:48.327 - request processed: interval: 230ms
throttle: 2010-07-20 20:27:48.337 - active queue full
throttle: 2010-07-20 20:27:48.898 - request processed: interval: 571ms
throttle: 2010-07-20 20:27:48.908 - active queue full
throttle: 2010-07-20 20:27:48.921 - request processed: interval: 23ms
throttle: 2010-07-20 20:27:48.939 - active queue full
throttle: 2010-07-20 20:27:49.020 -  waiting 810ms
throttle: 2010-07-20 20:27:49.832 - request processed: interval: 911ms
throttle: 2010-07-20 20:27:49.843 -  waiting 184ms
throttle: 2010-07-20 20:27:50.027 - request processed: interval: 195ms
throttle: 2010-07-20 20:27:50.037 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.047 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.057 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.067 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.077 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.087 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.097 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.107 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.117 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.128 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:50.138 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.148 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:50.158 - active queue full
throttle: 2010-07-20 20:27:50.170 - request processed: interval: 22ms
throttle: 2010-07-20 20:27:50.181 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:50.192 - active queue full
throttle: 2010-07-20 20:27:50.262 -  waiting 6ms
throttle: 2010-07-20 20:27:50.273 - request processed: interval: 92ms
throttle: 2010-07-20 20:27:50.284 -  waiting 284ms
cache: 2010-07-20 20:27:50.387 - served from cache: http://api.stackapps.com/1.0/users/14/answers
cache: 2010-07-20 20:27:50.388 - served from cache: http://api.stackapps.com/1.0/users/14/badges
cache: 2010-07-20 20:27:50.388 - served from cache: http://api.stackapps.com/1.0/users/14/comments
cache: 2010-07-20 20:27:50.388 - served from cache: http://api.stackapps.com/1.0/users/14/comments/18
cache: 2010-07-20 20:27:50.388 - served from cache: http://api.stackapps.com/1.0/users/14/favorites
cache: 2010-07-20 20:27:50.388 - served from cache: http://api.stackapps.com/1.0/users/14/mentioned
cache: 2010-07-20 20:27:50.389 - served from cache: http://api.stackapps.com/1.0/users/14/reputation
cache: 2010-07-20 20:27:50.389 - served from cache: http://api.stackapps.com/1.0/users/14/tags
cache: 2010-07-20 20:27:50.389 - served from cache: http://api.stackapps.com/1.0/users/14/timeline
throttle: 2010-07-20 20:27:50.575 - request processed: interval: 302ms
throttle: 2010-07-20 20:27:50.586 -  waiting 1144ms
cache: 2010-07-20 20:27:51.397 - served from cache: http://api.stackapps.com/1.0/questions/386/answers
cache: 2010-07-20 20:27:51.397 - served from cache: http://api.stackapps.com/1.0/questions/386/comments
cache: 2010-07-20 20:27:51.397 - served from cache: http://api.stackapps.com/1.0/questions/386/timeline
cache: 2010-07-20 20:27:51.398 - served from cache: http://api.stackapps.com/1.0/revisions/386
cache: 2010-07-20 20:27:51.398 - served from cache: http://api.stackapps.com/1.0/revisions/386/e452e755-a3fe-45d0-976f-e319ec460200
throttle: 2010-07-20 20:27:51.732 - request processed: interval: 1157ms
throttle: 2010-07-20 20:27:51.742 -  waiting 268ms
throttle: 2010-07-20 20:27:52.012 - request processed: interval: 280ms
throttle: 2010-07-20 20:27:52.022 -  waiting 18ms
throttle: 2010-07-20 20:27:52.042 - request processed: interval: 30ms
throttle: 2010-07-20 20:27:52.052 -  waiting 313ms
throttle: 2010-07-20 20:27:52.373 - request processed: interval: 331ms
throttle: 2010-07-20 20:27:52.383 -  waiting 66ms
throttle: 2010-07-20 20:27:52.453 - request processed: interval: 80ms
throttle: 2010-07-20 20:27:52.463 -  waiting 72ms
throttle: 2010-07-20 20:27:52.544 - request processed: interval: 91ms
throttle: 2010-07-20 20:27:52.554 -  waiting 192ms
throttle: 2010-07-20 20:27:52.755 - request processed: interval: 211ms
throttle: 2010-07-20 20:27:52.766 -  waiting 231ms
throttle: 2010-07-20 20:27:53.001 - request processed: interval: 246ms
throttle: 2010-07-20 20:27:53.011 -  waiting 86ms
throttle: 2010-07-20 20:27:53.101 - request processed: interval: 100ms
throttle: 2010-07-20 20:27:53.112 -  waiting 215ms
throttle: 2010-07-20 20:27:53.334 - request processed: interval: 233ms
throttle: 2010-07-20 20:27:53.345 -  waiting 553ms
throttle: 2010-07-20 20:27:53.903 - request processed: interval: 569ms
throttle: 2010-07-20 20:27:53.914 -  waiting 7ms
throttle: 2010-07-20 20:27:53.924 - request processed: interval: 21ms
throttle: 2010-07-20 20:27:53.935 -  waiting 897ms
throttle: 2010-07-20 20:27:54.839 - request processed: interval: 915ms
throttle: 2010-07-20 20:27:54.849 -  waiting 178ms
throttle: 2010-07-20 20:27:55.029 - request processed: interval: 190ms
throttle: 2010-07-20 20:27:55.040 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:55.051 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:55.062 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:55.073 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:55.084 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:55.094 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:55.104 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:55.114 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:55.124 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:55.134 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:55.145 - request processed: interval: 11ms
throttle: 2010-07-20 20:27:55.155 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:55.166 -  waiting 4ms
throttle: 2010-07-20 20:27:55.176 - request processed: interval: 21ms
throttle: 2010-07-20 20:27:55.187 - active queue full
throttle: 2010-07-20 20:27:56.079 - request processed: interval: 903ms
throttle: 2010-07-20 20:27:56.090 - active queue full
throttle: 2010-07-20 20:27:56.212 - request processed: interval: 133ms
throttle: 2010-07-20 20:27:56.222 - request processed: interval: 10ms
throttle: 2010-07-20 20:27:56.232 -  waiting 500ms
throttle: 2010-07-20 20:27:56.735 - request processed: interval: 513ms
throttle: 2010-07-20 20:27:56.745 -  waiting 267ms
throttle: 2010-07-20 20:27:57.018 - request processed: interval: 283ms
throttle: 2010-07-20 20:27:57.028 -  waiting 14ms
throttle: 2010-07-20 20:27:57.049 - request processed: interval: 31ms
throttle: 2010-07-20 20:27:57.059 - active queue full
throttle: 2010-07-20 20:27:57.261 -  waiting 112ms
throttle: 2010-07-20 20:27:57.373 - request processed: interval: 324ms
throttle: 2010-07-20 20:27:57.383 -  waiting 70ms
throttle: 2010-07-20 20:27:57.455 - request processed: interval: 82ms
throttle: 2010-07-20 20:27:57.465 -  waiting 79ms
throttle: 2010-07-20 20:27:57.545 - request processed: interval: 90ms
throttle: 2010-07-20 20:27:57.566 -  waiting 189ms
throttle: 2010-07-20 20:27:57.774 - request processed: interval: 229ms
throttle: 2010-07-20 20:27:57.796 -  waiting 205ms
throttle: 2010-07-20 20:27:58.003 - request processed: interval: 229ms
throttle: 2010-07-20 20:27:58.023 -  waiting 78ms
throttle: 2010-07-20 20:27:58.103 - request processed: interval: 100ms
throttle: 2010-07-20 20:27:58.123 - active queue full
throttle: 2010-07-20 20:27:58.351 - request processed: interval: 248ms
throttle: 2010-07-20 20:27:58.361 - active queue full
throttle: 2010-07-20 20:27:58.412 -  waiting 491ms
throttle: 2010-07-20 20:27:58.907 - request processed: interval: 556ms
throttle: 2010-07-20 20:27:58.918 -  waiting 6ms
throttle: 2010-07-20 20:27:58.928 - request processed: interval: 21ms
throttle: 2010-07-20 20:27:58.938 -  waiting 901ms
throttle: 2010-07-20 20:27:59.842 - request processed: interval: 914ms
throttle: 2010-07-20 20:27:59.852 -  waiting 177ms
throttle: 2010-07-20 20:28:0.033 - request processed: interval: 191ms
throttle: 2010-07-20 20:28:0.043 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.053 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.063 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.073 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.083 -  waiting 1ms
throttle: 2010-07-20 20:28:0.093 - request processed: interval: 20ms
throttle: 2010-07-20 20:28:0.104 - request processed: interval: 11ms
throttle: 2010-07-20 20:28:0.115 - request processed: interval: 11ms
throttle: 2010-07-20 20:28:0.125 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.135 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.145 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.155 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.165 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:0.175 - active queue full
throttle: 2010-07-20 20:28:0.216 - request processed: interval: 51ms
throttle: 2010-07-20 20:28:0.227 - active queue full
throttle: 2010-07-20 20:28:0.237 -  waiting 842ms
throttle: 2010-07-20 20:28:1.081 - request processed: interval: 865ms
throttle: 2010-07-20 20:28:1.092 -  waiting 120ms
throttle: 2010-07-20 20:28:1.215 - request processed: interval: 134ms
throttle: 2010-07-20 20:28:1.226 - request processed: interval: 11ms
throttle: 2010-07-20 20:28:1.237 -  waiting 498ms
throttle: 2010-07-20 20:28:1.735 - request processed: interval: 509ms
throttle: 2010-07-20 20:28:1.746 -  waiting 272ms
throttle: 2010-07-20 20:28:2.026 - request processed: interval: 291ms
throttle: 2010-07-20 20:28:2.036 -  waiting 13ms
throttle: 2010-07-20 20:28:2.056 - request processed: interval: 30ms
throttle: 2010-07-20 20:28:2.066 -  waiting 307ms
throttle: 2010-07-20 20:28:2.376 - request processed: interval: 320ms
throttle: 2010-07-20 20:28:2.387 -  waiting 68ms
throttle: 2010-07-20 20:28:2.457 - request processed: interval: 81ms
throttle: 2010-07-20 20:28:2.467 -  waiting 78ms
throttle: 2010-07-20 20:28:2.548 - request processed: interval: 91ms
throttle: 2010-07-20 20:28:2.559 -  waiting 215ms
throttle: 2010-07-20 20:28:2.779 - request processed: interval: 231ms
throttle: 2010-07-20 20:28:2.790 -  waiting 213ms
throttle: 2010-07-20 20:28:3.011 - request processed: interval: 232ms
throttle: 2010-07-20 20:28:3.022 -  waiting 81ms
throttle: 2010-07-20 20:28:3.112 - request processed: interval: 101ms
throttle: 2010-07-20 20:28:3.122 - active queue full
throttle: 2010-07-20 20:28:3.343 -  waiting 8ms
throttle: 2010-07-20 20:28:3.354 - request processed: interval: 242ms
throttle: 2010-07-20 20:28:3.365 - active queue full
throttle: 2010-07-20 20:28:3.385 -  waiting 522ms
throttle: 2010-07-20 20:28:3.916 - request processed: interval: 562ms
throttle: 2010-07-20 20:28:3.927 -  waiting 1ms
throttle: 2010-07-20 20:28:3.938 - request processed: interval: 22ms
throttle: 2010-07-20 20:28:3.948 -  waiting 894ms
throttle: 2010-07-20 20:28:4.846 - request processed: interval: 908ms
throttle: 2010-07-20 20:28:4.856 -  waiting 177ms
throttle: 2010-07-20 20:28:5.036 - request processed: interval: 190ms
throttle: 2010-07-20 20:28:5.046 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:5.056 - request processed: interval: 10ms
throttle: 2010-07-20 20:28:5.066 - active queue full
throttle: 2010-07-20 20:28:5.106 - request processed: interval: 50ms
throttle: 2010-07-20 20:28:5.117 - active queue full
throttle: 2010-07-20 20:28:5.198 - request processed: interval: 92ms
throttle: 2010-07-20 20:28:5.208 - active queue full
throttle: 2010-07-20 20:28:5.270 - request processed: interval: 72ms
throttle: 2010-07-20 20:28:5.280 - active queue full
throttle: 2010-07-20 20:28:5.340 - request processed: interval: 70ms
throttle: 2010-07-20 20:28:7.424 - request processed: interval: 2084ms
throttle: 2010-07-20 20:28:9.329 - request processed: interval: 1905ms
throttle: 2010-07-20 20:28:13.267 - request processed: interval: 3938ms
throttle: 2010-07-20 20:28:14.359 - request processed: interval: 1092ms
cache: 2010-07-20 20:28:45.017 - removed from cache: http://stackauth.com/1.0/sites
cache: 2010-07-20 20:28:45.560 - removed from cache: http://api.stackapps.com/1.0/answers/704/comments
cache: 2010-07-20 20:28:46.720 - removed from cache: http://api.stackapps.com/1.0/badges/name
cache: 2010-07-20 20:28:47.002 - removed from cache: http://api.stackapps.com/1.0/answers/704
cache: 2010-07-20 20:28:47.037 - removed from cache: http://api.stackapps.com/1.0/badges
[-- truncated for size --]
cache: 2010-07-20 20:29:13.267 - removed from cache: http://api.stackapps.com/1.0/questions?pagesize=100&page=5
cache: 2010-07-20 20:29:14.356 - removed from cache: http://api.stackapps.com/1.0/questions?pagesize=100&page=6
cache: 2010-07-20 20:29:15.175 - removed from cache: http://api.stackapps.com/1.0/questions?pagesize=100&page=7
>>>
0

You have a very serious problem on the page you reference above.

It includes the following JavaScript file which is over 5MB in size. I have a 256kbps Internet connection, so you can only imagine how long I had to wait for the page to load.

http://soapi.info/Code/JS2/latest/docs/content/SoapiProjectData.js

If this is not a glitch, please consider adding a warning page so that users know why the page is taking 2 minutes to load.

4
  • that is the documentation data. you can alleviate latency by downloading the package and accessing the documentation locally. but your point is valid. I should add a size and explanation. Sep 4, 2010 at 2:49
  • @code: Ya, I downloaded the ZIP and discovered that... don't worry about it :) By the way, do you think that Soapi2.JS is stable enough to use in StackCenter? Sep 4, 2010 at 2:57
  • yes, it seems stable enough. I don't anticipate any changes in the existing functionality, only additions. but... while I have reduced the code size significantly using run-time code generation, I still need to do some optimization for minification and add that to my build process. If you minify it yourself and the resulting script passes the unit tests you should be good to go until then. Sep 4, 2010 at 4:25
  • ooops. your other note brought up the only possible functional change that may be coming down the pike. Sep 4, 2010 at 4:32

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .