As we go on building more and more JavaScript applications, the need for APIs will go on increasing. Writing a full-fledged API requires quite an amount of planning and time. But sometimes, all we require in an API are just common CRUD actions. Writing CRUD APIs for each and every resource can be quite a tedious and repetitive task. The Ruby community being what it is, some enterprising developers have taken a shot at solving the tedium. Today we will try a gem that will simplify creating these CRUD-based APIs within a Rails application. Let’s get started.
Creating a Rails application
We will create a basic Rails application with a Contact model. The application will use MRI Ruby, Rails 4.2, and SQLite to keep things simple. First of all, install MRI Ruby with RVM or rbenv.
Switch to Ruby and gem install rails
to get the latest Rails gem. Now create a new Rails application, like so:
rails new databound_rails -T
After the application is generated, create a Contact model:
cd databound_rails
rails g model Contact name:string address:string city:string phone:string
Migrate the database:
rake db:migrate
Now we have Contact model in place. We will create the CRUD interface later.
Installing Databound
Now we will integrate Databound gem in our Rails application. We will also add ‘lodash-rails’ gem since Databound depends on it. Like so:
gem 'databound', '3.1.3'
gem 'lodash-rails', '3.10.1'
We are using ‘3.x’ version of ‘lodash’ for compatibility reasons.
Install both gems:
bundle install
Databound comes with a generator to add the required files into our application, so run it:
rails g databound:install
This adds databound.js to our asset pipeline. You’ll need to manually add ‘lodash’ in app/assets/javascripts/application.js, so that it’s picked up by the asset pipeline:
//= require lodash
//= require databound
Databound will already we added in application.js, but you need to remember to require ‘lodash’ before Databound.
Configuring Databound
Let’s use Databound with our Contact model. First, modify config/routes.rb to add Databound routes for Contact model:
Rails.application.routes.draw do
databound :contacts
end
As you can see, we have added a Databound route for Contact model.
Databound will generate a controller named ContactsController
automatically at runtime if it is not found. For simple models, this feature can save the time of writing controllers for each model. We can also specify API accessible columns here, like so:
databound :contacts, columns: [:name, :address, :city, :phone]
But we will create a ContactsController
since we will explore more advanced options provided by Databound and hence will configure the columns in the controller itself.
Let’s ContactsController
with following code:
class ContactsController < ApplicationController
databound do
model :contact
columns :name, :address, :city, :phone
end
end
Here we have defined the model
as :contact
, along with the columns
that are accessible from the API here instead of doing so in routes.rb. Now when we invoke the API, only these specified columns can be accessed or modified.
Building the Frontend
We have our backend ready. Let’s create a simple jQuery-powered frontend for testing the API. First, add an action to our ContactsController
for showing the Contacts page.
In app/controllers/contacts_controller.rb, add the following:
def index
end
You’ll need add a route for that action in config/routes.rb:
get 'contacts' => 'contacts#index'
Create the view for the action in app/views/contacts/index.html.erb with the following code:
<h1>Contacts</h1>
<table border="0">
<tr>
<td colspan="2"><h3>Create Contact</h3></td>
</tr>
<tr>
<td><strong>Name:</strong></td>
<td><input name="txtName" id="txtName" /></td>
</tr>
<tr>
<td><strong>Address:</strong></td>
<td><input name="txtAddress" id="txtAddress" /></td>
</tr>
<tr>
<td><strong>City:</strong></td>
<td><input name="txtCity" id="txtCity" /></td>
</tr>
<tr>
<td><strong>Phone:</strong></td>
<td><input name="txtPhone" id="txtPhone" /></td>
</tr>
<tr>
<td colspan="2" align="center"><button name="createContact" id="createContact">Create Contact</button></td>
</tr>
</table>
<table border="0" id="tblContacts">
<thead>
<th>Name</th>
<th>Address</th>
<th>City</th>
<th>Phone</th>
</thead>
<tbody>
</tbody>
</table>
Here we have created a simple interface to create a new Contact and show it in a table below. Now we will create contacts.js in app/assets/javascripts to bring the view to life:
var Contact = new Databound('/contacts');
$(document).ready(function(){
$('#createContact').on('click', function() {
Contact.create({ name: $('#txtName').val(), address: $('#txtAddress').val(), city: $('#txtCity').val(), phone: $('#txtPhone').val() }).then(function(new_contact) {
var table = $('#tblContacts tbody');
var row = "<tr>";
row += "<td>" + new_contact.name + "</td>";
row += "<td>" + new_contact.address + "</td>";
row += "<td>" + new_contact.city + "</td>";
row += "<td>" + new_contact.phone + "</td>";
row += "</tr>";
$(table).append(row);
});
});
});
We have defined the Contact
Javascript object which we will be using for invoking CRUD actions on Contact model:
var Contact = new Databound('/contacts');
Next, there’s a simple event handler for the createContact
button to call Contact.create
, passing along the data from the Name, Address, City and Phone fields. Finally, append the created record to the table.
Let’s test if this works. Fire up the Rails server:
rails s
Hit http://localhost:3000/conatcts and create a few records. Records should be created and be visible in the table.
We have now successfully integrated Databound into our API. Let’s explore the other functions provided by Databound.
Searching API
Databound provides three client-side APIs for searching records. Let’s see them one by one.
where
API
Contact.where({ name: 'Devdatta' }).then(function(contacts) {
alert('Contacts named Devdatta');
});
Here we are invoking the where
API to search for contacts that have name
field matching with ‘Devdatta’. The matching records are returned in contacts
object.
find
API
Contact.find(1).then(function(contact) {
alert('Contact ID 1: ' + contact.name);
});
Here we can find a specific contact with its primary key using the find
API. We have passed the primary key as ‘1’ and received the relevant record in contact
object.
findBy
API
Contact.findBy({ name: 'Devdatta' }).then(function(contact) {
alert('Contact named Devdatta from ' + contact.city);
});
findBy
API is similar to where
but you can specify only one field to search with. Here we are searching with name
field matching with ‘Devdatta’. Matching record is returned in contact
object.
We can also specify the default scope for searching as well using the extra_where_scopes
while initializing the API. Like so –
var Contact = new Databound('/contacts',
{ city: 'Pune' },
{ extra_where_scopes: [{ city: 'Mumbai' }] }
);
Update & Delete API
Databound also provides update
and destroy
APIs to edit and delete records, respectively
Update API
Contact.update({ id: 1, name: 'John' }).then(function(contact) {
alert("My name has changed. I'm " + contact.name);
});
Using Databound’s update
API we can update the record’s fields as specified. Only accessible columns’ data will be updated as specified in the controller. Once updated, the contact object is returned with updated data.
Delete API
Contact.destroy(1).then(function(status) {
if (status.success) alert('Contact deleted');
});
Records are deleted using the destroy
API which accepts the primary key of the record as argument and returns the status as true or false.
Permit actions
Databound also allows specifying which actions are permitted based on certain conditions that can be invoked using API. For example:
permit(:update, :destroy) do |params, record|
record.user_id == current_user.id
end
(Since we have not setup any authentication mechanism in our sample, this won’t work directly in our application)
Wrapping Up
Today we got a short introduction to Databound and how to use it in a Rails application. Many Rubyists may not like the way Databound works, since it binds your frontend code to the database rather tightly. However, there are sometimes cases where simplicity of solution wins over architectural purity. Databound merits a hard look is those kinds of scenarios. It is useful for fast prototyping of APIs and for small applications as well.
Hope you liked the tutorial.
Comments and suggestions welcome, as always.
Devdatta Kane is a software developer and designer based in Pune, India. He works with Radinik Technologies building traceability solutions for a variety of industries. He is also the lead developer of refers2, a CRM for small businesses. He works in Ruby on Rails, but likes to dabble with various new technologies as well. An aspiring photographer and passionate traveler, he loves traveling on his motorcycle, capturing experiences through camera.