Colibri

Описание

Colibri - это фреймворк для Node.js, который упрощает задачу построения REST-сервисов для управления данными в MongoDB.

Colibri, Express, Mongoose model and MongoDB

Допустим, у нас в mongodb есть коллекция статей Articles и мы хотим управлять ей через REST-ресурс.

Также у нас есть веб-сервер на Express, в который мы хотим добавить наш ресурс. Допустим, это - веб-сервер:

express = require 'express'
app = express.createServer()
app.use express.bodyParser()
app.use express.static "#{__dirname}/static"
app.listen 3000

Кроме того, мы создали модель ArticleModel с помощью Mongoose:

ArticleSchema = new mongoose.Schema
    title : String
    body  : String

ArticleModel = mongoose.model 'Articles', ArticleSchema

Наш REST-ресурс должен обслуживать следующие запросы:

GET /articles         - получить список статей
POST /articles        - создать статью
GET /articles/1234    - получить статью с _id = 1234
PUT /articles/1234    - изменить статью с _id = 1234
DELETE /articles/1234 - удалить статью с _id = 1234

Нас ждёт куча монотоной, кропотливой работы! Придётся написать множество маршрутов app.get(...), app.post(...) и т.д., в которых предстоит реализовать разбор запросов, выборку сущностей, операции с ними, обработку ошибок, и наконец, возврат результата на клиент.

Colibri упрощает эту задачу. Вот как с его помощью строится REST-сервис:

#создаём ресурс
resource = colibri.createResource
    model : ArticleModel

#с помощью ресурса создаём маршруты на сервере app
resource.express app

Теперь у нас есть готовый бэкенд, к которому мы можем, например, подключиться из браузера с помощью Backbone или других клиентских библиотек, поддерживающих REST.

Пример ToDo-приложения на Backbone показан в папке examples.

API

colibri.createResource(options)

Создает REST-ресурс.

options:

возвращаемое значение:

объект класса Resource

Resource#use(plugin)

Добавляет плагин. Плагин - это набор прослоек (route middleware) Express, которые навешиваются после шагов/методов Colibri для изменения их функциональности.

Colibri на каждый запрос создаёт объект req.rest, через свойства которого можно управлять менять поведение REST-метода. Например, добавлять условия в запросы, проверять авторизацию, делать постраничную навигацию, скрывать/добавлять поля в результирующие объекты, выполнять логгирование, обновление связанных записей, и т.д.

Бизнес-логику приложения удобно оформлять в виде таких плагинов.

plugin:

Двухуровневый объект, в котором задаются прослойки, навешиваемые на определённые методы и шаги ресурса.

Имена свойств первого уровня соответствует именам методов, используемых в Colibri: get, put, post, del или list.

Имена свойств второго уровня - именам шагов соответсвующего метода: begin, input, query, serialize, output и пр.

Значения свойств второго уровня - это функции или массивы функций, представляющие собой стандартные прослойки маршрутов (route middleware) Express - функции или массивы функций, выполняющиеся после соответствующих шагов соответствующего метода/шага.

Так, например, если мы хотим добавить сортировку списка по определённому полю, мы должны написать такой плагин:

resource.use({
    //list - имя метода
    list : {
        //query - имя шага.
        query : function (req, res, next) { //эта функция выполнится после шага query
            //В шаге list.query доступен объект req.rest.query,
            //представляющий собой Mongoose-запрос.

            //Добавляем параметр сортировки.
            req.rest.query.sort({my_field_name : 1});
            //Передаем управление дальше.
            next(null);
            //Следующим выполнится шаг load, который использует
            //req.rest.query для выборки документов
        }
    }
});

Список доступных шагов, а также свойств req.rest.*, доступных для модификации, (TODO).

Resource#express(app)

Добавляет созданный ресурс в Express-приложение.

Вызывайте этот метод после всех вызовов Resource#use().

API плагинов

Допустим у нас есть приложение такого вида:

express = require 'express'
app = express.createServer()
app.use express.bodyParser()
app.use express.static "#{__dirname}/static"
app.listen 3000

ArticleSchema = new mongoose.Schema
    title : String
    body  : String

ArticleModel = mongoose.model 'Articles', ArticleSchema

resource = colibri.createResource
    model : ArticleModel

Мы создали “голый” REST-ресурс по адресу /articles/. Он умеет отдавать статьи и списки статей, редактировать и удалять статьи.

Отлично, но мы не хотим, чтобы неавторизованные пользователи могли изменять наши статьи.

Допустим, система авторизации уже есть (ни Colibri, ни Express не предоставляют таковой “из коробки”, но есть отличные сторонние решения), а также предположим, что эта система добавляет переменные req.isAuthorized и req.currentUser.

Тогда мы должны написать плагин для нашего ресурса, который бы проверял req.isAuthorized и отвечал HTTP-кодом 403, если пользователь не авторизован.

Проверять авторизацию будет обычная прослойка (middleware) для Express:

isAuthorized = (req, res, next)->
    return res.send 403 unless req.isAuthorized
    next null

Теперь надо добавить её во все HTTP-методы нашего ресурса, которые мы хотим защитить:

resource.use
    post:
        begin: isAuthorized
    del:
        begin: isAuthorized
    put:
        begin: isAuthorized

Что всё это значит? У каждого HTTP-метода в Colibri-ресурсе есть последовательность фаз (шагов). Например, шаги для метода post таковы:

'begin'   - начало
'input'   - разбор HTTP-запроса
'create'  - создание и заполнение Mongoose-документа
'save'    - сохранение Mongoose-документа
'output'  - выдача Mongoose-документа на клиент

Colibri строит из всех этих шагов цепочку прослоек для Express, которые выполняются как асинхронный конвейер.

К счастью, в Colibri имеются средства для вмешательства в этот процесс. Это делается с помощью метода Resource#use. В него передается объект, в котором определяется в какой HTTP-метод и после какого из его шагов мы хотим вставить собственные функции-прослойки.

Именно это мы сделали, когда добавили нашу функцию isAuthorized в шаг begin методов post, put и delete.

Мы написали простейший плагин, который проверяет авторизацию.

Но с помощью API плагинов можно делать много других вещей. Например, мы можем добавить сортировку списка документов в зависимости от входных параметров GET-запроса. Можем добавить автоматически заполняемые поля (например, ID автора статьи) в обработчик POST-запроса. Можем также реализовать более гибкую проверку правд доступа (например, сделать так, чтобы пользователь мог редактировать только свои статьи, а не чужие). И так далее.

Резюме

Colibri по умолчанию создаёт простейший незащищённый, но уже работоспособный REST-ресурс. Задача разработчика приложения - отсечь лишние возможности и добавить свои плюшки с помощью плагинов.

Документация по плагинам - TODO.

Готовые плагины - тоже пока TODO. В разработке - плагины range и paginate.

Документация по добавлению кастомных методов - TODO.

API для разработчиков плагинов

ALL_METHODS.begin adds req.rest.meta:

ALL_METHODS.begin adds req.rest.model:

ALL_METHODS.begin adds req.rest.currentTime:

ALL_METHODS.output uses req.rest.result:

ALL_METHODS.output uses req.rest.meta:

get.input adds req.rest._id:

get.load uses req.rest._id:

get.load adds req.rest.document:

get.serialize adds req.rest.result:

del.input adds req.rest._id:

del.load uses req.rest._id:

del.load adds req.rest.document:

del.remove uses req.rest.document:

list.query adds req.rest.query:

list.load uses req.rest.query:

list.load adds req.rest.documents:

list.serialize adds req.rest.result:

post.input adds req.rest.fieldValues:

post.create adds req.rest.document:

post.save uses req.rest.document:

post.serialize adds req.rest.result:

put.input adds req.rest._id:

put.input adds req.rest.fieldValues:

put.load uses req.rest._id:

put.load uses req.rest.currentTime:

put.load adds req.rest.document:

put.update uses req.rest.fieldValues:

put.update uses req.rest.document:

put.save uses req.rest.document:

put.serialize adds req.rest.result: