Пусть LoopBack сделает это: пошаговое руководство по Node API Framework, о котором вы мечтали
Опубликовано: 2022-03-11Нет необходимости упоминать растущую популярность Node.js для разработки приложений. eBay запускает производственный сервис Node API с 2011 года. PayPal активно перестраивает свой интерфейс на Node. Мобильный сайт Walmart стал крупнейшим приложением Node с точки зрения трафика. В выходные дни Благодарения в 2014 году серверы Walmart обработали 1,5 миллиарда запросов, 70 процентов из которых были доставлены через мобильные устройства и на базе Node.js. Что касается разработки, менеджер пакетов Node (npm) продолжает быстро расти, недавно превысив 150 000 размещенных модулей.
Хотя в Ruby есть Rails, а в Python есть Django, доминирующая среда разработки приложений для Node еще не создана. Но есть мощный соперник, набирающий обороты: LoopBack, инфраструктура API с открытым исходным кодом, созданная Сан-Матео, Калифорния, компанией StrongLoop. StrongLoop вносит важный вклад в последнюю версию Node, не говоря уже о давних мейнтейнерах Express, одной из самых популярных существующих сред Node.
Давайте подробнее рассмотрим LoopBack и его возможности, применив все на практике и создав пример приложения.
Что такое LoopBack и как он работает с Node?
LoopBack — это платформа для создания API и их подключения к внутренним источникам данных. Построенный на основе Express, он может взять определение модели данных и легко создать полнофункциональный сквозной REST API, который может быть вызван любым клиентом.
LoopBack поставляется со встроенным клиентом API Explorer . Мы будем использовать это, так как это упрощает просмотр результатов нашей работы, и чтобы наш пример мог сосредоточиться на создании самого API.
Вам, конечно, потребуется установить Node на вашем компьютере, чтобы следовать дальше. Получи это здесь. npm поставляется с ним, так что вы можете легко установить необходимые пакеты. Давайте начнем.
Создать скелет
Наше приложение будет управлять людьми, которые хотели бы пожертвовать подарки или вещи, которые им больше не нужны, тем, кому они могут понадобиться. Таким образом, пользователи будут донорами и получателями. Донор может создать новый подарок и посмотреть список подарков. Получатель может видеть список подарков от всех пользователей и может забрать те, которые не были получены. Конечно, мы могли бы создать Доноров и Получателей как отдельные роли для одного и того же объекта (Пользователя), но давайте попробуем разделить их, чтобы увидеть, как строить отношения в LoopBack. Имя этого новаторского приложения будет Givesomebody .
Установите инструменты командной строки StrongLoop через npm:
$ npm install -g strongloop
Затем запустите генератор приложений LoopBack:
$ slc loopback _-----_ | | .--------------------------. |--(o)--| | Let's create a LoopBack | `--------- | application! | ( _U`_ ) '--------------------------' /___A___\ | ~ | __'.___.'__ ` |° Y ` ? What's the name of your application? Givesomebody
Добавим модель. Наша первая модель будет называться Gift. LoopBack запросит источник данных и базовый класс. Поскольку мы еще не настроили источник данных, мы можем указать db (memory)
. Базовый класс — это автоматически сгенерированный класс модели, и мы хотим использовать в данном случае PersistedModel
, так как он уже содержит все обычные для нас методы CRUD. Затем LoopBack спрашивает, следует ли предоставлять модель через REST (да), и имя службы REST. Нажмите здесь, чтобы использовать значение по умолчанию, которое представляет собой просто множественное число имени модели (в нашем случае, gifts
).
$ slc loopback:model ? Enter the model name: Gift ? Select the data-source to attach Gift to: (Use arrow keys) ❯ db (memory) ? Select model's base class: (Use arrow keys) Model ❯ PersistedModel ? Expose Gift via the REST API? (Y/n) Yes ? Custom plural form (used to build REST URL):
Наконец, мы даем имена свойств, их типы данных и обязательные/необязательные флаги. Подарок будет иметь свойства name
и description
:
Let's add some Gift properties now. Enter an empty property name when done. ? Property name: name invoke loopback:property ? Property type: (Use arrow keys) ❯ string ? Required? (y/N)Yes
Введите пустое имя свойства, чтобы указать, что вы закончили определение свойств.
Генератор модели создаст два файла, которые определяют модель в папке common/models
приложения: gift.json
и gift.js
В файле JSON указаны все метаданные об объекте: свойства, отношения, проверки, роли и имена методов. Файл JavaScript используется для определения дополнительного поведения и указания удаленных перехватчиков, которые должны вызываться до или после определенных операций (например, создания, обновления или удаления).
Два других объекта модели будут нашими моделями Donor и Receiver. Мы можем создать их, используя тот же процесс, за исключением того, что на этот раз давайте поместим User
в качестве базового класса. Это даст нам некоторые свойства, такие как username
, password
, email
из коробки. Мы можем добавить только имя и страну, например, чтобы иметь полную сущность. Для Получателя мы также хотим добавить адрес доставки.
Структура проекта
Давайте посмотрим на сгенерированную структуру проекта:
Три основных каталога: - /server
— содержит сценарии приложений узла и файлы конфигурации. - /client
— содержит .js, .html, .css и все другие статические файлы. - /common
— эта папка является общей как для сервера, так и для клиента. Файлы моделей идут сюда.
Вот подробная информация о содержимом каждого каталога, взятая из документации LoopBack:
Файл или каталог | Описание | Как получить доступ в коде |
---|---|---|
Каталог приложений верхнего уровня | ||
package.json | Спецификация стандартного пакета npm. См. package.json | Н/Д |
Каталог /server — файлы приложений Node. | ||
server.js | Основной файл прикладной программы. | Н/Д |
config.json | Настройки приложения. См. config.json. | app.get('setting-name') |
datasources.json | Файл конфигурации источника данных. См. источники данных.json. Пример см. в разделе Создание нового источника данных . | app.datasources['datasource-name'] |
model-config.json | Файл конфигурации модели. См. модель-config.json. Для получения дополнительной информации см. Подключение моделей к источникам данных . | Н/Д |
middleware.json | Файл определения промежуточного ПО. Дополнительные сведения см. в разделе Определение промежуточного программного обеспечения. | Н/Д |
/boot каталог | Добавьте сценарии для выполнения инициализации и настройки. См. загрузочные скрипты. | Скрипты автоматически выполняются в алфавитном порядке. |
Каталог /client - файлы клиентского приложения | ||
README.md | Генераторы LoopBack создают пустой файл README в формате уценки. | Н/Д |
Другой | Добавьте свои HTML, CSS, клиентские файлы JavaScript. | |
/common directory — общие файлы приложений | ||
/models | Файлы пользовательских моделей:
| Узел:myModel = app.models.myModelName |
Стройте отношения
В нашем примере у нас есть несколько важных отношений для моделирования. Даритель может пожертвовать много Даров, что дает отношение Даритель имеет много Даров. Получатель также может получать много подарков, поэтому у нас также есть отношение Получатель имеет много подарков . С другой стороны, Подарок принадлежит Дарителю , а также может принадлежать Получателю , если Получатель решит его принять. Давайте переведем это на язык LoopBack.
$ slc loopback:relation ? Select the model to create the relationship from: Donor ? Relation type: has many ? Choose a model to create a relationship with: Gift ? Enter the property name for the relation: gifts ? Optionally enter a custom foreign key: ? Require a through model? No
Обратите внимание, что сквозной модели нет; мы просто держим ссылку на Дар.
Если мы повторим вышеописанную процедуру для Получателя и добавим два отношения принадлежности к Подарку, мы завершим разработку нашей модели на задней стороне. LoopBack автоматически обновляет файлы JSON для моделей, чтобы точно выразить то, что мы только что сделали, с помощью этих простых диалогов:
// common/models/donor.json ... "relations": { "gifts": { "type": "hasMany", "model": "Gift", "foreignKey": "" } }, ...
Добавить источник данных
Теперь давайте посмотрим, как подключить реальный источник данных для хранения всех данных нашего приложения. Для целей этого примера мы будем использовать MongoDB, но у LoopBack есть модули для подключения к Oracle, MySQL, PostgreSQL, Redis и SQL Server.
Сначала устанавливаем коннектор:
$ npm install --save loopback-connector-mongodb
Затем добавьте источник данных в свой проект:
$ slc loopback:datasource ? Enter the data-source name: givesomebody ? Select the connector for givesomebody: MongoDB (supported by StrongLoop)
Следующим шагом будет настройка вашего источника данных в server/datasources.json
. Используйте эту конфигурацию для локального сервера MongoDB:
... "givesomebody": { "name": "givesomebody", "connector": "mongodb", "host": "localhost", "port": 27017, "database": "givesomebody", "username": "", "password": "" } ...
Наконец, откройте server/model-config.json
и измените datasource
для всех сущностей, которые мы хотим сохранить в базе данных, на "givesomebody"
.
{ ... "User": { "dataSource": "givesomebody" }, "AccessToken": { "dataSource": "givesomebody", "public": false }, "ACL": { "dataSource": "givesomebody", "public": false }, "RoleMapping": { "dataSource": "givesomebody", "public": false }, "Role": { "dataSource": "givesomebody", "public": false }, "Gift": { "dataSource": "givesomebody", "public": true }, "Donor": { "dataSource": "givesomebody", "public": true }, "Receiver": { "dataSource": "givesomebody", "public": true } }
Тестирование вашего REST API
Пришло время посмотреть, что мы уже построили! Мы будем использовать замечательный встроенный инструмент API Explorer , который можно использовать в качестве клиента для только что созданного нами сервиса. Давайте попробуем протестировать вызовы REST API.
В отдельном окне запустите MongoDB командой:
$ mongod
Запустите приложение с помощью:
$ node .
В браузере перейдите по http://localhost:3000/explorer/
. Вы можете увидеть свои объекты со списком доступных операций. Попробуйте добавить одного донора с помощью вызова POST /Donors
.
API Explorer очень интуитивно понятен; выберите любой из представленных методов, и соответствующая схема модели будет отображаться в правом нижнем углу. В области текста data
можно написать собственный HTTP-запрос. После заполнения запроса нажмите кнопку «Попробовать», и ниже отобразится ответ сервера.

Аутентификация пользователя
Как упоминалось выше, одной из предварительно созданных сущностей LoopBack является класс User. Пользователь имеет методы входа в систему и выхода из системы и может быть привязан к объекту AccessToken, который хранит токен конкретного пользователя. Фактически, полная система аутентификации пользователей готова к работе «из коробки». Если мы попытаемся вызвать /Donors/login
через API Explorer , то получим вот такой ответ:
{ "id": "9Kvp4zc0rTrH7IMMeRGwTNc6IqNxpVfv7D17DEcHHsgcAf9Z36A3CnPpZJ1iGrMS", "ttl": 1209600, "created": "2015-05-26T01:24:41.561Z", "userId": "" }
id
на самом деле является значением AccessToken, автоматически сгенерированным и сохраненным в базе данных. Как вы видите здесь, можно установить токен доступа и использовать его для каждого последующего запроса.
Удаленные методы
Удаленный метод — это статический метод модели, доступный через настраиваемую конечную точку REST. Удаленные методы могут использоваться для выполнения операций, не предусмотренных стандартной моделью REST API LoopBack.
Помимо методов CRUD, которые мы получаем из коробки, мы можем добавить столько пользовательских методов, сколько захотим. Все они должны войти в файл [model].js
. В нашем случае давайте добавим в модель Gift удаленный метод, чтобы проверить, зарезервирован ли уже подарок, и метод для вывода списка всех незарезервированных подарков.
Во-первых, давайте добавим в модель дополнительное свойство, называемое reserved
. Просто добавьте это в свойства в gift.json
:
... "reserved": { "type": "boolean" } ...
Удаленный метод в gift.js
должен выглядеть примерно так:
module.exports = function(Gift) { // method which lists all free gifts Gift.listFree = function(cb) { Gift.find({ fields: { reserved: false } }, cb); }; // expose the above method through the REST Gift.remoteMethod('listFree', { returns: { arg: 'gifts', type: 'array' }, http: { path: '/list-free', verb: 'get' } }); // method to return if the gift is free Gift.isFree = function(id, cb) { var response; Gift.find({ fields: { id: id } }, function(err, gift) { if (err) return cb(err); if (gift.reserved) response = 'Sorry, the gift is reserved'; else response = 'Great, this gift can be yours'; }); cb(null, response); }; // expose the method through REST Gift.remoteMethod('isFree', { accepts: { arg: 'id', type: 'number' }, returns: { arg: 'response', type: 'string' }, http: { path: '/free', verb: 'post' } }); };
Таким образом, чтобы узнать, доступен ли конкретный подарок, клиент теперь может отправить запрос POST в /api/Gifts/free
, передав id
соответствующего подарка.
Удаленные хуки
Иногда возникает необходимость выполнения какого-либо метода до или после удаленного метода. Вы можете определить два типа удаленных перехватчиков:
-
beforeRemote()
запускается перед удаленным методом. -
afterRemote()
запускается после удаленного метода.
В обоих случаях вы предоставляете два аргумента: строку, соответствующую удаленному методу, к которому вы хотите «подцепить» свою функцию, и функцию обратного вызова. Большая часть силы удаленных ловушек заключается в том, что строка может включать подстановочные знаки, поэтому она запускается любым методом сопоставления.
В нашем случае давайте установим хук для вывода информации на консоль всякий раз, когда создается новый донор. Для этого добавим хук «before create» в donor.js
:
module.exports = function(Donor) { Donor.beforeRemote('create', function(context, donor, next) { console.log('Saving new donor with name: ', context.req.body.name); next(); }); };
Запрос вызывается с заданным context
, а обратный вызов next()
в промежуточном программном обеспечении (обсуждается ниже) вызывается после запуска хука.
Контроль доступа
Приложения LoopBack получают доступ к данным через модели, поэтому управление доступом к данным означает определение ограничений для моделей; то есть указание того, кто или что может читать и записывать данные или выполнять методы в моделях. Контроль доступа LoopBack определяется списками управления доступом или ACL.
Давайте разрешим не вошедшим в систему Дарителям и Получателям просматривать подарки, но только вошедшим в систему Дарителям создавать и удалять их.
$ slc loopback:acl
Для начала давайте запретим всем доступ ко всем конечным точкам.
? Select the model to apply the ACL entry to: Gift ? Select the ACL scope: All methods and properties ? Select the access type: All (match all types) ? Select the role: All users ? Select the permission to apply: Explicitly deny access
Далее разрешите всем читать из моделей Gift:
$ slc loopback:acl ? Select the model to apply the ACL entry to: Gift ? Select the ACL scope: All methods and properties ? Select the access type: Read ? Select the role: All users ? Select the permission to apply: Explicitly grant access
Затем мы хотим разрешить аутентифицированным пользователям создавать подарки:
$ slc loopback:acl ? Select the model to apply the ACL entry to: Gift ? Select the ACL scope: A single method ? Enter the method name: create ? Select the role: Any authenticated user ? Select the permission to apply: Explicitly grant access
И, наконец, давайте позволим владельцу подарка вносить любые изменения:
$ slc loopback:acl ? Select the model to apply the ACL entry to: Gift ? Select the ACL scope: All methods and properties ? Select the access type: Write ? Select the role: The user owning the object ? Select the permission to apply: Explicitly grant access
Теперь, когда мы просматриваем gift.json
, все должно быть на своих местах:
"acls": [ { "accessType": "*", "principalType": "ROLE", "principalId": "$everyone", "permission": "DENY" }, { "accessType": "READ", "principalType": "ROLE", "principalId": "$everyone", "permission": "ALLOW" }, { "accessType": "EXECUTE", "principalType": "ROLE", "principalId": "$authenticated", "permission": "ALLOW", "property": "create" } ],
Одно важное замечание: $authenticated
— это предопределенная роль, которая соответствует всем пользователям в системе (как Дарителям, так и Получателям), но мы хотим разрешить Дарителям создавать новые подарки только. Поэтому нам нужна пользовательская роль. Поскольку роль — это еще одна сущность, которую мы получаем из коробки, мы можем использовать ее вызов API для создания роли $authenticatedDonor
в функции загрузки, а затем просто изменить pricipalId
в gift.json
.
Необходимо будет создать новый файл server/boot/script.js
и добавить следующий код:
Role.create({ name: 'authenticatedDonor' }, function(err, role) { if (err) return debug(err); })
Сущность RoleMapping сопоставляет роли с пользователями. Убедитесь, что Role и RoleMapping доступны через REST. В server/model-config.json
убедитесь, что для объекта «Роль» для параметра "public"
установлено значение true
. Затем в donor.js
мы можем написать хук «перед созданием», который сопоставит userID
и идентификатор роли в вызове POST API roleID
.
ПО промежуточного слоя
Промежуточное ПО содержит функции, которые выполняются, когда делается запрос к конечной точке REST. Поскольку LoopBack основан на Express, он использует промежуточное ПО Express с одной дополнительной концепцией, называемой «этапами промежуточного ПО». Фазы используются для четкого определения порядка, в котором вызываются функции в промежуточном программном обеспечении.
Вот список предопределенных фаз, как указано в документации LoopBack:
- Initial — первая точка, в которой может работать промежуточное ПО.
- session - подготовьте объект сеанса.
- auth — Обрабатывать аутентификацию и авторизацию.
- parse — разобрать тело запроса.
- маршруты — HTTP-маршруты, реализующие логику вашего приложения. ПО промежуточного слоя, зарегистрированное через Express API, app.use, app.route, app.get (и другие глаголы HTTP) запускается в начале этой фазы. Используйте эту фазу также для дополнительных приложений, таких как loopback/server/middleware/rest или loopback-explorer.
- файлы — обслуживать статические ресурсы (здесь запросы попадают в файловую систему).
- final - Работа с ошибками и запросами на неизвестные URL-адреса.
Каждая фаза имеет три подфазы. Например, подэтапами начальной фазы являются:
- инициал: до
- исходный
- инициал: после
Давайте быстро взглянем на наш middleware.json по умолчанию:
{ "initial:before": { "loopback#favicon": {} }, "initial": { "compression": {}, "cors": { "params": { "origin": true, "credentials": true, "maxAge": 86400 } } }, "session": { }, "auth": { }, "parse": { }, "routes": { }, "files": { }, "final": { "loopback#urlNotFound": {} }, "final:after": { "errorhandler": {} } }
На начальном этапе мы вызываем loopback.favicon()
( loopback#favicon
— это идентификатор промежуточного слоя для этого вызова). Затем вызывается compression
и cors
сторонних модулей npm (с параметрами или без). На заключительном этапе у нас есть еще два звонка. urlNotFound
— это вызов errorhandler
, а обработчик ошибок — сторонний модуль. Этот пример должен продемонстрировать, что множество встроенных вызовов можно использовать так же, как и внешние модули npm. И, конечно же, мы всегда можем создать собственное промежуточное ПО и вызывать его через этот JSON-файл.
loopback-boot
В завершение упомянем модуль, который экспортирует функцию boot()
, которая инициализирует приложение. В server/server.js
вы найдете следующий фрагмент кода, который загружает приложение:
boot(app, __dirname, function(err) { if (err) throw err; // start the server if `$ node server.js` if (require.main === module) app.start(); });
Этот скрипт выполнит поиск в папке server/boot
и загрузит все найденные скрипты в алфавитном порядке. Таким образом, в server/boot
мы можем указать любой скрипт, который должен запускаться при старте. Одним из примеров является explorer.js
, который запускает API Explorer , клиент, который мы использовали для тестирования нашего API.
Заключение
Прежде чем покинуть вас, я хотел бы упомянуть StrongLoop Arc, графический пользовательский интерфейс, который можно использовать в качестве альтернативы инструментам командной строки slc
. Он также включает инструменты для создания, профилирования и мониторинга приложений Node. Для тех, кто не является поклонником командной строки, это определенно стоит попробовать. Тем не менее, StrongLoop Arc скоро станет устаревшим, и его функциональные возможности будут интегрированы в IBM API Connect Developer Toolkit.
Вообще говоря, LoopBack может сэкономить вам много ручной работы, поскольку вы получаете много вещей из коробки. Это позволяет вам сосредоточиться на проблемах конкретного приложения и бизнес-логике. Если ваше приложение основано на операциях CRUD и манипулировании предопределенными сущностями, если вам надоело переписывать инфраструктуру аутентификации и авторизации пользователя, когда до вас это написали тонны разработчиков, или если вы хотите использовать все преимущества отличной веб-инфраструктуры, такой как Express, то создание REST API с помощью LoopBack может воплотить ваши мечты в реальность. Это кусок торта!