patternjavascriptModerate
Interactive API documentation page of a RESTful dictionary API using Backbone.js
Viewed 0 times
interactivedocumentationbackbonepagerestfulusingdictionaryapi
Problem
I'm working on the interactive API documentation of a RESTful dictionary service.
The page should let testers try the different API calls,
by playing with the parameters using a simple web form.
Changes to the web form should trigger API calls with the appropriate parameters,
display the JSON output,
and a well-formatted
I'm using Backbone.js to implement the dynamic client-side behavior,
and the following class structure:
-
such as a dictionary selector and a Run button,
to be used by different API endpoint tools
-
especially for building the common part of API URLs
-
for capturing the parameters needed for making one of the
Views can subscribe to change events to trigger refreshing themselves.
-
adding custom form controls specialized for the
It updates a
-
adding custom
Updates to the
trigger refreshing the view.
Implementation:
```
var App = window.App = {};
App.API_BASEURL = '/api/v1/dictionaries';
App.FormView = Backbone.View.extend({
_events: {
'click .run': 'runBtn',
'change .dictionary': 'run'
},
_initialize: function () {
this.dictionary = this.$('select[name="dictionary"]');
this.output = this.$('.api-output');
},
runBtn: function (e) {
e.preventDefault();
this.run();
},
runOnEnter: function (e) {
if (e.keyCode != 13)
The page should let testers try the different API calls,
by playing with the parameters using a simple web form.
Changes to the web form should trigger API calls with the appropriate parameters,
display the JSON output,
and a well-formatted
curl command that's ready to run.I'm using Backbone.js to implement the dynamic client-side behavior,
and the following class structure:
-
App.FormView: a base View class, for common form controls,such as a dictionary selector and a Run button,
to be used by different API endpoint tools
-
App.CurlView: a base View class, for common logic when rendering sample curl commands of different API endpoint,especially for building the common part of API URLs
-
App.FindByKeywordParams: a Model class,for capturing the parameters needed for making one of the
/find/:method/:keyword type of API calls.Views can subscribe to change events to trigger refreshing themselves.
-
App.FindByKeywordFormView: a View class, extending App.FormView,adding custom form controls specialized for the
/find/:method/:keyword type of API calls.It updates a
App.FindByKeywordParams object.-
App.FindByKeywordCurlView: a View class, extending App.CurlView,adding custom
curl URL building logic specialized for the /find/:method/:keyword type of API calls.Updates to the
App.FindByKeywordParams object it referencestrigger refreshing the view.
Implementation:
```
var App = window.App = {};
App.API_BASEURL = '/api/v1/dictionaries';
App.FormView = Backbone.View.extend({
_events: {
'click .run': 'runBtn',
'change .dictionary': 'run'
},
_initialize: function () {
this.dictionary = this.$('select[name="dictionary"]');
this.output = this.$('.api-output');
},
runBtn: function (e) {
e.preventDefault();
this.run();
},
runOnEnter: function (e) {
if (e.keyCode != 13)
Solution
Be consistent with your method naming; don't mix
I would define your
As Backbone depends on underscore, don't be afraid to tidy up your code with underscore's methods:
Better yet, you can get rid of the proxy entirely:
Instead of string concatenation, underscore templating can make this more readable:
Don't be afraid of borrowing methods; your
Your references to
Finally, you need to document your code so that you/another developer can know what it does in the future; I recommend JSDoc which can be easily integrated into a task runner/build system such as Grunt or gulp, or if not just use the CLI tool.
Hope this helps. :)
Edit: I've tested the
camelCase and snake_case, pick one and stick with it. I would recommend picking camelCase as there are a lot of JavaScript projects out there which use it as a convention, but feel free to go with whatever is most readable to you.I would define your
extras object as so; falsey values shouldn't be picked up on the server side code anyway:var extras = {
list: list,
similar: similar
}As Backbone depends on underscore, don't be afraid to tidy up your code with underscore's methods:
var success = _.bind(function(json) {
this.onApiSuccess(json);
}, this);Better yet, you can get rid of the proxy entirely:
$.ajax({
url: url,
data: extras,
success: _.bind(this.onApiSuccess, this),
error: _.bind(_.partial(this.onApiError, url), this)
});Instead of string concatenation, underscore templating can make this more readable:
var url = _.template('//find//', {
baseURL: App.API_BASEURL,
dictID: dict_id,
method: method,
keyword: keyword
});Don't be afraid of borrowing methods; your
_initialize method should be rewritten as initialize so that a later form view could use it, if it didn't need to implement some custom code. Then, instead of doing this._initialize() you can do this:initialize: function() {
App.FormView.prototype.initialize.apply(this);
this.keyword = this.$('.keyword');
this.keyword.val(this.model.get('keyword'));
}Your references to
this.$el.find() can simply be this.$(), which you've used, but not consistently. :)Finally, you need to document your code so that you/another developer can know what it does in the future; I recommend JSDoc which can be easily integrated into a task runner/build system such as Grunt or gulp, or if not just use the CLI tool.
Hope this helps. :)
Edit: I've tested the
_.bind and _.partial methods and they're working for me, you might want to make sure that you are running the latest version of underscore (upgrading to Backbone 1.0.x might be good too but that may require some additional code changes). Personally I've been using Lo-Dash as underscore instead as the library offers more features and is generally regarded to be more stable/consistent. You can also use the underscore build of Lo-Dash for a drop in replacement.Code Snippets
var extras = {
list: list,
similar: similar
}var success = _.bind(function(json) {
this.onApiSuccess(json);
}, this);$.ajax({
url: url,
data: extras,
success: _.bind(this.onApiSuccess, this),
error: _.bind(_.partial(this.onApiError, url), this)
});var url = _.template('<%- baseURL %>/<%- dictID %>/find/<%- method %>/<%- keyword %>', {
baseURL: App.API_BASEURL,
dictID: dict_id,
method: method,
keyword: keyword
});initialize: function() {
App.FormView.prototype.initialize.apply(this);
this.keyword = this.$('.keyword');
this.keyword.val(this.model.get('keyword'));
}Context
StackExchange Code Review Q#60257, answer score: 10
Revisions (0)
No revisions yet.