Введение: Зачем нужен RESTful сервис на Meteor


Meteor привлекает простотой использования и возможностью очень быстро создать работающее приложение с минимальным набором функций. У Meteor — хорошо развитое сообщество. Есть множество полезных дополнительных модулей, которые не требуют сложной настройки, и могут быть использованы сразу после установки. Есть хорошая документация, примеры и большое количество постов на форумах, вроде StackOverflow. Meteor — это full-stack фреймворк, который предлагает удобную и многофункциональную интеграцию сервера с клиентом. Так зачем же выходить за рамки этого взаимодействия, и создавать RESTful сервис?

Клиент-серверное приложение, по-сути, состоит из 2 независимых частей, которые которые взаимодействуют посредством определенного интерфейса. При этом каждая из частей клиент-серверного приложения может создаваться разными людьми или командами. Разработчики клиенсткой части вовсе не ограничены использованием Meteor, они могут использовать любой другой JS фреймворк, клиент даже не обязательно должен быть написан на JS, это может быть к примеру приложение Android, написанное на Java, или iOS, написанное на Objective C.

Именно эти причины заставили меня выбрать Meteor для построения back end в моем проекте, и искать пути для создания RESTful сервиса на Meteor.

Обзор имеющихся модулей


После некоторого времени, потраченного на поиск подходящий модулей, у меня появился следующий список:

github.com/meteorhacks/picker — использует маршрутизацию аналогичную той, что используется, скажем, в Express.js

Picker.route('/post/:_id', function(params, req, res, next) {
  var post = Posts.findOne(params._id);
  res.end(post.content);
});

с возможностью отправки ответа в формате JSON

github.com/crazytoad/meteor-collectionapi позволяет создать API Endpoints для CRUD операций над коллекциями. Нет механизмов для разграничения уровней доступа (к примеру: гость, авторизованный пользователь, администратор), авторизации или создания custom endpoints.

github.com/kahmali/meteor-restivus позволяет создать API Endpoints для CRUD операций над коллекциями. Имеются механизмы авторизации, разграничения уровней доступа и создания custom endpoints. Об этом пакете будет рассказано подробней чуть ниже.

github.com/Differential/reststop2 — устаравшее и неподдерживаемое решение. На главной странице ресурса — ссылка на проект Restivus с указанием о том, что все имеющиеся функции этого решения также есть и в Restivus.

github.com/stubailo/meteor-rest позволяет создать API Endpoints для CRUD операций над коллекциями. Имеются механизмы авторизации и создания custom endpoints. Нет механизмов для разграничения уровней доступа. Документации не хватает ясности и работающих примеров. Позволяет интегрировать Restivus для создания custom endpoints.

Поскольку Restivus имеет весь необходимый мне набор функций, и был отмечен наибольшим количеством звезд как на GitHub, так и на Atmosphere, а также по причине того, что 2 других проекта из приведенного списка ссылались на Restivus, я решил остановить свой выбор именно на нем.

Использование коллекций, CRUD операции и уровни доступа


Использовать Restivus — очень просто. Чтобы установить, введите в консоли:

meteor add nimble:restivus

Чтобы создать RESTful сервис, добавьте в серверный код следующие сроки:

if (Meteor.isServer) {
	var Api = new Restivus({useDefaultAuth: true});
}

В аргументах конструктора передаются опции API. Значение опции `useDefaultAuth` рассмотрим в слеудющем разделе.

Если у вас зарегистрирована коллекция, скажем

var Contacts = new Mongo.Collection('contacts');

И вы хотите открыть к ней доступ посредством CRUD операций через API, в простейшей форме достаточно сделать:

Api.addCollection(Contacts);

Это создаст следующие Endpoints:

`getAll` Endpoint
GET /api/collection
Вернуть информацию обо всех элементах коллекции

`post` Endpoint
POST /api/collection
Добавить новый элемент в коллекцию

`get` Endpoint
GET /api/collection/:id
Вернуть информацию об элементе коллекции

`put` Endpoint
PUT /api/collection/:id
Изменить элемент коллекции

`delete` Endpoint
DELETE /api/collection/:id
Удалить элемент из коллекции

Формат ответа всегда следующий:

{status: "success", data: {}}

Префикс `api/` может быть заменен на другой посредством установки опции `apiPath`, переданной при создании API. Как вы видите, у каждой API операции над коллекцией есть свой идентификатор. Он может быть использован следующим образом:

Api.addCollection(Contacts, {
    excludedEndpoints: ['getAll', 'put'],
    routeOptions: {
      authRequired: true
    },
    endpoints: {
      get: {
        authRequired: false
      },
      delete: {
        roleRequired: 'admin'
      }
    }
  });

В этом случае в API была зарегистрирована коллекция с операциями delete, get и post. Для операции get авторизация не требуется, для операций post и delete — требуется, при этом для операцию delete может выполнить только администратор. Об авторизации и аутентификации читайте далее

Авторизация и аутентификация


После регистрации в API коллекции users добавляются 2 специальные endpoints:

POST /api/login
GET|POST /api/logout

Несмотря на заверения из документации, в версии Restivus, которую использовал я (0.8.4), разлогивание работало только при GET запросе. Более подробно об этом можете посмотреть здесь.

Чтобы авторизоваться, нужно передать имя пользователя и пароль в следующем виде:

curl http://localhost:3000/api/login/ -d "username=test&password=password"

В случае неудачи в теле ответа прийдет строка 'Unauthorized'.
В случае успеха прийдет следующий ответ:

{status: "success", data: {authToken: "f2KpRW7KeN9aPmjSZ", userId: fbdpsNf4oHiX79vMJ}}

Сохраните токен и id пользователя, чтобы передавать в заголовках запросов к API Endpoints, требующих авторизации:

curl -H "X-Auth-Token: f2KpRW7KeN9aPmjSZ" -H "X-User-Id: fbdpsNf4oHiX79vMJ" http://localhost:3000/api/contacts/


Custom Endpoints


Api.addRoute('contacts/favorite/:userId', {
	get: {
		authRequired: false,
		roleRequired: ['author', 'admin'],
		action: function () {
			this.response.write({"user-id": this.urlParams.userId});
			this.done();
		}
	}
});

Этот фрагмент кода регистрирует API Endpoint по адресу api/contacts/favorite/. Параметр `userId`, переданный как часть пути, будет доступен как `this.urlParams.userId`. Параметры GET запроса будут доступны через `queryParams`. Если не требуется описывать опции для авторизации и уровней доступа, то можно использовать краткую форму:

Api.addRoute('contacts/favorite/:userId', {
	get: function () {
		this.response.write({"user-id": this.urlParams.userId});
		this.done();
	}
});

Итоги: За и против построения RESTful сервиса на Meteor


Как мы видим, Meteor в очередной раз предоставил удобный инструмент для быстрого создания прототипа, что является очень ценным на ранних стадиях проекта. Нередко приложения не выходят за рамки работы с тектовыми и числовыми данными. Таким образом мы имеем: Meter и Restivus + resource механизм (имеющийся, к примеру в Angular или Vue) = взаимодействие между клиентом и сервером, созданное за считанные минуты.
Поделиться с друзьями
-->

Комментарии (6)


  1. gearbox
    06.05.2016 22:43

    Вот бы кто еще пощупал генераторов со swagger или raml и рассказал про свои ощущения :) Типа этого https://github.com/apinf/restivus-swagger или этого https://github.com/warehouseman/meteor-swagger-client
    За статью — спасибо!


  1. HuKaToH
    07.05.2016 07:25
    +2

    Из вашего введения как-то не совсем ясно зачем создавать RESTful сервис на метор для комуникации клиента и сервера. Meteor имеет всроенный bidirectional DDP connection, что позволяет пересылать данные более эффективно. Более того, официальная документация довольно подробно открывает тему REST в Метеор (http://meteorpedia.com/read/REST_API). Вкратце:

    " it's critical to understand that if you are making an API for your own apps and tools, you've got it wrong! We hope it's very clear that between the server and client, you have a bidirectional DDP connection that handles realtime syncing of published data (see Publish and Subscribe in the docs).

    Beyond that, if you're building an external tool that requires live data and will manipulate this data, the correct way to do this is with a DDP Client. This will save you a lot of wasted time and effort in writing completely unnecessary code."


    1. keylase
      07.05.2016 21:37

      Тут важный момент в том, что некоторые клиенты все же не поддерживают DDP из коробки и RESTful интерфейсы являются делом привычным и простым. К тому же часто бывает, что нужно перенести функционал старого RESTful api на meteor.


    1. sijio
      08.05.2016 04:56

      В моем случае заказчик выбрал Meteor из-за развитого сообщества: весь нужный ему функционал он нашел на Atmosphere, и посчитал, что если ему понадобится что-то еще, он так же сможет найти нужный ему пакет, и легко самостоятельно его встроить. Возможности, предоставляемые DDP никому в проекте не были нужны. Я занимался созданием back end и веб клиента, а над Android и iOS приложениями работали 2 других программиста. Они привыкли работать через RESTful API. Изучать DDP клиента они не хотели (а для Objective C, насколько я знаю, его и нет). Поэтому в реальной жизни приходится считаться с факторами, о которых в руководстве по Meteor ничего не написано.


      1. HuKaToH
        09.05.2016 08:33

        DPP clients есть практически на все: ( http://meteorpedia.com/read/DDP_Clients ). Плюс ко всему в метеоре есть понятие Optimistic UI которое базируется на интеграции данных с помощью DDP, LiveQuery, and Minimongо и выпилвания DDP вместе с фичами subscribe and publish создает из Метеора хромую лошадь. ( http://info.meteor.com/blog/optimistic-ui-with-meteor-latency-compensation ).


        1. sijio
          09.05.2016 16:30

          Вы, вроде, спрашивали, зачем использовать REST. Я объяснил, по каким причинам был выбран Meteor, почему использование REST было правильным решением, и что DDP не был нужен, вообще. А вы — опять со своим DDP! Если вы хотите поведать о DDP клиентах, полагаю, вам лучше написать новую статью, а не использовать для этого комментарии