Loopback был создан компанией Strongloop как мобильный фреймворк как услуга с открытым исходным кодом. Он позволяет настроить REST API за считанные минуты и основан на Экспрессе.

В этом посте мы будем использовать версию 3.4.0 для создания API для бронирования кемпингов:

Начиная

Loopback поставляется с инструментом CLI для создания приложения. Вы можете настроить все вручную, но инструмент командной строки - это просто действительно удобный подарок для начала. Установите его через диспетчер пакетов узлов: npm install -g loopback-cli

После установки введите lb, чтобы запустить yeoman-generator:

     _-----_     
    |       |    ╭──────────────────────────╮
    |--(o)--|    │  Let's create a LoopBack │
   `---------´   │       application!       │
    ( _´U`_ )    ╰──────────────────────────╯
    /___A___\   /
     |  ~  |     
   __'.___.'__   
 ´   `  |° ´ Y ` 

? What's the name of your application? reservations
? Enter name of the directory to contain the project: reservations
   create reservations/
     info change the working directory to reservations

? Which version of LoopBack would you like to use? 3.x (current)
? What kind of application do you have in mind? empty-server (An empty LoopBack API, without any configured models or datasources)

Когда закончите, вы увидите следующую структуру проекта. Файлы JSON предназначены для конфигурации, а файлы Javascript - для расширения Express.

reservations/ 
├── client                       # Client JS, HTML and CSS files
│ └── README.md                  # Empty README.md file
├── package.json                 # Npm package specification
└── server                       # Node scripts and config 
 ├── boot                        # Initialization scripts
 │ └── root.js                   # Specify the contextroot
 ├── component-config.json       # Loopback components config
 ├── config.json                 # Global settings
 ├── datasources.json            # Datasource config
 ├── middleware.development.json # Middleware config for dev
 ├── middleware.json             # Middleware config
 ├── model-config.json           # Binds models to datasources
 └── server.js                   # Main application script

Перейдите в папку резервирования и запустите приложение, запустив npm start. Откройте в браузере http: // localhost: 3000 / explorer, чтобы увидеть базовый Swagger-UI.

Упорство

Посмотрите server / datasources.json. У нас еще нет настроенных источников данных. Здесь мы будем использовать базу данных в памяти, но есть много готовых соединителей базы данных.

Введите lb datasource, чтобы снова запустить генератор.

? Enter the data-source name: reservationDS
? Select the connector for reservationDS: In-memory db (supported by StrongLoop)
Connector-specific configuration:
? window.localStorage key to use for persistence (browser only): 
? Full path to file for persistence (server only): db.json

Файл db.json сохранит данные в памяти в файле. Это позволяет нам сохранить наши данные при перезапуске сервера. Это также позволяет нам запустить наше приложение с некоторыми тестовыми данными.

Мы создадим этот файл позже. Loopback не выдаст никаких предупреждений, если этот файл еще недоступен.

Модели

Loopback разработан вокруг концепции модели. Давайте создадим модель для нашего кемпинга, фунтовая модель

? Enter the model name: campground
? Select the data-source to attach campground to: reservationDS (memory)
? Select model's base class PersistedModel
? Expose campground via the REST API? Yes
? Custom plural form (used to build REST URL): 
? Common model or server only? server
Let's add some campground properties now.

Enter an empty property name when done.
? Property name: name
   invoke   loopback:property
? Property type: string
? Required? Yes
? Default value[leave blank for none]: 

Let's add another campground property.
Enter an empty property name when done.
? Property name: location
   invoke   loopback:property
? Property type: geopoint
? Required? No
? Default value[leave blank for none]: 

Let's add another campground property.
Enter an empty property name when done.
? Property name:

Мы создали модель палаточного лагеря и извлекли ее из PersistedModel, поэтому мы можем сохранить ее в нашем источнике данных. Вы можете сделать модель общей, что означает, что одна и та же модель может использоваться совместно клиентом и сервером
, но мы решили использовать ее только для сервера.

Наша модель имеет 2 свойства: имя и местоположение. Свойство id добавляется автоматически, поэтому вам не нужно его добавлять.

Посетите обозреватель API по адресу http: // localhost: 3000 / explorer. Теперь вы увидите множество конечных точек, доступных для наших
кемпингов.

Давайте протестируем наш API, получив все палаточные лагеря:

curl -X GET 'http://localhost:3000/api/campgrounds'

Поскольку у нас нет данных, ответ будет пустым списком. Теперь мы создаем файл db.json, который мы указали в предыдущем разделе. Создайте файл db.json в корне проекта.

{
  "ids": {
    "campground": 5
  },
  "models": {
     "campground": {
       "1": "{\"name\":\"Salt Lake City KOA\",\"location\":{\"lat\": 40.772112, \"lng\": -111.932165},\"id\":1}",
       "2": "{\"name\":\"Gouldings Campground\",\"location\":{\"lat\": 37.006989, \"lng\": -110.214907},\"id\":2}",
       "3": "{\"name\":\"Grand Canyon Mather Campground\",\"location\":{\"lat\": 36.056472, \"lng\": -112.140728},\"id\":3}",
       "4": "{\"name\":\"Camping Paris Bois de Boulogne\",\"location\":{\"lat\": 48.868879, \"lng\": 2.234914},\"id\":4}"
     }
  }
}

Перезагрузите сервер и попробуйте еще раз. Теперь вы должны увидеть 4 кемпинга.
Мы закончим эту часть созданием нашей модели бронирования, lb model:

? Enter the model name: reservation
? Select the data-source to attach reservation to: reservationDS (memory)
? Select model's base class PersistedModel
? Expose reservation via the REST API? Yes
? Custom plural form (used to build REST URL): 
? Common model or server only? server
Let's add some reservation properties now.

Enter an empty property name when done.
? Property name: startDate
   invoke   loopback:property
? Property type: date
? Required? Yes
? Default value[leave blank for none]: 

Let's add another reservation property.
Enter an empty property name when done.
? Property name: endDate
   invoke   loopback:property
? Property type: date
? Required? Yes
? Default value[leave blank for none]: 

Let's add another reservation property.
Enter an empty property name when done.
? Property name:

связи

У кемпингов может быть ноль или более бронирований. Для этого мы должны создать связь между нашими моделями, lb Relations:

? Select the model to create the relationship from: campground
? Relation type: has many
? Choose a model to create a relationship with: reservation
? Enter the property name for the relation: reservations
? Optionally enter a custom foreign key: 
? Require a through model? No

Снова запустите сервер и перейдите в API Explorer. Вы увидите несколько новых конечных точек для / campgrounds / {id} / reservations.

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

{
  "ids": {
    "campground": 5,
    "reservation": 2
  },
  "models": {
     "campground": {
       "1": "{\"name\":\"Salt Lake City KOA\",\"location\":{\"lat\": 40.772112, \"lng\": -111.932165},\"id\":1}",
       "2": "{\"name\":\"Gouldings Campground\",\"location\":{\"lat\": 37.006989, \"lng\": -110.214907},\"id\":2}",
       "3": "{\"name\":\"Grand Canyon Mather Campground\",\"location\":{\"lat\": 36.056472, \"lng\": -112.140728},\"id\":3}",
       "4": "{\"name\":\"Camping Paris Bois de Boulogne\",\"location\":{\"lat\": 48.868879, \"lng\": 2.234914},\"id\":4}"
     },
     "reservation": {
       "1": "{\"startDate\":\"2017-03-21\",\"endDate\":\"2017-03-23\",\"campgroundId\":1,\"id\":1}",
       "2": "{\"startDate\":\"2017-03-25\",\"endDate\":\"2017-03-31\",\"campgroundId\":2,\"id\":2}"
     }
  }
}

Запросы

Конечные точки петли также можно использовать для запроса определенных данных.
Вот некоторые из стандартных возможностей:

  • Показать все кемпинги с «KOA» в названии
    / api / campgrounds? Filter [where] [name] [like] = KOA
  • Показать все бронирования после или после 22 марта 2017 г.
    / api / reservations? Filter [where] [startDate] [gte] = 2017-03-22
  • Показать только названия кемпингов:
    / api / campgrounds? Filter [fields] [name] = true
  • Показать все, кроме названий кемпингов:
    / api / campgrounds? Filter [fields] [name]
  • Покажите кемпинги и укажите их бронирования:
    / api / campgrounds? Filter [include] [reservations]
  • Показать первые 2 кемпинга:
    / api / campgrounds? Filter [limit] = 2
  • Показать следующие 2 кемпинга:
    / api / campgrounds? Filter [skip] = 2 & filter [limit] = 2
  • Упорядочить кемпинги по названию
    / api / campgrounds? Filter [order] = name
  • Кемпинги в порядке убывания по названию:
    / api / campgrounds? Filter [order] = name% 20DESC

Геолокация

Вы также можете делать запросы на основе геолокации. Расположение нашего кемпинга - геоточка.
Представьте, что мы в Национальном парке Арки и хотим найти все кемпинги в радиусе 200 миль:

/api/campgrounds?filter[where][location][near]=38.7006538,-109.5643742&filter[where][location][maxDistance]=200

Проверка

Когда мы создавали модель палаточного городка, мы сделали название обязательным. Если мы попытаемся создать палаточный лагерь без имени, loopback выдаст нам ошибку проверки, говорящую, что поле не может быть пустым.

Loopback также имеет несколько встроенных методов проверки для часто используемой проверки. В названии нашего кемпинга должно быть макс. 100 символов. Мы можем реализовать это, добавив следующий код в server / models / campground.js:

'use strict';

module.exports = function(Campground) {
 Campground.validatesLengthOf('name', {max: 100, message: {max: 'Name is too long'}});
};

Вы также можете добавить настраиваемую проверку. Для резервирования endDate должен быть после startDate:

'use strict';

module.exports = function(Reservation) {
  Reservation.validate('startDate', dateValidator, {message: 'endDate should be after startDate'});
    function dateValidator(err) {
      if(this.startDate >= this.endDate) {
        err();
      }
    }
};

Безопасность

Все наши конечные точки являются общедоступными. Здесь мы должны добавить немного безопасности. В нашем примере будет 3 типа пользователей. Анонимные пользователи должны иметь возможность видеть все кемпинги. Когда они зарегистрируются, они станут клиентами, которые смогут делать бронирования и видеть только свои собственные бронирования.

Администраторы должны иметь возможность видеть и делать все, что угодно. Loopback имеет
встроенные модели для обеспечения безопасности, но не рекомендуется использовать их напрямую.

Мы создадим модель клиента, расширяющую модель пользователя. фунтовая модель:

? Enter the model name: customer
? Select the data-source to attach customer to: reservationDS (memory)
? Select model's base class User
? Expose customer via the REST API? Yes
? Custom plural form (used to build REST URL): 
? Common model or server only? server
Let's add some customer properties now.

Enter an empty property name when done.
? Property name: name
   invoke   loopback:property
? Property type: string
? Required? No
? Default value[leave blank for none]: 

Let's add another customer property.
Enter an empty property name when done.
? Property name: 

У клиентов может быть ноль или более бронирований. Давайте создадим здесь отношение, lb Relations:

? Select the model to create the relationship from: customer
? Relation type: has many
? Choose a model to create a relationship with: reservation
? Enter the property name for the relation: reservations
? Optionally enter a custom foreign key: 
? Require a through model? No

Поскольку мы начали с пустого сервера, встроенные модели не определены в server / model-config.json. Давайте добавим их сейчас.

Установка для свойства 'public' значения false означает, что они не будут общедоступными и не будут отображаться в нашем проводнике API.

{
  "_meta": {
    "sources": [
      "loopback/common/models",
      "loopback/server/models",
      "../common/models",
      "./models"
    ],
    "mixins": [
      "loopback/common/mixins",
      "loopback/server/mixins",
      "../common/mixins",
      "./mixins"
    ]
  },
  "campground": {
    "dataSource": "reservationDS",
    "public": true
  },
  "reservation": {
    "dataSource": "reservationDS",
    "public": true
  },
  "customer": {
    "dataSource": "reservationDS",
    "public": true
  },
  "User": {
    "dataSource": "reservationDS",
    "public": false
  },
  "AccessToken": {
    "dataSource": "reservationDS",
    "public": false
  },
  "ACL": {
    "dataSource": "reservationDS",
    "public": false
  },
  "RoleMapping": {
    "dataSource": "reservationDS",
    "public": false
  },
  "Role": {
    "dataSource": "reservationDS",
    "public": false
  }
}

Теперь мы добавим в приложение 3-х пользователей:

  • Энди, наш администратор: (имя пользователя: энди, пароль: энди)
  • Кеннет, клиент: (имя пользователя: kenneth, пароль: kenneth)
  • Claudiu, другой клиент: (имя пользователя: claudiu, пароль: claudiu)

Пароли необходимо хешировать в файле db.json. Мы также связываем наши бронирования с нашими клиентами.

{
  "ids": {
    "campground": 5,
    "reservation": 3,
    "customer": 4,
    "AccessToken": 1,
    "ACL": 1,
    "RoleMapping": 1,
    "Role": 1
  },
  "models": {
    "campground": {
      "1": "{\"name\":\"Salt Lake City KOA\",\"location\":{\"lat\": 40.772112, \"lng\": -111.932165},\"id\":1}",
      "2": "{\"name\":\"Gouldings Campground\",\"location\":{\"lat\": 37.006989, \"lng\": -110.214907},\"id\":2}",
      "3": "{\"name\":\"Grand Canyon Mather Campground\",\"location\":{\"lat\": 36.056472, \"lng\": -112.140728},\"id\":3}",
      "4": "{\"name\":\"Camping Paris Bois de Boulogne\",\"location\":{\"lat\": 48.868879, \"lng\": 2.234914},\"id\":4}"
    },
    "reservation": {
      "1": "{\"startDate\":\"2017-03-21\",\"endDate\":\"2017-03-23\",\"campgroundId\":1,\"customerId\":2,\"id\":1}",
      "2": "{\"startDate\":\"2017-03-25\",\"endDate\":\"2017-03-31\",\"campgroundId\":2,\"customerId\":3,\"id\":2}"
    },
    "customer": {
      "1": "{\"name\":\"Andy Van Den Heuvel\",\"username\":\"andy\",\"password\":\"$2a$10$1lmPRI0Xjd5fU8HGdPmDoOkZpIPJj2axcdJYIfc/3RUnBDDqQe31K\",\"email\":\"[email protected]\",\"id\":1}",
      "2": "{\"name\":\"Kenneth Van den Berghe\",\"username\":\"kenneth\",\"password\":\"$2a$10$H5wtnFvhxf8CPn66gEbPu.tki2WRpkplqvUV3yhQ049ugY8rHFSJi\",\"email\":\"[email protected]\",\"id\":2}",
      "3": "{\"name\":\"Claudiu Matei\",\"username\":\"claudiu\",\"password\":\"$2a$10$6b9jxIwb6y84gpq.ZU57YegRM4BWxHoXc.K/WwlEOJTa/9fO7cCta\",\"email\":\"[email protected]\",\"id\":3}"
    },
    "AccessToken": {},
    "ACL": {},
    "RoleMapping": {
      "1": "{\"principalType\":\"USER\",\"principalId\":\"1\",\"roleId\":1,\"id\":1}"
    },
    "Role": {
      "1": "{\"name\":\"admin\",\"created\":\"2017-02-21T06:07:25.571Z\",\"modified\":\"2017-02-21T06:07:25.571Z\",\"id\":1}"
    }
  }
}

Теперь мы активируем нашу аутентификацию. Для этого мы должны добавить
bootscript. Создайте новый файловый сервер / boot / authentication.js со следующим содержимым и после этого перезапустите сервер.

'use strict';

module.exports = function enableAuthentication(server) {
  server.enableAuth();
};

Аутентификация теперь включена, но все конечные точки по-прежнему общедоступны, потому что мы не настроили авторизацию. Loopback использует для этого списки контроля доступа. Давайте добавим здесь несколько правил.

Сначала запретите доступ всем, lb acl:

? Select the model to apply the ACL entry to: (all existing models)
? 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

Теперь разрешите всем просматривать кемпинги, фунт акл:

? Select the model to apply the ACL entry to: campground
? 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

Кроме того, разрешите каждому клиенту использовать его собственную информацию, lb acl:

? Select the model to apply the ACL entry to: customer
? Select the ACL scope: All methods and properties
? Select the access type: All (match all types)
? Select the role The user owning the object
? Select the permission to apply Explicitly grant access

И, наконец, разрешите администраторам делать и видеть все, lb acl:

? Select the model to apply the ACL entry to: (all existing models)
? Select the ACL scope: All methods and properties
? Select the access type: All (match all types)
? Select the role other
? Enter the role name: admin
? Select the permission to apply Explicitly grant access

Запустите свой сервер и перейдите по адресу http: // localhost: 3000 / api / campgrounds. Как анонимный пользователь я все еще могу видеть все кемпинги. Когда я перехожу на http: // localhost: 3000 / api / reservations, я получаю 401 Требуется авторизация.

Теперь мы войдем в систему как Кеннет, чтобы увидеть его оговорки. Перейдите по адресу http: // localhost: 3000 / explorer / #! / Customer / customer_login и войдите в систему:

Идентификатор ответа - это сгенерированный токен доступа. Теперь вы можете скопировать его и установить в заголовке, чтобы предоставить токен доступа для всех вызовов в API Explorer.

Или вы можете добавить токен доступа в качестве параметра запроса. Давай попробуем это.

  • Как Кеннет, я могу видеть свои собственные бронирования: / api / customers / 2 / reservations? Access_token = XMFN5GsykpxFokvWsXRYtKZidlJYKyClvak0KmEn87LisnFYSQ9TzmrBcz9GFrHv
  • Запрос на резервирование Клауди приводит к 401 неавторизованному запросу: / api / customer / 3 / reservations? Access_token = XMFN5GsykpxFokvWsXRYtKZidlJYKyClvak0KmEn87LisnFYSQ9TzmrBcz9GFrHv
  • Как Клаудиу, я могу видеть свое бронирование: / api / customers / 3 / reservations? Access_token = v51y2iZa1nkKTWC7s1yKELaIatfDJPVxcEEVa6FFIG4llZCGyZVbwR4plhfpYAxx
  • Но я не вижу оговорок Кеннета: / api / customers / 2 / reservations? Access_token = v51y2iZa1nkKTWC7s1yKELaIatfDJPVxcEEVa6FFIG4llZCGyZVbwR4plhfpYAxx
  • Как Энди, я могу сделать оговорки Кеннета: / api / customers / 2 / reservations? Access_token = okxVkWcdoVzWb3WmCK9KkiuBArz1HOOHrIn1h2mOfa0kBzeUna1V9wFmFRe6BCHe
  • А также оговорки Клауди: / api / customers / 3 / reservations? Access_token = okxVkWcdoVzWb3WmCK9KkiuBArz1HOOHrIn1h2mOfa0kBzeUna1V9wFmFRe6BCHe
  • Как Энди, вы также можете видеть все бронирования: / api / reservations? Access_token = okxVkWcdoVzWb3WmCK9KkiuBArz1HOOHrIn1h2mOfa0kBzeUna1V9wFmFRe6BCHe

Логика приложения

До этого момента мы могли создать полностью функционирующий REST API, в основном путем генерации кода. Но более продвинутым приложениям почти всегда потребуется дополнительная логика приложения.

Loopback позволяет добавлять удаленные методы, удаленные перехватчики и перехватчики операций. Дистанционные и рабочие крючки практически не отличаются. Разница в том, что рабочие перехватчики определены более высокого уровня, тогда как удаленные перехватчики более специфичны для конечной точки.

В нашем примере мы хотим отправить электронное письмо с подтверждением, когда клиент сделал заказ. Loopback имеет коннектор электронной почты, доступный на основе Nodemailer.

Перейдите на server / datasources.json и добавьте конфигурацию электронной почты. Я использовал для этого Gmail:

{
  "reservationDS": {
    "name": "reservationDS",
    "localStorage": "",
    "file": "db.json",
    "connector": "memory"
  },
  "emailDS": {
    "name": "mail",
    "connector": "mail",
    "transports": [{
      "type": "SMTP",
      "host": "smtp.gmail.com",
      "secure": true,
      "port": 465,
      "auth": {
        "user": "YOUR_USER",
        "pass": "YOUR_PASSWORD"
      }
    }]
  }
}

Теперь привязываем источник данных в server / models-config.js:

{
  "_meta": {
    "sources": [
      "loopback/common/models",
      "loopback/server/models",
      "../common/models",
      "./models"
    ],
    "mixins": [
      "loopback/common/mixins",
      "loopback/server/mixins",
      "../common/mixins",
      "./mixins"
    ]
  },
  "campground": {
    "dataSource": "reservationDS",
    "public": true
  },
  "reservation": {
    "dataSource": "reservationDS",
    "public": true
  },
  "customer": {
    "dataSource": "reservationDS",
    "public": true
  },
  "User": {
    "dataSource": "reservationDS",
    "public": false
  },
  "AccessToken": {
    "dataSource": "reservationDS",
    "public": false
  },
  "ACL": {
    "dataSource": "reservationDS",
    "public": false
  },
  "RoleMapping": {
    "dataSource": "reservationDS",
    "public": false
  },
  "Role": {
    "dataSource": "reservationDS",
    "public": false
  },
  "Email": {
    "dataSource": "emailDS"
  }
}

Перейдите на server / models / reservation.js и добавьте логику для отправки и отправки по электронной почте после сохранения:

'use strict';

module.exports = function (Reservation) {
  Reservation.validate('startDate', dateValidator, {message: 'endDate should be after startDate'});
  function dateValidator(err) {
    if (this.startDate >= this.endDate) {
      err();
    }
  }

  Reservation.observe("after save", function (ctx, next) {   Reservation.app.models.Campground.findById(ctx.instance.campgroundId, function (err, campground) {
      Reservation.app.models.Email.send({
        to: '[email protected]',
        from: '[email protected]',
        subject: 'Thank you for your reservation at ' + campground.name,
        html: '<p>We confirm your reservation for <strong>' + campground.name + '</strong></p>'
      }, function (err, mail) {
        console.log('email sent!');
      });
    });
    next();
  });

};

Место хранения

Если вы заинтересованы в загрузке / скачивании файлов из вашего API, вы можете использовать Компонент хранилища Loopback. Для этого вам нужно установить компонент хранилища через NPM:

npm install loopback-component-storage --save

Как и в случае с электронной почтой, компонент хранилища называется источником данных. Давайте добавим его в server / datasources.json.

{
  "reservationDS": {
    "name": "reservationDS",
    "localStorage": "",
    "file": "db.json",
    "connector": "memory"
  },
  "emailDS": {
    "name": "mail",
    "connector": "mail",
    "transports": [{
      "type": "SMTP",
      "host": "smtp.gmail.com",
      "secure": true,
      "port": 465,
      "auth": {
        "user": "YOUR_USER",
        "pass": "YOUR_PASSWORD"
      }
    }]
  },
  "photos": {
    "name": "photos",
    "connector": "loopback-component-storage",
    "provider": "filesystem",
    "root": "./server/files"
  }
}

Здесь мы используем нашу локальную файловую систему в качестве провайдера, но компонент хранилища использует pkgcloud для поддержки нескольких облачных провайдеров (Amazon, Azure, Google, HP, Openstack, Rackspace).

Loopback хранит файлы в контейнерах. Нам нужно создать модель контейнера, чтобы мы могли создать контейнер для наших фотографий, lb model:

? Enter the model name: container
? Select the data-source to attach container to: photos (loopback-component-storage)
? Select model's base class Model
? Expose container via the REST API? Yes
? Custom plural form (used to build REST URL): 
? Common model or server only? server
Let's add some container properties now.

Enter an empty property name when done.
? Property name:

Создайте папку server / files / photos, чтобы мы могли загрузить в нее несколько фотографий и запустить ваш сервер. Когда вы перейдете в проводник API, вы увидите конечную точку / container, но мы еще не создали ни одного контейнера. В этой части мы будем использовать завиток.

Создайте контейнер «фотографии»:

curl -X GET --header 'Accept: application/json' 'http://localhost:3000/api/containers/photos'

Загрузите в контейнер «фотографии»:

curl -F "[email protected]" http://localhost:3000/api/containers/photos/upload

Загрузите из контейнера «фотографии»:

http://localhost:3000/api/containers/photos/download/image.jpg

Тестирование

Мы почти готовы, но хотим убедиться, что можем протестировать наши конечные точки. Для этого мы будем использовать мокко, чай и чай-http.

Если у вас на машине нет мокко. Установить глобально через npm

npm install -g mocha

Затем установите chai и chai-http для проекта:

npm install chai chai-http --save-dev

Теперь мы можем написать тест в test / campground.js. Я написал здесь не полный набор тестов, а всего несколько тестов, чтобы вы поняли. chai позаботится о том, чтобы сервер был запущен до того, как мы сделаем запрос, и остановится после теста.

'use strict';

var chai = require('chai');
var chaiHttp = require('chai-http');
var server = require('../server/server');
var should = chai.should();

chai.use(chaiHttp);

describe('Campgrounds', function() {
  it('should show all campgrounds on GET /api/campgrounds', function(done) {
    chai.request(server)
      .get('/api/campgrounds')
      .end(function(err, res) {
        res.should.have.status(200);
        res.body.should.have.lengthOf(4);
        done();
      });
  });

  it('should show only the names of the campgrounds on GET /api/campgrounds?filter[fields][name]=true', function(done) {
    chai.request(server)
      .get('/api/campgrounds?filter[fields][name]=true')
      .end(function(err, res) {
        res.should.have.status(200);
        res.body[0].should.have.property('name');
        res.body[0].should.not.have.property('id');
        done();
      });
  });

  it('should show the first 2 campgrounds on GET /api/campgrounds?filter[limit]=2', function(done) {
    chai.request(server)
      .get('/api/campgrounds?filter[limit]=2')
      .end(function(err, res) {
        res.should.have.status(200);
        res.body.should.have.lengthOf(2);
        res.body[0].name.should.equal('Salt Lake City KOA');
        res.body[1].name.should.equal('Gouldings Campground');
        done();
      });
  });

  it('should show the last 2 campgrounds on GET /api/campgrounds?filter[skip]=2&filter[limit]=2', function(done) {
    chai.request(server)
      .get('/api/campgrounds?filter[skip]=2&filter[limit]=2')
      .end(function(err, res) {
        res.should.have.status(200);
        res.body.should.have.lengthOf(2);
        res.body[0].name.should.equal('Grand Canyon Mather Campground');
        res.body[1].name.should.equal('Camping Paris Bois de Boulogne');
        done();
      });
  });
});

Запустите команду mocha сейчас, и вы увидите, что все тесты проходят успешно:

Хорошо, мы почти закончили. Последняя остановка: развертывание в производстве.

Развертывание

Чтобы перейти в производство, нам нужна некоторая конфигурация для конкретной среды.

Loopback имеет для этого соглашения об именах. По соображениям безопасности мы не хотим показывать обозреватель API при работе в производственной среде. Создайте файл с именем /server/component-config.prod.json

{
  "loopback-component-explorer": null
}

Мы также не хотим работать с базой данных в памяти. Здесь мы перейдем на mongoDB. Для этого нам потребуется установить MongoDB Connector:

npm install loopback-connector-mongodb --save

Я буду использовать MongoDB, размещенную на MLab. Вы также можете зарегистрироваться и создать бесплатную песочницу.

  • Создайте базу данных «бронирования»
  • Учетные данные имени пользователя, которые вы использовали для регистрации в MLab, не используются для подключения в вашем приложении, убедитесь, что вы создали пользователя базы данных здесь:

Создайте новый файл с именем server / datasources.prod.json и добавьте свои настройки mongodb:

{
  "reservationDS": {
    "host": "YOUR_HOST",
    "port": 0,
    "url":  false,
    "database": "reservations",
    "name": "reservations",
    "connector": "mongodb",
    "user": "YOUR_USERNAME",
    "password": "YOUR_PASSWORD"
  }
}

Обратите внимание, что мы используем шаблон именования component-config. env .json и datasources. env .json. Loopback использует NODE_ENV, чтобы решить, какую конфигурацию следует загрузить. Давайте изменим нашу среду на прод.

export NODE_ENV="prod"

Loopback теперь будет использовать нашу новую конфигурацию. Наш проводник отключен, и мы все еще можем использовать api, на этот раз через MongoDB:

http://localhost:3000/api/campgrounds

Поскольку сейчас мы работаем над mongoDB, у нас больше нет доступных тестовых данных. Давайте создадим коллекцию «палаточный лагерь», и, щелкнув коллекцию, вы сможете добавить новый документ.

Все это можно сделать в веб-интерфейсе mLab. Например:

{
    "name": "Salt Lake City KOA",
    "location":{
        "lat": 40.772112, 
        "lng": -111.932165
    }
}

Когда вы создали документ и перешли по адресу http: // localhost: 3000 / api / campgrounds, вы должны увидеть только что созданный.

Исходный код

Https://github.com/optis/loopback-rest-api

Ресурсы

Http://loopback.io
https://github.com/strongloop/loopback-example-access-control