CRUD Operations

As mentioned in the previous part of this tutorial, our data model has two types, User and Tweet. In this part of the tutorial, we will define our User type, and see how to perform CRUD operations using the API.

Note: In xnlogic, we define a data model in terms of models and parts. In future tutorials, we will discuss these fundamental concepts in detail. For now, think of the words part, model and type as synonyms.

Create the User model

Note: All paths below are specified relative to the application root folder.

Create the file lib/my_app/parts/user.rb with the following code:

module MyApp
  module User

    property :username, type: :text
    property :email, type: :text

Edit lib/my_app/models.rb, and add the following entry to the client_models hash:

user: [User]

Important: The code above is case sensitive.

We are now ready to perform CRUD operation using the REST API.

Send API requests

Once your application is deployed to a server, any HTTP client can be used to access the API. During development, we find it convenient to avoid running a server, and simulate API requests from the console.

If you haven’t done so already, start the console (using the command xn-console). Next, get a reference to the application, by database name, and include its console module:

jruby-1.7.18 :001 > app = PM['db01']
jruby-1.7.18 :002 > include app.console

Once we include app.console, we can use the xput, xget, xpath, xdelete and xpost to simulate the corresponding HTTP request.


We create model objects using PUT requests.

jruby-1.7.18 :003 > xput '/model/user', {username: 'jon', email: ''}
jruby-1.7.18 :004 > xput '/model/user', {username: 'cynthia', email: ''}
jruby-1.7.18 :005 > xput '/model/user', {username: 'randy.marsh', email: ''}
jruby-1.7.18 :006 > xput '/model/user', {username: 'cool.dude', email: ''}


We read model objects using GET requests.

jruby-1.7.18 :007 > xget '/model/user'
{:meta=>{:xnid=>"/model/user/id/71", :model_name=>"user", :rendered=>["record", "has_notes", "user"], :format=>"partial", :rel_limit=>50}, :id=>71, :name=>nil, :model_name=>"user", :description=>nil, :created_at=>2015-02-18 00:59:18 UTC, :username=>"jon", :email=>"", :display_name=>71}
{:meta=>{:xnid=>"/model/user/id/73", :model_name=>"user", :rendered=>["record", "has_notes", "user"], :format=>"partial", :rel_limit=>50}, :id=>73, :name=>nil, :model_name=>"user", :description=>nil, :created_at=>2015-02-18 00:59:19 UTC, :username=>"cynthia", :email=>"", :display_name=>73}
{:meta=>{:xnid=>"/model/user/id/75", :model_name=>"user", :rendered=>["record", "has_notes", "user"], :format=>"partial", :rel_limit=>50}, :id=>75, :name=>nil, :model_name=>"user", :description=>nil, :created_at=>2015-02-18 00:59:19 UTC, :username=>"randy.marsh", :email=>"", :display_name=>75}
{:meta=>{:xnid=>"/model/user/id/77", :model_name=>"user", :rendered=>["record", "has_notes", "user"], :format=>"partial", :rel_limit=>50}, :id=>77, :name=>nil, :model_name=>"user", :description=>nil, :created_at=>2015-02-18 00:59:20 UTC, :username=>"cool.dude", :email=>"", :display_name=>77}
Total: 4
 => #<V-Lucene(model_name:"user") ~ 4 -> V-Range(-1...100) -> Obj-Map>

Note: The server’s JSON response is printed as a Ruby object.

Note: The console output includes information about how the query is performed. This can be a useful debugging/inspection tool.

If you are not interested in the server’s verbose response, you can ask for a specific property.

jruby-1.7.18 :008 > xget '/model/user/properties/username'
[71, "jon"] [73, "cynthia"] [75, "randy.marsh"] [77, "cool.dude"]  
Total: 4
 => #<V-Lucene(model_name:"user") ~ 4 -> Obj-Map -> Obj-Range(-1...100)>

Or a number of properties.

jruby-1.7.18 :009 > xget '/model/user/properties/email,username'
[71, "", "jon"] [73, "", "cynthia"] [75, "", "randy.marsh"] [77, "", "cool.dude"]    
Total: 4
 => #<V-Lucene(model_name:"user") ~ 4 -> Obj-Map -> Obj-Range(-1...100)>

If you are not interested in all users, you can get a specific user by id.

jruby-1.7.18 :010 > xget '/model/user/id/71'
jruby-1.7.18 :011 > xget '/model/user/id/71/properties/email'

There are many additional features for reading data, such as filter/search, limit, offset and more. These features will be covered in other tutorials.


We update model objects using PATCH requests.

jruby-1.7.18 :014 > xpatch '/model/user/id/71', {email: ''}


As expected, deleting an object is done using DELETE requests.

jruby-1.7.18 :015 > xdelete '/model/user/id/71'


  • We perform CRUD operations by sending HTTP requests.
CRUD operation HTTP method
Create PUT
Read GET
Update PATCH
  • We can simulate HTTP requests from the console.

  • When sending a GET request, we build a query using the request’s URL.

URL Path Description
/model/foo All objects of type foo.
/model/foo/id/42 The foo object whose id is 42 (if it exists).
/model/foo/properties The names of all properties defined in type foo.
/model/foo/id/42/properties/NAME1,NAME2 The (values of the) NAME1 and NAME2 properties of the foo object whose id is 42.