Pull to refresh

Comments 63

Очень полезная статья, но возник вопрос, почему вы не использовали Python, PyMongo, Django и Django REST Framework? В своем проекте хочу использовать данные инструменты, и интересно было бы услышать мнение по поводу их.
Думаю автор, имея знания в JS, потратил меньше времени на изучение и реализацию. Ну и видимо ему было интересно решить эту задачу своими силами, раз он не использовал Parse и ему подобные решения
gradmax тут абсолютно прав, с JS на стороне клиента я немного знаком, c Python никогда не сталкивался. Node.js меня заинтересовала, как относительно новая, интересная и нестандартная технология для подобных задач. Так что критиковать Python&Django не могу, можете лишь рассмотреть мой проект и решить, в чем его плюсы и минусы относительно вашего подхода.
UFO just landed and posted this here
отличный пост, только смущает комментарий в примере

app.use(express.static(path.join(__dirname, "public"))); // отдаем статический index.html из папки public/

это не просто отдаёт статический index.html, а запускает статический файловый сервер, который смотрит на папку public/
может я и придираюсь, просто тех, кто только начал изучение Express, это может ввести в заблуждение. Мне так кажется во всяком случае
все совершенно верно, поправил комментарий.
Хорошая статья!

Возможно глупый вопрос, но все же: что произойдет, если в обработчике попробовать поделить на 0?
В JavaScript деление на ноль не является ошибкой. Результатом будет Infinity.
Человек, скорее всего, хотел спросить, рухнет ли все при исключении, например при вызове несуществующего метода или ошибке доступа к базе. В моем примере на Imperess (ниже) при этом один из процессов вылетит, случится запись в лог, запустится новый процесс.
Спасибо, именно это я и имел ввиду.
В статье выделена обработка ошибок. Если вкратце, то все необработанные ошибки Express направит в наш обработчик:
app.use(function(err, req, res, next){
    res.status(err.status || 500);
    log.error('Internal error(%d): %s',res.statusCode,err.message);
    res.send({ error: err.message });
    return;
});


А вот и пример вызова ошибки. Так же можно выполнить throw new Error('my silly error');, все прекрасно обработается, сервер не упадет.
app.get('/ErrorExample', function(req, res, next){
    next(new Error('Random error!'));
});
Поясните пожалуйста, зачем в функции обработок ошибок передаётся параметр next, если он не используется в теле функций?
Если я не ошибаюсь, то express по кол-ву аргументов определяет что это функция обработки ошибок. Было бы три аргумента у функции, то это был бы обычный middleware со следующими параметрами: req, res, next
А уж использовать next внутри функции, или нет, это на усмотрение разработчика.
А уж использовать next внутри функции, или нет, это на усмотрение разработчика.

А IDE ругается — unused parameter next
и приходится ставить костыли а-ля
if (next) {}

чтобы было хоть какое-то упоминание функции, хех.
Это какая-то неправильная IDE :)
Она выводит warning или error? Подобный warning я бы потерпел ради чистоты кода.
Не могу себе представить более правильную IDE, нежели Webstorm :)
Отмечает как warning. И вот кроме конкретно этого случая — хендлинга ошибок экспресса — подобные варнинги очень информативны и полезны.
А если использовать ещё и встроенные JSLint и JSHint…
WebStorm — отличная IDE, и подобное предупреждение мне значительно не мешает :) Повторюсь:
Подобный warning я бы потерпел ради чистоты кода.
Что же это за чистота кода, если для решения задачи требуется ввод неиспользуемой переменной?
Однообразные стандартные вызовы middleware — правильнее и удобнее, чем лаконичный, но нечитаемый код.
И я чаще встречаю проекты с проблемами в коде намного серьезней, чем неиспользуемые в некоторых случаях переменные в функции.
Сложно сказать, что здесь будет уместнее. Речь о том, что подсветка warning в данном случае указывает на плохой дизайн вышеуказанной библиотеки (я не говорю «в целом», но конкретно в этом месте).
Я пока не исследовал, как в Экспрессе будет работать большое API, насаженное на по цепочке, через next и с большим кол-вом обработчиков. Мне стало стремно такое делать в Импрессе и я решил строить дерево наследования (на каждом узле хеш-таблица), и привязать его к дереву файловой системы, поэтому, для поиска обработчика урла в таком дереве достаточно 3-4 поиска по хешу, и на каждом уровне не так много ключей же будет. Нужно будет сделать сравнительные тесты, а то я даже не представляю, признаюсь честно, сделали ли мы оптимальнее или хуже.
Это формат функции middleware в Express.js. Middleware — функция-обработчик в Express, которая выполняет операцию и передает request следующему middleware по цепочке. Например, 1 звено — контроль доступа, 2 звено — отдает нужные данные.
Обычное middleware принимает function(req, res, next), где req — запрос, res — ответ, next — вызов следующего middleware. Например, 1 звено проверяет токен и если он валидный — вызывает next, 2 звено отдает данные, больше ничего не требуется делать, ответ отправлен, вызывать next не требуется.

Простейшее middleware c next()
app.use(function(req, res, next){
  // do smth with req & res
  next();
});


Пример middleware: обработчик ошибки 404 — вызывается, когда нет другого подходящего middleware
app.use(function(req, res, next){
    res.status(404);
    log.debug('Not found URL: %s',req.url);
    res.send({ error: 'Not found' });
    return;
});


При возникновении ошибки Express сразу передает ее в специальное middleware с 4 аргументами — добавляется error.
Все хорошо сделано, но очень большая трудоемкость, на Impress, чтобы сделать новый обработчик API урла, нужно всего-то:
1. Создаем папку /api/myAPI/getSomething.json/
2. Кладем туда файл post.js и в нем пишем:
module.exports = function(req, res, callback) {
	db.impress.collectionName.find({ fieldName: req.post.fieldValue }).toArray(function(err, nodes) {
		res.context.data = nodes;
		callback();
	});
}

3. В каталоге /api/myAPI делаем файл access.js и в нем пишем:
module.exports = {
	guests: false, logged: true, http: true, https: true
}

Все готово, для https просто в config.js настройки ставим и в access.js запрещаем http для этой папки. Более того, создавать обработчики можно безе перезапуска сервера, просто создаем еще папку и пишем там код в файле. При первом обращении код попадает в память, при изменении файла на винте код подгружается новый в память и там сидит и ждет вызова.
Интересно выглядит, изучу на досуге.
По поводу трудоемкости Express — согласен. Но во время поиска альтернатив я наткнулся на несколько упоминаний, что самый популярный фреймворк для быстрого развертывания RESTful API на Node — Restify — нестабилен под нагрузкой (один из примеров). Так что я решил использовать Express, о котором только положительные отзывы, чтоб не столкнуться с подобными неведомыми проблемами.
Уже три статьи о нем на Хабре написал, но это уже не прототип, а оттестированный вполне готовый для использования инструмент. На нем уже десяток проектов работает под нагрузками.

А про возможности по разработке API вот только готовлю статью. Если что, обращайтесь, это разработка моей компании, все оупенсорс, лицензия RUMI. Постепенно и документацию делаем, а примеров полно, хорошими примерами может служить админка к базам данных, выложенная в исходниках вместе с Impress в одном пакете.
А чем Restify для создания REST API server превосходит express.js?

Беглый взгяд в API показал, что все выглядит практически идентично…
В Express.js имеется излишняя функциональность, предназначенная для браузерных приложений. Цель создания Restify — избавиться от излишнего функционала и сосредоточиться на одной задаче. Но, как можно увидеть, Express не так уж и плох, а альтернативы, подобные Restify не всегда справляются со своей единственной задачей.
Или можно просто не подключать лишние middleware в express.js
Очень удобно использовать app.param. Плюс бонус, один хендлер вместо двух на обновление и добавление статьи.

var underscore = require('underscore');
app.param(":articleId", function(req, res, next, articleId){
    ArticleModel.findById(articleId, function(err, article){
        if(err || !article) {
            return next('route');
        }
        req.dbArticle = article;
        next();
    });
});

app.post("/api/articles/:articleId?", function(req, res, next){
    if(!req.dbArticle) {
        req.dbArticle = new ArticleModel();
    }
    req.dbArticle.set(underscore.pick(req.body, 'title', 'description', 'author', 'images'));
    req.dbArticle.save(function(err, article){
        // ..............
    })
});
Еще скажу про сам принцип REST API. Дело в том, что он пришел в ноду из каменного века тяжеловесных веб серверов (как Apache и IIS), которые каждый раз запускают внешние (по отношению к ним) приложения, передавая им запросы HTTP протокола через CGI. Такое приложение порождает новый процесс, он должен провести инициализацию рабочей среды, т.е. установить соединения с базой, развернуть все свои данные, прочитать себе из файловой системы что-то (если нужно) и т.д. и все это лишь для того, чтобы через несколько мил миллисекунд завершить работу и освободить память, отключиться от базы. До веба я писал на языках, в которых принято STATEful API, такого типа, как RPC (COM, DCOM, Corba...), и для меня REST в вебе всегда представлялся маразмом. И вот, наконец, после перехода в вероисповедание ноды, мне было счастье. Теперь опять можно разворачивать в памяти данные и они ни куда не деваются от запроса к запросу, можно хранить увесистые сессии в оперативной памяти и не делать сериализацию/десериализацию оных при завершении и повторном запуске процессов. И мне было видение, что REST ушел в прошлое вместе с ублюдочными заплатками типа viewstate и серверами состояний. Понял я, что STATEful API есть величайшее благо, дарованное Всевышним каждому живому существу, познавшему ноду.
Как «разворачивать в памяти данные и они ни куда не деваются от запроса к запросу» противоречит принципам REST?
REST — есть род болезненного отказа от хранения состояния объектов во всех компонентах системы, кроме постоянного хранилища (БД, диска). Первейший симптом REST, это STATEless, когда между парой запрос/ответ ни на сервере, ни на клиенте, не сохраняется состояние объекта. В противоположность RPC, на котором основаны клиент-серверные приложения с толстыми клиентами или STATEful серверами приложений, в которых принято создавать модель в клиенте и создавать модель в сервере, связывая их интерфейсы по сети и транслируя между этими моделями события и вызовы. Вот нода позволяет развернуть модель на двух концах провода и синхронизировать через AJAX вызовы, что конечно более удобно для прикладных приложений.
Масштабируемость будет страдать. REST пришел тогда, когда потребовалось десятки и сотни миллионов пользователей обслуживать.
Так решалась проблема масштабируемости в каменном веке, теперь не нужно отказываться от состояния STATEful возможностей, ради высоких нагрузок, а можно использовать асинхронный ввод/вывод, балансировщики в ip-sticky и cookie-sticky режимах, межпроцессовое взаимодействие через ØMQ, много оперативной памяти, и это работает гораздо эффективнее.
Со statefull опасно довольно. Особенно для мобильных приложений с падучим интернетом.
Вот держите вы открытое подключение, на сервере стейт. Человек заехал в туннель в метро, коннект отвалился — стейт подвис, клиент будет переподключаться и получается тот же REST практически.
Да и выкатывать новую версию ПО снова проблема. Вот висит у вас 1000 подключенных клиентов, вам нужно апдейт выкатить. Получается всех клиентов придётся отрубать (в Erlang это более-менее ещё можно обойти). То же самое, но в меньшей степени, можно сказать про вылеты серверов. Сервер упал-стейт потерял.

Да и REST на месте не стоит. Есть keep-alive, большинство современных серверов приложений умеют держать подключения к БД, кешам и т.п. открытыми, некоторые ещё и часть данных хранят или просто какие то объекты/настройки и т.п.

Но, если у вас какой-то другой опыт, то будет интересно, если поделитесь.
Поддерживаю. Только что сбежали с socket.io в сторону REST API.
Но у нас еще и наложилось, что socket.io нивкакую не хотел скейлится на несколько серверов.
Скейлиться совершенно не сложно, для взаимодействия между серверами в рамках одной машины, можно использовать IPC, а если машин несколько, то есть ZeroMQ и его аналоги, которые решают проблему обмена сообщениями полностью. Но обмен сообщениями нужен только если нужно наладить взаимодействие между пользователями или разными клиентами. Если такой задачи не стоит, то можно реализовать «IP sticky» (приклеивание по IP) или «cookie sticky» (приклеивание по кукизу). И то и другое для ноды — не проблема, есть кучу реализаций, что позволяет все соединения с одного IP или c одним и тем же кукизом при разрыве и восстановлении опять направлять в тот же процесс, от которого они отвалились и который хранит их сессию.
Решение есть и это не мое изобретение, это известные все вещи. В нестабильном интернете с разрывом соединений нет ни какой проблемы, такие случаи нужно и можно правильно обрабатывать:
1. Не нужно присоединять состояние к открытому сокету, а нужно разделить состояние на три типа: состояние сессии, состояние пользователя и состояние объекта (с которым работает пользователь). Таким образом, одна сессия может иметь множество не закрытых (подвисших) соединенией, которые через какой-то таймаут умрут, но у них всех есть общее состояние сессии, обращение к которому происходит по идентификатору сессии (например, кукизу или другому выдаваемому ключу). Состояние пользователя общее для всех его сесиий, а состояние информационного объекта общее для всех пользователей, которые с ним работают.
2. Правильно настраивать таймауты, обработку ошибок и выделение/освобождение памяти. Для ноды, в которой память выделяется и освобождается не явно, нужно просто заботиться, чтобы удалялись ссылки на ненужные данные из соответствующих хешей, делать это нужно или по ошибке или по таймауту или по событию отключения (что-то из этого все же случится).
3. Нода достаточно хорошо справляется с большими нагрузками, и много открытых соединений ей не страшно, лишь бы только память выделялась не на соединения, а на сессии: Миллион одновременных соединений на Node.js

Про вылеты серверов: стейт нужно сохранять в базу, просто сохранение это не все же время происходит, а «ленивым» способом, отложенным, что не мешает обработке запросов. Сохранение происходит только при изменении стейта. Если упал сервер, то из базы подымается все, проблем нет.

Про обновление кода, все решено уже в Impress, можно изменять код без разрушения структуры данных, без перестартовывания ноды. А если в момент изменения обрабатывались запросы старой версией кода, то некоторое время в памяти хранятся 2 версии кода, потом старая удаляется.

Какие еще проблемы Вы видете в STATEful серверах?
Насчёт обновления кода сомневаюсь что всё так гладко, но не очень хочу эту тему развивать (типа трансформация структур данных состояния под работу с новой версией кода).

Ещё — в stateless системах, говорят, проще делать балансировку. Один запрос пришел на один сервер, второй на совсем другой. В statefull нужно следить, чтобы юзер был приклеен к одному серверу (хотя при постоянных подключениях это несколько легче по понятным причинам).

Ну и сложность системы повышается (разделение состояния на 3 типа, приклеивание юзеров к серверам).

Ну и главное — не понимаю в чём собственно профит? В том, что не нужно делать сериализацию / десериализацию состояния? Так вроде решили что это опасно в случае вылета железа/софта.

У меня на statefull работает хобби-проект сервис временной почты на Erlang и я уже временами сомневаюсь нужен ли мне этот геморрой, или стоит перейти на тупой long-polling. И это при том, что в Erlang проблема обновления кода под живыми пользователями решена лучше, чем у кого либо.
Трансформация структур данные — это уже бред, если стурктура данных меняется, то нужно менять не только код, но и сбрасывать состояние. Но тут ничего страшного, один разок система поработает медленно, додтянет данные, как это делает STATEless при каждом запросе.

На вопрос приклеивания я уже ответил тут.

Профит: скорость разворачивания среды и подгрузка данных. На сохранении данных особо не сэкономишь, разве что, можно сохранять данные уже после того, как запрос обработан и ответ клиенту отослан. То есть, делать это в отложенном режиме. По опыту могу сказать, что максимальное использование памяти — это конечно сложнее, но скорость возрастает так же круто, как и от применения неблокирующей обработки. То есть, возможность хранить состояние считаю одним из ключевых преимуществ ноды. В Impress мы храним в памяти не только состояние, но и всю статику (картинки, css, js, шаблоны), да еще в кеш сразу преобразуем в минифицированный вид и храним сразу сжатое gzip, что при отдаче только буфер нужный подставляем.
У меня вопросы по методам апи.

Перове есть список элементов со свойством, которое зависит от других елементов.
Допустим есть список вероятностных событий и мы им можем менять вероятность их наступления, но общая их сумма должна быть 100%.
Таким образом на клиенте мы меняем минимум 2 элемента, максимум — все.
Как сохранять изменения?

Второе мы создаем новую сущность пост запросом тру вей не возвращать сущность в запоросе. Значит нам надо делать всегда гет запрос после создания?
Таким образом на клиенте мы меняем минимум 2 элемента, максимум — все.
Как сохранять изменения?

Если я правильно понял вопрос — предлагаю присваивать все доступные в запросе параметры в схему mongoose, а затем вызвать метод схемы, который расставит по особому правилу все остальные, незаполненные атрибуты. А затем уже можно сохранить.
В примере по полю password заполняются сразу хэш и соль, но это не метод схемы, а виртуальное поле.

мы создаем новую сущность пост запросом тру вей не возвращать сущность в запоросе

Почему так не следует делать? Я в примере возвращаю созданную сущность. В принципе можно только id новой записи возвращать, а в случае необходимости — делать по данному id GET запрос.
Если я правильно понял вопрос

Не совсем. Меняются поля не одной сущности а несколько. Еще раз, есть список с вероятностными событиями у них по 2 поля имя и %. Сумма всех процентов должна быть 100%, таким образом меняя % у одной сущности мы по какому-то алгоритму меняем у других, что б сумма была 100. В итоге получаем список, у которого у всез лементов изменился параметр и его надо созранить.

list of events:
event1:
name:«событие 1»
percent: 20

event2:
name:«событие 2»
percent: 30

event3:
name:«событие 3»
percent: 40

event4:
name:«событие 4»
percent: 10

Я в примере возвращаю созданную сущность
Каноничный REST на пост должен вернуть 201 и идентификатор новой записи (ссылку) по кторой клиент гетом уже забирает. Многие клиентские либы на пост сами разгребают запрос и гетом забирают данные об объекте, прозрачно для юезра, но 2 запроса это 2 запроса

Карма не дает оформить коммент(
1. Если это разные сущности — в чем проблема после изменения n сущностей (с помощью, например, bulk data update через PUT) найти в базе все неизмененные, отредактировать по нужному алгоритму и сохранить?
P.S. Изменение/удаление/создание сразу нескольких сущностей сделать несложно, но в моем примере не отражено. Пример можно найти в этой статье (тоже Express & Mongoose) под заголовком Bulk Actions for UPDATE and DELETE.

2. Возвращать ли созданную сущность? — я предпочитаю возвращать сразу, если это не займет много траффика.
А зачем возвращать вновь созданную сущность? Ведь клиент только что передал все данные, которые были необходимы для создания сущности, значит, у клиента они и так есть. Разве что вернуть айдишник, как выше отметил BenderRodriguez. Или если сервер дозаполняет некоторые важные поля сущности, тогда можно рассмотреть — либо вернуть только их (для уменьшения трафика), либо всё-таки возвращать всю сущность (для удобства).
Спасибо за статью, очень интересно, но не хватает такой информации либо отсылок к конкретным статьям:
* «хэши, алгоритмы и сол»;
* какая роль у коллекций: Client, User, Token, RefreshToken;
* что именно реализовано и зачем в «5. Access control» — т.к. какие модули использованы и так видно из require(xxx);
Спасибо за рекомендации. Я добавил краткое описание коллекций и описания некоторых шагов в «5. Access control».
Хэши, алгоритмы и соли — это чрезвычайно обширная и спорная тема. Советую всем интересующимся внимательно ознакомится с данной областью на специализированных ресурсах. Ну или в предметных топиках, здесь же.
Кстати, в процессе изучения этих вопросов открыл для себя security.stackexchange.com/ — там есть дискуссии с описаниями, плюсами и минусами различных алгоритмов.
Вот этот кусок кода у самого автора oauth2orize есть:
server.serializeClient(function(client, done) { return done(null, client.id); }); server.deserializeClient(function(id, done) { db.clients.find(id, function(err, client) { if (err) { return done(err); } return done(null, client); }); });

а у вас его нету

можете объяснить почему?
Этот код используется для сохранения в сессии объекта «клиент». Сессия используется для хранения транзакции в процессе аутентификации, состоящего из нескольких http-запросов. В примере из статьи сессии не используются, авторизация происходит только через username-password flow, в 1 запрос.
Спасибо. Разобрался. Интересная статья! Вы OAuth2 используете для доступа к данным из своих собственных мобильных приложений или открываете доступ к вашим данным через api для других пользователей?
Использую для собственных мобильных приложений, но OAuth2 был выбран для упрощения интеграции с другими сервисами, стандартизации и избегания самописных реализаций.
Спасибо автору за статью. Прочитал и погонял код, я понял, что застрял в другом стеке технологий. :)
Предлагаю небольшую модификацию кода для его работы на компьютере разработчика и в средах AppFog и OpenShift:
libs/config.js (целиком)
var nconf = require('nconf');

var env = 'development',
    defaults = {
        'http': {
            'host': '127.0.0.1',
            'port': 3000
        },
        'mongo': {
            'hostname': 'localhost',
            'host': '127.0.0.1',
            'port': '27017',
            'username': '',
            'password': '',
            'db': 'test',
            'uri': 'mongodb://localhost:27017/'
        }
    }
;

if (process.env.VCAP_SERVICES) { // AppFog

    env = 'appfog';
    defaults.mongo = JSON.parse(process.env.VCAP_SERVICES)['mongodb-1.8'][0]['credentials'];
    defaults.mongo.uri = 'mongodb://' + defaults.mongo.username + ':' + defaults.mongo.password + '@' + defaults.mongo.hostname + ':' + defaults.mongo.port + '/';
    defaults.http.host = process.env.VCAP_APP_HOST;
    defaults.http.port = parseInt(process.env.VCAP_APP_PORT)

} else

if (process.env.OPENSHIFT_APP_NAME) { // OpenShift

    env = 'openshift';
    defaults.mongo = {
        'host': process.env.OPENSHIFT_MONGODB_DB_HOST,
        'port': process.env.OPENSHIFT_MONGODB_DB_PORT,
        'username': process.env.OPENSHIFT_MONGODB_DB_USERNAME,
        'password': process.env.OPENSHIFT_MONGODB_DB_PASSWORD,
        'db': process.env.OPENSHIFT_APP_NAME,
        'uri': process.env.OPENSHIFT_MONGODB_DB_URL
    };
    defaults.http.host = process.env.OPENSHIFT_NODEJS_IP || '127.0.0.1';
    defaults.http.port = parseInt(process.env.OPENSHIFT_NODEJS_PORT) || 8080
}

nconf.argv()
    .env()
    .file({ file: './' + env + '.json' })
    .defaults(defaults)
;

module.exports = nconf;


libs/mongoose.js (connection)
var uri = config.get('mongo:uri') + config.get('mongo:db');
mongoose.connect(uri);


server.js (listen)
var port = config.get('http:port'),
    ipAddr = config.get('http:host')
;
app.listen(port, ipAddr, function(){
        log.info('Node server started on %s:%d ...', ipAddr, port);
});


В конфигурационных файлах development.json, appfog.json и openshift.json соответственно устанавливаем специфичные значения, например:
{
    "mongo": {
        "db": "anotherTest"
    }
}

Ещё раз: спасибо! Долго думал, как реализовать коллаборативную работу с древовидным документом с версионностью в стеке php+orm… Серия статей, появившихся в последнее время на Хабре, решили вопрос. STATEfull парадигма node.js (+express+...) + MongoDB (+mondoose+plugins) рулят :)
Да, ещё добавочка:
libs/mongoose.js
// validation
Article.path('title').validate(function (v) {
    if ('undefined' == typeof v) { // чтобы node.js не "падала" при пустом теле запроса :)
        return false
    }
    return v.length > 5 && v.length < 70
});
Я бы сделал так:

// validation
Article.path('title').validate(function (v) {
    return !!v && v.length > 5 && v.length < 70;
});
Да, конечно, так элегантней. Правда, я озадачен тем, что процесс вообще доходит до валидации полей при пустом теле post-запроса.
Столкнулся с проблемой mongoose — если написать middleware, который получает модель, делает populate ее полей (у меня оба таких поля массивы суб-моделей) и сохраняет ее в req.model, то req.model.arr[subdocument].save() перестает работать. Возвращает сохраненную модель, но не записывает ее в mongo.
Извините за глупый вопрос, но авторизация у меня не получается: в oauth.js базовая стратегия обмена юзернейма и пароля на токен, вот только поиск идёт по клиенту, у которого нет пароля:

...
passport.use(new BasicStrategy(
    function(username, password, done) {
        ClientModel.findOne({ clientId: username }
...


Кроме того, в коде, предоставленном в статье, нигде не создаётся и не записывается ClientModel.
Буду рад, если кто-то объяснит.
Я так понимаю, что для обычной авторизации username/password всё равно нужен пакет passport-local, правильно?
Поиск идет по клиенту, у которого есть client_secret. Клиент вполне создается и сохраняется в dataGen.js. 2 стратегии я использую, потому что в oauth2orize example они так используются:

ClientPasswordStrategy
http POST http://localhost:1337/oauth/token grant_type=password client_id=mobileV1 client_secret=abc123456 username=andrey password=simplepassword

BasicStrategy
http -a mobileV1:abc123456 POST http://localhost:1337/oauth/token grant_type=password username=andrey password=simplepassword

Для простой авторизации, без использования OAuth — нужно применять passport-local.
Спасибо за ответ.

Уже разобрался.
Sign up to leave a comment.

Articles