syntaxhighlighter

Showing posts with label curl. Show all posts
Showing posts with label curl. Show all posts

Tuesday, August 28, 2012

REST - express-examples/sqlite

I've developed a very simple RESTful app, only considering CRUD operations over an user entity.

Naming convention

I followed some good recommendations mentioned in this article, shared by jjeronimo.

In general, the API is:

  • getUsers. Retrieves all users (SELECT * FROM user).
  • curl -X GET http://localhost:3000/rest/users
    
  • getUsersById. Retrieves an user by its id (SELECT * FROM user WHERE id = ?).
  • curl -X GET http://localhost:3000/rest/users/1
    
  • addUser. Adds an user (INSERT INTO user(name) VALUES(?)).
  • curl -X PUT -H "Content-type:application/x-www-form-urlencoded" -d "name=rodolfo" http://localhost:3000/rest/users
    
  • updateUser. Updates an user (UPDATE user SET name = ? WHERE id = ?).
  • curl -X POST -H "Content-type:application/x-www-form-urlencoded" -d "id=1&name=juan" http://localhost:3000/rest/users
    
  • removeUser. Removes an user by its id (DELETE FROM user WHERE id = ?).
  • curl -X DELETE -H "Content-type:application/x-www-form-urlencoded" -d "id=1" http://localhost:3000/rest/users
    

As you can see, the URLs follow the pattern: rest/users. Some comments about this:

  • URLs start with the word "rest" for easily identifying them as part of the REST API. Another part indicating the API version can be added, like: rest/v1/users (util for backwards compatibility issues).
  • A REST API should be easy to discover, just adding and removing parameters. For example: if you want all users can use /users, but if you want an specific user can use /users/1. Maybe a better option, for more complex cases, could be: /users/id/1, because this way results "evident" something like: /users/name/rodolfo/status/0. However, I prefer to specify getXxxById following the entity name (written in plural) by its id.

Facade-Service

I think REST APIs should be coded using Facades. The idea is to decouple business logic into some classes, and treat REST, SOAP, and whatever interfaces aside.

Following an example extracted from /lib/rest/user.js:

var common = require('./common');

var userService = require('../service/user');

this.addUser = function(req, res) {
  var params = req.body;

  common.call(
    res, 
    params.name, 
    function(name) { 
      var valid = true;

      if (name == null) 
        valid = false; 
  
      return valid;
    },
    userService.newInstance().addUser
  );
};

// ...

As you can see in the code above, inside the REST addUser method there are only operations related with parameters filling and validations. For this specific implementation, I coded a generic method for calling services (common.call). But you could just take parameters, validate them, call the corresponding service method, and return the expected output; sort of controller in a MCV implementation.

Call method for integrating Service - REST Facade

I wrote a method call (lib/rest/common.js) for integrating Service methods and REST facades. Following the code main highlights:

// ...

this.call = function(res, params, validate, execute) {
  if (validate(params)) {
    if (params != null) {
      execute(params, function(result) { // TODO: Add extra parameter for error msg
        if (result) 
          ok(res); 
        else 
          nok(res, HTTP_CODE_FORBIDDEN); 

        res.end(JSON.stringify(result)); 
      });
    } else {
      execute(function(result) { // TODO: Add extra parameter for error msg
        if (result) 
          ok(res); 
        else 
          nok(res, HTTP_CODE_FORBIDDEN); 

        res.end(JSON.stringify(result)); 
      });
    }
  } else {
    nok(res, HTTP_CODE_PRECONDITION_FAILED); 
    res.end(); 
  }
};

// ...

The call method executes validate method using params, if true, calls execute method, if not, returns HTTP code 412 (HTTP_CODE_PRECONDITION_FAILED).

  • res: HttpResponse object
  • params: Can be value or object (JSON)
  • validate: Function used for validation, if returns true everything is OK (HTTP_CODE_OK=200), if not, something went wrong and returns HTTP_CODE_PRECONDITION_FAILED(412)
  • execute: Function executed if validate=true. Parameters (params) can be value (e.g. String) or Object (JSON). Callback should receive one parameter (result).

An example about how to use the common.call method can be found above (this.addUser).

Friday, August 24, 2012

How to run the project - express-examples/sqlite

Installing Node.js

For create the application I installed node.js and its package manager npm. For doing that I just typed:

$ sudo aptitude install nodejs npm

My actual linux installation (obsolete):

$ cat /etc/issue
 Ubuntu 10.10 \n \l

My actual node.js packages are:

$ dpkg -l node\* npm
...
ii  nodejs                      0.8.2-1chl1~maverick1       Node.js event-based server-side javascript engine
ii  nodejs-dev                  0.8.2-1chl1~maverick1       Development files for Node.js
ii  npm                         1.1.39-1chl1~maverick1      package manager for nodejs

Note: If you want to install an specific version, you can do it directly from the sources.

Downloading the code

$ git clone https://github.com/camposer/express-examples.git

Starting the server

$ cd express-examples
$ cd sqlite
$ node app
Express server listening on port 3000

Running the app

Just enter in your browser: http://localhost:3000

Testing the REST services

You can test the REST services directly using CURL (installing it is as simple as enter: `sudo aptitude install curl`). Inside the app there is a script named curltest.sh.

echo "getUsers"
curl -X GET http://localhost:3000/rest/users; echo

echo "getUserById"
curl -X GET http://localhost:3000/rest/users/1; echo

echo "addUser"
curl -X PUT -H "Content-type:application/x-www-form-urlencoded" -d "name=rodolfo" http://localhost:3000/rest/users; echo

echo "updateUser"
curl -X POST -H "Content-type:application/x-www-form-urlencoded" -d "id=1&name=juan" http://localhost:3000/rest/users; echo

echo "deleteUser"
curl -X DELETE -H "Content-type:application/x-www-form-urlencoded" -d "id=1" http://localhost:3000/rest/users; echo