---
title: 'Jester: JavaScriptian REST'
teaser:
tags: news,web,javascript,jester
author: Eric Mill
published_on: 2007-04-02
---

Jester is our implementation of REST, in JavaScript.  It provides (nearly)
identical syntax to ActiveResource for using REST to find, update, and create
data, but from the client side.

**Update, 6/16/07:** We have released version 1.3 of Jester.  You may want to
view its [release description].

[release description]: https://thoughtbot.com/blog/jester-1-3-jsonic-rest

Jester is available from SVN in [trunk form], or a [1.1 release] form. You can
also download a [zipped copy of 1.1]. Jester is released under the MIT License.

[trunk form]: http://svn.thoughtbot.com/jester/trunk
[1.1 release]: http://svn.thoughtbot.com/jester/tags/rel-1.1/
[zipped copy of 1.1]: http://images.thoughtbot.com/ui/2007-4-16-jester-1.1.zip

## Syntax

All examples below are taken from inside the JavaScript console of [Firebug],
the best JavaScript development tool you could possibly have.

[Firebug]: https://www.getfirebug.com/

First, declare a model in Jester by calling **model** on Base:

```javascript
>>> Base.model("User")
>>> User
Object _name=User _singular=user _plural=users
```

This creates a global variable called User.  It assumes that the <abbr
title="Uniform Resource Locator">URL</abbr> prefix it uses to base its HTTP
requests from is the current domain and port, and assumes "user" and "users" as
single and plural forms to make these URLs.  There's no "people/person"
intelligence here, so make sure to override these defaults if you need to, like
so:

```javascript
>>> Base.model("Child", "https://thoughtbot.com", "child", "children")
>>> Child
Object _name=Child _singular=child _plural=children
```

If you want to capture the model created in a local variable, or simply prefer
more traditional JavaScript syntax, you can do:

```javascript
>>> var Child = new Base("Child", "https://thoughtbot.com", "child", "children")
>>> Child
Object _name=Child _singular=child _plural=children
```

**Find** will retrieve a particular instance of your model.  Attributes are
auto-converted to integer or boolean types if that's what they are on the server
side.  The "GET" line is not a return value, just Firebug's report of activity,
but relevant to understanding what's happening.

```javascript
>>> eric = User.find(1)
GET http://localhost:3000/users/1.xml
Object _name=User _singular=user _plural=users

>>> eric.attributes
["active", "email", "id", "name"]

>>> eric.id
1
>>> eric.name
"Eric Mill"
>>> eric.active
true
```

**Create** takes a hash of attribute values.  After calling **create**, the
model will fetch its new ID from the return headers.

```javascript
>>> floyd = User.create({name: "Floyd Wright", email: "tfwright@thoughtbot.com"})
POST http://localhost:3000/users.xml
Object _name=User _singular=user _plural=users

>>> floyd.id
9

>>> User.find(9).name
GET http://localhost:3000/users/9.xml
"Floyd Wright"
```

Updating is as simple as changing one of the properties and calling **save**.

```javascript
>>> eric = User.find(1)
GET http://localhost:3000/users/1.xml
Object _name=User _singular=user _plural=users

>>> eric.email
"emill@thoughtbot.com"
>>> eric.email = "sandybeach@wintermute.com"
"sandybeach@wintermute.com"

>>> eric.save()
POST http://localhost:3000/users/1.xml
true

>>> User.find(eric.id).email
GET http://localhost:3000/users/1.xml
"sandybeach@wintermute.com"
```

Sadly, there's one area where Jester's syntax can't match ActiveResource's
perfectly. The method "new" has been renamed to **build**, due to "new" being an
illegal method name in JavaScript up to 1.6.  Hopefully this can be updated as
the browser landscape evolves.  **Build** was chosen because it is similarly
used in ActiveRecord to replace "new" on an association array, where "new"
cannot be used.

```javascript
>>> chad = User.build({email: "cpytel@thoughtbot.com", name: "Chad Pytel"})
Object _name=User _singular=user _plural=users

>>> chad.new_record()
true
>>> chad.save()
POST http://localhost:3000/users.xml
true

>>> chad.id
9
>>> chad.new_record()
false
```

Error validations are supported.  If a model fails to save, **save** returns
false, and the model's **errors** property is set with an array of the error
messages returned.

```javascript
>>> jared = User.build({name: "", email: ""})
Object _name=User _singular=user _plural=users

>>> jared.save()
POST http://localhost:3000/users.xml
false

>>> jared.errors
["Name can't be blank", "Email can't be blank"]
>>> jared.valid()
false

>>> jared.name = "Jared Carroll"
"Jared Carroll"
>>> jared.email = "emill@thoughtbot.com"
"emill@thoughtbot.com"

>>> jared.save()
POST http://localhost:3000/users.xml
false

>>> jared.errors
["Email has already been taken"]
>>> jared.email = "jcarroll@thoughtbot.com"
"jcarroll@thoughtbot.com"

>>> jared.save()
POST http://localhost:3000/users.xml
true
```

Lastly, associations are also supported.  If the association data is included in
the <abbr title="Extensible Markup Language">XML</abbr>, they'll be loaded into
the returned model as Jester models of their own, using the same assumptions on
naming and <abbr title="Uniform Resource Locator">URL</abbr> prefix described
above.  They're full models, so you can edit and save them as you would the
parent.  Has\_many relationships come back as simple arrays, has\_one
relationships as a property.  In this example, User has\_many :posts, and Post
belongs\_to :user.

```javascript
>>> eric = User.find(1)
GET http://localhost:3000/users/1.xml
Object _name=User _singular=user _plural=users

>>> eric.posts
[Object _name=Post _singular=post _plural=posts,
  Object _name=Post _singular=post _plural=posts]

>>> eric.posts.first().body
"Today I passed the bar exam. Tomorrow, I make Nancy my wife."
>>> eric.posts.first().body = "Today I *almost* passed the bar exam. The ring
  waits one more day."
"Today I *almost* passed the bar exam. The ring waits one more day."

>>> eric.posts.first().save()
POST http://localhost:3000/posts/1.xml
true

>>> post = Post.find(1)
GET http://localhost:3000/posts/1.xml
Object _name=Post _singular=post _plural=posts

>>> post.body
"Today I *almost* passed the bar exam. The ring waits one more day."
>>> post.user
Object _name=User _singular=user _plural=users
>>> post.user.name
"Eric Mill"
```

## Using Jester

Jester depends on two libraries: [Prototype], which comes with Rails and most
people are familiar with, and [ObjTree], a nice DOM parsing engine for
JavaScript.  Both of these are packaged along with Jester in its SVN repository,
so you don't have to hunt for them yourself.  Just make sure you're including
all three in your test file.

[Prototype]: http://prototypejs.org
[Objtree]: http://www.kawa.net/works/js/xml/objtree-e.html

```html
<script type="text/javascript" src="/javascripts/prototype.js"></script>
<script type="text/javascript" src="/javascripts/ObjTree.js"></script>
<script type="text/javascript" src="/javascripts/jester.js"></script>
```

JavaScript in the browser is limited to requests with in only the same domain as
the script is running in, so without iframe hackery, Jester is probably only
useful for writing client code in your own apps, to talk to itself.  We're
investigating whether Jester can use this hackery to make cross-domain requests,
but it's not clear if this will be feasible.

There are also some basic unit tests included inside Jester's repository, which
run using [JsUnit].  To run them yourself, from Jester's repository open the
file test/jsunit/testRunner.html in your browser, and choose
`test/jester_test.html` as the test file.

[JSUnit]: http://www.jsunit.net

## The Server Side

These examples are talking with a Rails application whose controllers were
generated with `./script generate scaffold_resource`—in other words, the ideal
RESTful controllers.  It's very easy to make your controller RESTful.  Here's
the source for the User controller I'm using.  The lines that deal with
returning <abbr title="HyperText Markup Language">HTML</abbr> have been removed,
and I have added `(:include => :posts)` as an argument to `to_xml` in two
places, so associations are included (it's that easy!).

```html
<script type="text/javascript" src="/javascripts/prototype.js"></script>
<script type="text/javascript" src="/javascripts/ObjTree.js"></script>
<script type="text/javascript" src="/javascripts/jester.js"></script>
```

An example of the <abbr title="Extensible Markup Language">XML</abbr> produced
here, of a User with one Post, at /users/2.xml:

```xml
<user>
  <active type="boolean">true</active>
  <email>cpytel@thoughtbot.com</email>
  <id type="integer">2</id>
  <name>Chad Pytel</name>
  <posts>
    <post>
      <title>Life as a Jester</title>
      <body>It's not as hard as Master said it would be.  Today I made 200 dollars.</body>
      <created-at type="datetime">2007-04-01T04:01:56-04:00</created-at>
      <id type="integer">2</id>
      <user-id type="integer">2</user-id>
    </post>
  </posts>
</user>
```

To see some real live examples, the [Beast forum] is
currently implenting some of ActiveResource.  Here's [technoweenie's User
account][user-xml], in <abbr title="Extensible Markup Language">XML</abbr>, and
an <abbr title="Extensible Markup Language">XML</abbr> [list of selected
Users][users-xml].  Pretty much any <abbr title="Uniform Resource
Locator">URL</abbr> in Beast can have ".xml" appended to it.

[Beast forum]: http://repo.or.cz/w/beast-modified.git
[user-xml]: http://beast.caboo.se/users/1.xml
[users-xml]: http://beast.caboo.se/users.xml

## ActiveResource Reference

[Taking ARes Out for a Test Drive][test-drive]—Great introduction to
ActiveResource, by one of its authors.  [ActiveResource and Testing]—A post I
made here discussing how I tested ActiveResource models.  [ActiveResource's
Subversion Repository][svn]—Current ActiveResource trunk, at
svn.rubyonrails.org.

[test-drive]: http://weblog.techno-weenie.net/2006/12/12/taking-ares-out-for-a-test-drive
[ActiveResource and Testing]: https://thoughtbot.com/blog/activeresource-and-testing
[svn]: http://svn.rubyonrails.org/rails/trunk/activeresource/

## Thanks

Thanks go to Chad for the original idea, and Jared for writing Jester's tests.

Jester is new, so we'd love to hear feedback on its strengths and weaknesses.
We're using it ourselves, so it's under active development and getting plenty of
love and attention.  Please tell us what you think!
