Sweetroll was an experiment in writing a tiny framework that simplified application writing and sharing code between client and server. Today there are much better options for writing isomorphic JavaScript applications, so I've chosen to deprecate this project.
Sweetroll is a light, minimalist, DRY, isomorphic toolkit for HTML+JS applications with Node.js.
var connect = require("connect"),
sweetroll = require("sweetroll");
var app = sweetroll();
app.at("/")
.data(function(req) {
return "world";
})
.render(function(req, data) {
return "Hello "+data;
});
app.serve("/scripts/client.js");
connect().use(app).listen(80);
You shouldn't have to write your web application twice: once on the server and once again on the client. Sweetroll rolls your server and app into one easy-to-build-and-maintain codebase. Here's a glance at what to look for from Sweetroll:
- DRY design: Craft your app with one codebase--Sweetroll will intelligently bundle client code for you. API routes and rendering code, so the client only gets the code it needs.
- Automatic (progressive) enhancement: Sweetroll shares your API and rendering code with the client, so it can query the server for data to render client-side (a la single-page apps). Your app gets an automatic speedup and costs you no development time.
- Control: Sweetroll streamlines development without forcing you to give up the fine control you regularly enjoy with Node.js. Work with express, connect, or vanilla http.
npm install sweetroll
Creates a new sweetroll context, a function with a req, res, next
signature.
This can be used with http:
var http = require("http"),
sweetroll = require("sweetroll");
var app = sweetroll();
http.createServer(app).listen(8080);
It can also be used with express and connect:
var express = require("express"),
sweetroll = require("sweetroll");
var app = express(),
sweetie = sweetroll();
app.use(sweetie);
// Do other express routing...
app.use(function(req, res, next) {
res.send("Sweetie could not find anything for your request.");
});
app.listen(8080);
var connect = require("connect"),
sweetroll = require("sweetroll");
var app = sweetroll();
connect()
.use(app)
.use(function(req, res) {
res.end("Sweetroll could not find anything for your request.");
})
.listen(8080);
Specifies a route that your app will try to match on every request. Depending on your match function, this will usually be a string, but could be a regexp object.
The at() method returns a Route object.
var app = sweetroll();
app.at("/").render(function(req) {
return "You made a request for the root page!";
});
When a route is matched, data
function on that route is executed on the server to interact with a database or run other custom server logic. The request
object from the server (or connect or express) is always provided as a parameter. To pass data out of the data function, simply return
it. Note, that currently requiring the return from the function makes this a blocking operation. A fix is coming soon.
app
.at("/feeds/json/latest")
.data(function(req) {
return db.query("_changes/latest");
});
When a route is matched, render
acts on the return value from the data
step. This is useful for rendering data fresh from the database into HTML and placing it in the document. If the client library is being used, this render function will act on the same data from the earlier step, retrieved by AJAX. In order to use this feature, you will have to make sure that render
has its dependencies satisfied on both the client and server. The render function is always provided with the request
from the server and the data
from the data step. Once again, note that currently the return from this function makes it a blocking operation.
app.at("/hello_world")
.data(function(req) {
return "world";
})
.render(function(req, data) {
return "Hello "+data;
});
Sometimes you may want to exercise control over how requests into the server are compared with routes. For this, there is the match function. You can pass a function into app.match
with accepts the parameters req
and route
, where route
is the value you entered in app.at(route)
. You can use this to write a custom match function which works on regular expressions as routes, or accepts different degrees of matches, such as matching the entire url, just the query string, etc. The result of the match function should be a boolean, where true is returned on a match.
Here is the default match function:
if (req.url == route) {
return true;
}
else {
return false
}
Here is how you could specify your own:
app.match(function(req, route) {
if (req.url.search(route) != -1)
return true;
return false;
});
To use the client library, call the app.client
method, optionally specifying the route where the .js file will be served. If no route is provided, the default string "/sweetroll-client.js"
is used. When this route is matched the client-side sweetroll library is composed and served.
app.client("/scripts/client.js");
The client library is served when you enable the route using app.client
. The client library includes a small amount of client-specific code as well as all of the routes and rendering functions as specified on the server. The client library intercepts anchor clicks and attempts to match these links to the routes already specified. If a route is matched, an AJAX request is made to the server that returns only the data from the data
function, and is applied to the render
function on the client. In this way, you can easily craft a search-engine friendly single-page app by just writing your server routing, data, and rendering functionality.
Care should be taken to ensure that the render function can make the necessary modifications to the client page if this feature can be used.
The client library is off by default, and is not served until you use app.client
and include the script in your page.