Introducción a Eve : A RESTful Web API

eve-sidebarActualmente  estamos inmersos en un mundo lleno de aplicaciones móviles, redes sociales, blogs… todas ellas  con la necesidad de consultar datos de manera remota. La mayoría de estos  datos están expuestos en servicios web para que sea consumidos. En esta entrada del blog vamos a hablar de esto precisamente, la creación de una API Restful con EVE que pueda ser consumida por aplicaciones.

EVE es un framework para la creación de servicios web de tipo RestFul que proporciona una robusta implementación y con muchas características que hará mas fácil la creación de tu propia API Restful. Comenzamos…

Instalación de EVE:

Instalar EVE es muy sencillo, recomendamos instalar la versión de desarrollo ya que es la que contiene los últimos cambios y actualizaciones disponibles, para ello ejecutaremos el siguiente comando:

pip install git+git://github.com/nicolaiarocci/eve.git

Como base de datos para guardar nuestros datos vamos a usar MongoDb que es una base de datos NOSQL. Los siguientes comandos son para realizar la instalación en Ubuntu pero si tenéis otro sistema operativo podéis consultar esta web que contiene documentación para la instalación en otros entornos.

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10

echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list

sudo apt-get update

sudo apt-get install -y mongodb-org

sudo apt-get install -y mongodb-org=2.6.1 mongodb-org-server=2.6.1 mongodb-org-shell=2.6.1 mongodb-org-mongos=2.6.1 mongodb-org-tools=2.6.1

Destacar que EVE esta implementada en Python y será el lenguaje a usar para desarrollar cualquier API en EVE.

Como ejemplo vamos a crearnos una API que maneje una agenda de contactos.

Primero vamos a crear la aplicación EVE, para ello creamos un archivo llamado agenda.py  y escribimos las siguientes líneas de código:

from eve import Eve
app = Eve()

if __name__ == '__main__':
    app.run()

¡Tan solo necesitamos estas líneas código para que nuestra aplicación EVE pueda ser ejecutada!

Ahora vamos configurar la estructura que tendrán los datos de nuestra agenda, para ello creamos un archivo llamado settings.py y escribimos las siguientes lineas de código:

DOMAIN = {'agenda': {}}

Este es el archivo de configuración de EVE y lo que estamos indicando es que nuestra API va  a tener un recurso llamado agenda.

En este momento tenemos todo listo para ejecutar nuestra API, para ello ejecutamos el siguiente comando:

$ python agenda.py
* Running on http://127.0.0.1:5000/

Una vez ejecutada  podemos consumirla:

$ curl -i http://127.0.0.1:5000
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 82
Server: Eve/0.0.5-dev Werkzeug/0.8.3 Python/2.7.3
Date: Wed, 27 Mar 2013 16:06:44 GMT

Como vemos tenemos una respuesta positiva  del servidor y si consultamos el payload devuelto observamos lo siguiente:

{
    "_links": {
        "child": [
            {
                "href": "/agenda",
                "title": "agenda"
            }
        ]
    }
}

El recurso agenda de la API esta disponible desde el punto de entrada a la API. Si realizamos ahora las siguiente consulta:

$ curl http://127.0.0.1:5000/agenda
{
    "_items": [],
    "_links": {
        "self": {
            "href": "/agenda",
            "title": "agenda"
        },
        "parent": {
            "href": "/",
            "title": "home"
        }
    }
}

La API nos responde con todos los datos de la agenda disponibles en la propiedad _items, en este caso no hay ninguno. Nos indica ademas el recurso donde nos encontramos «agenda» y el padre que sería la raiz de la API.

Necesitaremos que nuestros contactos de la agenda puedan ser almacenados, para ello vamos a configurar la base de datos.

Editamos el fichero settings.py y añadimos la configuración de nuestra base de datos:

MONGO_HOST = 'localhost'
MONGO_PORT = 27017
MONGO_USERNAME = 'user'
MONGO_PASSWORD = 'user'
MONGO_DBNAME = 'agendatest'

Por defecto las APIs creadas con EVE son de solo lectura. Vamos a añadir a nuestro servicio de agenda mas potencia permitiendo el total de las operaciones CRUD.

# Enable reads (GET), inserts (POST) and DELETE for resources/collections
# (if you omit this line, the API will default to ['GET'] and provide
# read-only access to the endpoint).
RESOURCE_METHODS = ['GET', 'POST', 'DELETE']

# Enable reads (GET), edits (PATCH), replacements (PUT) and deletes of
# individual items  (defaults to read-only item access).
ITEM_METHODS = ['GET', 'PATCH', 'PUT', 'DELETE']

RESOURCE_METHODS lista todos los métodos permitidos en el endpoint de recursos /agenda

ITEM_METHODS lista los métodos permitidos en los endpoints de artículos /agenda/<object_id>

Vamos a definir que datos vamos a guardar de nuestros contactos, para ello nos crearemos un schema dentro del fichero de configuración settings.py:

schema = {
    # Schema definition, based on Cerberus grammar. Check the Cerberus project
    # (https://github.com/nicolaiarocci/cerberus) for details.
    'firstname': {
        'type': 'string',
        'minlength': 1,
        'maxlength': 10,
    },
    'lastname': {
        'type': 'string',
        'minlength': 1,
        'maxlength': 15,
        'required': True,
        # talk about hard constraints! For the purpose of the demo
        # 'lastname' is an API entry-point, so we need it to be unique.
        'unique': True,
    },
    # An embedded 'strongly-typed' dictionary.
    'location': {
        'type': 'dict',
        'schema': {
            'address': {'type': 'string'},
            'city': {'type': 'string'}
        },
    },
    'born': {
        'type': 'datetime',
    },

   'phoneNumber' : {
        'type' : 'string'
    }
}

Ahora que tenemos definidos los datos que vamos a guardar y sus validaciones vamos a terminar de configurar el recurso agenda:

agenda = {
    # 'title' tag used in item links. Defaults to the resource title minus
    # the final, plural 's' (works fine in most cases but not for 'people')
    'item_title': 'contacts',

    # by default the standard item entry point is defined as
    # '/people/<ObjectId>'. We leave it untouched, and we also enable an
    # additional read-only entry point. This way consumers can also perform
    # GET requests at '/people/<lastname>'.
    'additional_lookup': {
        'url': 'regex("[\w]+")',
        'field': 'lastname'
    },

    # We choose to override global cache-control directives for this resource.
    'cache_control': 'max-age=10,must-revalidate',
    'cache_expires': 10,

    # most global settings can be overridden at resource level
    'resource_methods': ['GET', 'POST'],

    'schema': schema
}

Simplemente le hemos añadido un título al recurso, se ha añadido un endpoint específico para el campo lastname y se ha indicado que los dos únicos métodos permitidos serán GET y POST.

Finalmente actualizamos la definición del dominio:

DOMAIN = {
    'agenda': agenda,
}

Cada vez que se hacen cambios en el fichero settings.py hay que volver a lanzar la aplicación EVE, para ello ejecuta el siguiente comando:

$ python run.py
 * Running on http://127.0.0.1:5000/

Ahora podemos añadir contactos a nuestra agenda mediante el método POST que hemos habilitado anteriormente (no necesariamente tenemos que introducir todos los datos, solo aquellos que por validación sean obligatorios)

$ curl -d '[{"firstname": "John", "lastname": "Appleseed"}, {"firstname": "mitt", "lastname": "romney"}]' -H 'Content-Type: application/json'  http://127.0.0.1:5000/agenda
HTTP/1.0 201 OK

Si ahora realizamos una consulta a la API mediante el endpoint /agenda/Appleseed

$ curl -i http://127.0.0.1:5000/agenda/Appleseed
HTTP/1.0 200 OK
Etag: 28995829ee85d69c4c18d597a0f68ae606a266cc
Last-Modified: Wed, 21 Nov 2012 16:04:56 GMT
Cache-Control: 'max-age=10,must-revalidate'
Expires: 10
...

{
    "firstname": "John",
    "lastname": "Appleseed",
    "_id": "50acfba938345b0978fccad7"
    "updated": "Wed, 24 Nov 2014 16:04:56 GMT",
    "created": "Wed, 24 Nov 2014 16:04:56 GMT",
    "_links": {
        "self": {"href": "/agenda/50acfba938345b0978fccad7", "title": "contacts"},
        "parent": {"href": "/", "title": "home"},
        "collection": {"href": "/agenda", "title": "agenda"}
    }
}

Vemos que nos devuelve el contacto cuyo lastname es Appleseed.

Hasta aquí hemos visto los primeros pasos para crear una sencilla API con EVE.

Eve tiene muchas mas funcionalidades que iremos descubriendo en sucesivas entradas.

Esperamos que esta introducción a EVE haya sido de vuestra ayuda.