Aptana’s Jaxer is a Web Application platform that uses JavaScript on the server side. I’m starting to develop a real taste for the JavaScript language, so Jaxer is a nice place to try out new modes of JavaScript programming.
Server side programming for Web Applications typically involves a good bit of interaction with a database. As a Java developer, I typically use JDBC or an Object Relational Mapping (ORM) framework such as Hibernate for database interaction. If you look around, you’ll find JavaScript lacks such established API/Frameworks for DB interaction (though it looks like some JavaScript ORMs are being actively developed). At this point in time, things are pretty much DIY.
With this in mind, I’ve worked up some code example for CRUD operations using the Jaxer database API. This is a simple example, intended to help folks climb up the Jaxer learning curve. The complete source code for the example is at the bottom of this post.
The example reads and writes from elements on a HTML page and also a database table. In the example, there are HTML tables which describe a person, and likewise, I’ve created a table in the database that has the same attributes (with slightly different names, just to keep this real. The table DDL is in the example code). Here’s a screen shot of the example:

The flow of the example is:
1) Read attributes from the first (from the top) HTML table and then create a corresponding record in the database.
2) Read the same attributes back from the database and then populate the next HTML table
3) Read attributes from the third HTML table and update the database
4) Delete the record from the database.
Line 108 also shows an interesting capability of Jaxer: The script block there is set to runat=”server-proxy”. This tells Jaxer to run these scripts at the server, but let them be callable from the browser. So when I click the CRUD button on the client, it calls runExample on line 14, which in turn calls the four CRUD functions which run at the server. I’m actually calling a function on the server directly from the client! Note too that this example uses blocking calls from the browser to the Jaxer server. It is very easy to change this code to the Jaxer asynchronous API, which is likely topic for my next post.
Without further ado – the code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Jaxer CRUD</title>
<script type="text/javascript" src="prototype.js"></script>
/*
* Scripts that run in the client browser
*/
<script runat='client'>
//
// Run the example on button click
//
function runExample() {
// server side method that creates a table if needed
createTable();
// Do the CRUD operations
createPerson();
readPerson();
updatePerson();
deletePerson();
}
// Create a person in the DB based on properties read from a HTML table
function createPerson() {
// pull properties from HTML table
var p = objectFromTable('create-table');
// write to DB via server proxy call
createPerson_Server(p);
}
// Read a person from the DB and write to a HTML table
function readPerson() {
// read the person from the DB via a server proxy method
var p = readPerson_Server(1);
// populate the HTML table
tableFromObject('read-table', p);
}
// Update a person in the DB based on properties read from an HTML table
function updatePerson() {
// read the person properties from an HTML table
var p = objectFromTable('update-table');
// update the DB via a server proxy call
updatePerson_Server(p);
}
// Delete a person in the DB
function deletePerson() {
// Server call to delete in the DB
deletePerson_Server(1);
}
//
// Utility functions
//
// populate an object from values in a HTML table
function objectFromTable(tableId) {
var o = {};
var propNames = $$('#' + tableId + ' th');
var props = $$('#' + tableId + ' td');
var propNum = 0;
propNames.each(function(item) {
o[item.innerHTML] = props[propNum++].innerHTML;
});
o.birthDate = dateFromStr(o.birthDate);
return o;
}
// populate a HTML table from values in an object
function tableFromObject(tableId, anObject) {
anObject.birthDate = strFromDate(anObject.birthDate);
var propNames = $$('#' + tableId + ' th');
var props = $$('#' + tableId + ' td');
var propNum = 0;
propNames.each(function(item) {
props[propNum++].innerHTML = anObject[item.innerHTML];
});
}
// convert from string 'yyyy-mm-dd' to JS Date
function dateFromStr(dateStr) {
if(dateStr) {
var dateParts = dateStr.split('-');
var date = new Date();
date.setFullYear(dateParts[0]);
date.setMonth(dateParts[1] - 1);
date.setDate(dateParts[2]);
}
return date;
}
// convert from JS Date to string 'yyyy-mm-dd'
function strFromDate(aDate) {
var str = '';
if(aDate) {
str += (aDate.getFullYear() + (aDate.getMonth() + 1) + aDate.getDate());
}
return str;
}
</script>
/*
* Scripts that run on the server.
* Jaxer generated stubs are used to call these from the client.
*/
<script runat='server-proxy'>
//
// Server side persistence functions
//
// Write a new object to the DB
function createPerson_Server(person) {
// execute insert statement, binding to properties of passed in person
Jaxer.DB.execute(
'insert into person ( '+
' id, first_name, last_name, birth_date, weight_kg, height_m ' +
' ) values (?, ?, ?, ?, ?, ?);',
person.id, person.firstName,
person.lastName, person.birthDate,
person.weightInKilograms, person.heightInMeters
);
}
// Read an existing record and return a JavaScript object
function readPerson_Server(id) {
// execute query and receive returned a Jaxer.DB.ResultSet
var resultSet = Jaxer.DB.execute('select * from person where id = ?;', id);
// select the first row since weonly care about 1 person
var aRow = resultSet.rows[0];
// map DB column names to front end column names
var person = {
id: aRow.id,
firstName: aRow.first_name,
lastName: aRow.last_name,
birthDate: aRow.birth_date,
weightInKilograms: aRow.weight_kg,
heightInMeters: aRow.height_m
};
// return to client for display
return person;
}
// From a JS Object, update a row in the DB
function updatePerson_Server(person) {
// Update all fields for the given primary key value
Jaxer.DB.execute(
'update person set '+
' first_name = ?, last_name = ?, birth_date = ?, weight_kg = ?, height_m = ? ' +
' where id = ?;',
person.firstName, person.lastName, person.birthDate,
person.weightInKilograms, person.heightInMeters,
person.id
);
}
// Delete a record in the DB
function deletePerson_Server(id) {
Jaxer.DB.execute('delete from person where id = ?;', id);
}
// Create a table if the table is not already in the DB
function createTable(){
Jaxer.DB.execute(
'create table if not exists person ( ' +
' id integer, ' +
' first_name varchar(20), ' +
' last_name varchar(20), ' +
' birth_date datetime, ' +
' weight_kg decimal(5,2), ' +
' height_m decimal(5,2), ' +
' constraint person_pk primary key(id)); '
);
}
</script>
<style>
.big-cap {
color: blue;
font-size:larger;
font-style:italic;
}
body {
font-family:Arial;
font-size: 10pt;
}
</style>
</head>
<body>
<h3><span class="big-cap">C</span>reate a person from this table</h3>
<table border="1" style="text-align: center" id="create-table">
<tr>
<th>id</th>
<th>firstName</th>
<th>lastName</th>
<th>birthDate</th>
<th>weightInKilograms</th>
<th>heightInMeters</th>
</tr>
<tr>
<td>1</td>
<td>John</td>
<td>Doe</td>
<td>1975-11-20</td>
<td>90.7</td>
<td>1.82</td>
</tr>
</table>
<hr/>
<h3><span class="big-cap">R</span>ead a person and populate this table</h3>
<table border="1" style="text-align: center" id="read-table">
<tr>
<th>id</th>
<th>firstName</th>
<th>lastName</th>
<th>birthDate</th>
<th>weightInKilograms</th>
<th>heightInMeters</th>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<hr/>
<h3><span class="big-cap">U</span>pdate a person from this table</h3>
<table border="1" style="text-align: center" id="update-table">
<tr>
<th>id</th>
<th>firstName</th>
<th>lastName</th>
<th>birthDate</th>
<th>weightInKilograms</th>
<th>heightInMeters</th>
</tr>
<tr>
<td>1</td>
<td>Jane</td>
<td>Smith</td>
<td>1978-1-1</td>
<td>89.2</td>
<td>1.76</td>
</tr>
</table>
<hr/>
<h3><span class="big-cap">D</span>elete person with id = 1</h3>
<hr/>
<button onclick="runExample();">Crud</button>
</body>
</html>
[...] Integrating with a database and calling server-side JS functions from the browser [...]
there is a orm for Aptana Jaxer:JStORM
http://labs.urielkatz.com/wiki/JStORM
Datafacade (http://www.datafacade.com) provides a code free out of the box crud solution. It provides 100% web based configuration and web enables databases with minimal effort.
Thanks a lot for this example of Jaxer coding, very helpful
and worked first time!!
I’ve been trying to replace your Create details with form input but am having problems, is this possible?
Also, do you have any update on the status of Jaxer development re JS ORMs?
Regards
Alan
Hi Alan,
Thanks for the positve comments. Here is a posting that has links to several server-side javascript resources:
http://www.sitepoint.com/blogs/2009/03/10/server-side-javascript-will-be-as-common-as-php/
Those links should get you on your way to Javascript ORM.
Marty