HopJS
The RESTful API dynamic web apps crave.
Introduction
HopJS is a RESTful based declarative API framework for Node.js that:
- Supports Android, and Shell client side stub generation
- Generates easy to use browser side API hooks
- Has a declarative testing interface, which can generate native unit tests in JavaScript and Shell code
- Generates it's own API documentation
- Supports intelligent server-side caching of results using Redis
- Supports event based APIs using Socket.io
- Enhanced APIs with optional declarative models
First, we simply define the interface you wish to expose
(either as static methods on an object or as a proper JavaScript class)
UserService={};
UserService.create=function(input,onComplete){
}
UserService.authenticate=function(input,onComplete){
}
Next, we use Hop to define the interface; this will expose the interface via a RESTful API
Hop.defineClass("UserService",UserService,function(api){
api.post("create","/user").demand("email").demand("username");
api.post("authenticate","/user/auth").demand("email").demand("username");
});
Hop.exposeAPI("/api/",app)
Now that we've done that we get a few things:
- We have our RESTful API
- HopJS generates a client side API we can use in our browser which will have the following definitions:
- UserService.create(input,onComplete)
- UserService.authenticate(input,onComplete)
So now our website has:
# An API for UserService.create
POST /api/user
# An API for UserService.authenticate
POST /api/user/authenticate
# Documentation for our API as generated by HopJS with online unit tests
GET /api/
# A jQuery based client set of stubs for our API
GET /api/api.js
# A JSON definition of our API for client side stub generation
GET /api/api.json
But we can also define the test cases for our new interface!
Hop.defineTestCase("UserService.authenticate",function(test){
var validUser = { email:"test@test.com", username:"TestUser" };
test.do("UserService.create").with(validUser).noError();
test.do("UserService.authenticate").with(validUser).noError();
test.do("UserService.authenticate").with({password:"BOB"},validUser).hasError(/Permission denied/);
});
Now let's suppose we wanted an Android set of native client stubs for our API in Java:
hopjs-gen -url http://www.website.com:3000/ android -outputDir ./androidApp -package com.website.www
Let's also generate a shell script for using our new API
# This will create a shell script which uses curl to call our API
hopjs-gen -url http://www.website.com:3000/ shell -output api.sh
./api.sh UserService.create -APIURL http://www.website.com:3000/ --email user@user.com --username foo
We can also generate a unit test for our shell script
# This will create a shell script which runs our unit tests on our shell script
hopjs-gen -url http://www.website.com:3000/ shell -unitTest -output test_api.sh
./test_api.sh http://www.website.com:3000/ ./api.sh
You can see a complete working example at: https://github.com/celer/hopjs/tree/master/examples/intro
Intelligent server-side caching of results
Now lets assume that we've written a killer server-side API, but we haven't done any caching of our results so each
time we need to do something we're hitting our database. HopJS has the ability to add caching on top of your API quickly
and easily.
Hop.enableCaching({ log:true, redisClient: myRedisClient });
Hop.defineClass("UserService",UserService,function(api){
api.usage("Manages users");
api.get("load","/user/:id").demand("id").cacheId("/user/:id",60,true);
api.del("delete","/user/:id").demand("id").cacheInvalidate("/user/:id");
api.get("list","/user/").optional("sortBy").cacheId("/users/:start/:size/",5000).demand("start").demand("size");
});
Caching works by associating a unique ID with each result returned from an API call - the trick is that the ID is calculated based upon the object that is used as an input or returned as a result of calling the API call.
Time for a quick example:
UserService.load({id:5})
UserService.del({id:5})
You can see a complete working example at: https://github.com/celer/hopjs/tree/master/examples/caching
Advanced Topics
API Interfaces
HopJS also has the ability to define an API interface which can be used to quickly stub out APIs which share their interfaces:
Hop.defineInterface("Notification",function(api){
api.post("send","#classname/send").usage("Sends a message").demand("msg").demand("subject").demand("to");
});
Hop.defineClass("Email",EmailService,function(api){
api.extends("Notification");
});
Models
TBD
Known Issues / Todo
- Android API is non-functional after major re-factor
- Curl can't save session cookies so some shell tests won't work
- Need to add SSL support
- Need to add dev key support
- DRY for local API calling
- Figure out a way for unit tests to save intermediate results for later usage or comparison
- Add more tests