{"id":193458,"date":"2013-09-12T11:50:02","date_gmt":"2013-09-12T07:50:02","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=193458"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=193458","title":{"rendered":"<span class=\"post_title\">RESTful API \u043d\u0430 Node.js + MongoDB<\/span>"},"content":{"rendered":"<div class=\"content html_format\"> \t\t\t\u042f, \u0431\u0443\u0434\u0443\u0447\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u043c \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u0447\u0430\u0441\u0442\u043e \u043d\u0443\u0436\u0434\u0430\u044e\u0441\u044c \u0432 backend-\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043f\u0440\u043e\u0447\u0435\u0433\u043e. \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0434\u043b\u044f \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c BaaS (Parse, Backendless, etc\u2026). \u041d\u043e \u0441\u0432\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u2014 \u044d\u0442\u043e \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u043e\u043b\u0435\u0435 \u0443\u0434\u043e\u0431\u043d\u043e \u0438 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u043d\u043e.<\/p>\n<p>  \u0418 \u044f \u0432\u0441\u0435 \u0436\u0435 \u0440\u0435\u0448\u0438\u043b \u0438\u0437\u0443\u0447\u0438\u0442\u044c \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0435 \u0434\u043b\u044f \u043c\u0435\u043d\u044f \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0435\u0439\u0447\u0430\u0441 \u0432\u0435\u0441\u044c\u043c\u0430 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b \u0438 \u043f\u043e\u0437\u0438\u0446\u0438\u043e\u043d\u0438\u0440\u0443\u044e\u0442\u0441\u044f, \u043a\u0430\u043a \u043b\u0435\u0433\u043a\u043e \u043e\u0441\u0432\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043d\u043e\u0432\u0438\u0447\u043a\u0430\u043c\u0438 \u0438 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u044e\u0449\u0438\u0435 \u0433\u043b\u0443\u0431\u043e\u043a\u0438\u0445 \u0437\u043d\u0430\u043d\u0438\u0439 \u0438 \u043e\u043f\u044b\u0442\u0430 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432. \u0412\u043e\u0442 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u0432\u043c\u0435\u0441\u0442\u0435, \u043c\u043e\u0436\u0435\u0442 \u043b\u0438 \u043d\u0435\u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0441\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b\u0439 \u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u0431\u044d\u043a\u0435\u043d\u0434.<\/p>\n<p>  \u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043e \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 REST API \u0434\u043b\u044f \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 Node.js \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 Express.js \u0438 \u043c\u043e\u0434\u0443\u043b\u044f Mongoose.js \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 MongoDB. \u0414\u043b\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043f\u0440\u0438\u0431\u0435\u0433\u043d\u0435\u043c \u043a \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438 OAuth 2.0 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0434\u0443\u043b\u0435\u0439 OAuth2orize \u0438 Passport.js.<\/p>\n<p>  \u041f\u0438\u0448\u0443 \u0441 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e\u0433\u043e \u043d\u043e\u0432\u0438\u0447\u043a\u0430. \u0420\u0430\u0434 \u043b\u044e\u0431\u044b\u043c \u043e\u0442\u0437\u044b\u0432\u0430\u043c \u0438 \u043f\u043e\u043f\u0440\u0430\u0432\u043a\u0430\u043c \u043f\u043e \u043a\u043e\u0434\u0443 \u0438 \u043b\u043e\u0433\u0438\u043a\u0435!<\/p>\n<h5>\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435<\/h5>\n<p>  <\/p>\n<ol>\n<li><a href=\"#p1\">Node.js + Express.js, \u043f\u0440\u043e\u0441\u0442\u043e\u0439 web-\u0441\u0435\u0440\u0432\u0435\u0440<\/a><\/li>\n<li><a href=\"#p2\">Error handling<\/a><\/li>\n<li><a href=\"#p3\">RESTful API endpoints, CRUD<\/a><\/li>\n<li><a href=\"#p4\">MongoDB &#038; Mongoose.js<\/a><\/li>\n<li><a href=\"#p5\">Access control \u2014 OAuth 2.0, Passport.js<\/a><\/li>\n<\/ol>\n<p>  <a name=\"habracut\"><\/a><br \/>  \u0420\u0430\u0431\u043e\u0442\u0430\u044e \u044f \u0432 OSX, IDE \u2014 <a href=\"http:\/\/www.jetbrains.com\/webstorm\/\">JetBrains WebStorm<\/a>.<\/p>\n<p>  \u041e\u0441\u043d\u043e\u0432\u044b Node.js \u043f\u043e\u0447\u0435\u0440\u043f\u043d\u0443\u043b \u0432 <a href=\"http:\/\/learn.javascript.ru\/nodejs-screencast\">\u0441\u043a\u0440\u0438\u043d\u043a\u0430\u0441\u0442\u0430\u0445 \u0418\u043b\u044c\u0438 \u041a\u0430\u043d\u0442\u043e\u0440\u0430<\/a>, \u043a\u0440\u0430\u0439\u043d\u0435 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e! (<a href=\"http:\/\/habrahabr.ru\/post\/193158\/\">\u0410 \u0432\u043e\u0442 \u0438 \u043f\u043e\u0441\u0442 \u043f\u0440\u043e \u043d\u0438\u0445 \u043d\u0430 \u0445\u0430\u0431\u0440\u0435<\/a>)<\/p>\n<p>  \u0413\u043e\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0441\u0442\u0430\u0434\u0438\u0438 \u043c\u043e\u0436\u043d\u043e \u0432\u0437\u044f\u0442\u044c \u043d\u0430 <a href=\"https:\/\/github.com\/ealeksandrov\/NodeAPI\">GitHub<\/a>. \u0414\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0432\u0441\u0435\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 npm install \u0432 \u043f\u0430\u043f\u043a\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<p>  <a name=\"p1\"><\/a><\/p>\n<h4>1. Node.js + Express.js, \u043f\u0440\u043e\u0441\u0442\u043e\u0439 web-\u0441\u0435\u0440\u0432\u0435\u0440<\/h4>\n<p>  Node.js \u043e\u0431\u043b\u0430\u0434\u0430\u0435\u0442 \u043d\u0435\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0449\u0438\u043c \u0432\u0432\u043e\u0434\u043e\u043c-\u0432\u044b\u0432\u043e\u0434\u043e\u043c, \u044d\u0442\u043e \u043a\u0440\u0443\u0442\u043e \u0434\u043b\u044f API, \u043a \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432. Express.js \u2014 \u0440\u0430\u0437\u0432\u0438\u0442\u044b\u0439, \u043b\u0435\u0433\u043a\u043e\u0432\u0435\u0441\u043d\u044b\u0439 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u0439 \u0431\u044b\u0441\u0442\u0440\u043e \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u0443\u0442\u0438 (API endpoints), \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c. \u0422\u0430\u043a \u0436\u0435 \u043a \u043d\u0435\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439.<\/p>\n<p>  \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u0444\u0430\u0439\u043b\u043e\u043c server.js. \u0422\u0430\u043a \u043a\u0430\u043a \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u0446\u0435\u043b\u0438\u043a\u043e\u043c \u043f\u043e\u043b\u0430\u0433\u0430\u0442\u044c\u0441\u044f \u043d\u0430 <a href=\"http:\/\/expressjs.com\/\">Express.js<\/a>, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0435\u0433\u043e. \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0447\u0435\u0440\u0435\u0437 Node Package Manager \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u044b <code>npm install modulename<\/code> \u0432 \u043f\u0430\u043f\u043a\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<pre><code class=\"bash\">cd NodeAPI npm i express <\/code><\/pre>\n<p>  Express \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0432 \u043f\u0430\u043f\u043a\u0443 node_modules. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u0435\u0433\u043e \u043a \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e:<\/p>\n<pre><code class=\"javascript\">var express         = require('express'); var app = express();  app.listen(1337, function(){     console.log('Express server listening on port 1337'); }); <\/code><\/pre>\n<p>  \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 IDE \u0438\u043b\u0438 \u043a\u043e\u043d\u0441\u043e\u043b\u044c (<code>node server.js<\/code>). \u0414\u0430\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430 localhost:1337. \u0415\u0441\u043b\u0438 \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u2014 \u043e\u043d \u0432\u044b\u0432\u0435\u0434\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 <code>Cannot GET \/<\/code>. \u042d\u0442\u043e \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043c\u044b \u0435\u0449\u0435 \u043d\u0435 \u0437\u0430\u0434\u0430\u043b\u0438 \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0443\u0442\u0438 (route). \u0414\u0430\u043b\u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0443\u0442\u0435\u0439 \u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043c \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Express.<\/p>\n<pre><code class=\"javascript\">var express         = require('express'); var path            = require('path'); \/\/ \u043c\u043e\u0434\u0443\u043b\u044c \u0434\u043b\u044f \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430 \u043f\u0443\u0442\u0438 var app = express();  app.use(express.favicon()); \/\/ \u043e\u0442\u0434\u0430\u0435\u043c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0443\u044e \u0444\u0430\u0432\u0438\u043a\u043e\u043d\u043a\u0443, \u043c\u043e\u0436\u0435\u043c \u0437\u0434\u0435\u0441\u044c \u0436\u0435 \u0441\u0432\u043e\u044e \u0437\u0430\u0434\u0430\u0442\u044c app.use(express.logger('dev')); \/\/ \u0432\u044b\u0432\u043e\u0434\u0438\u043c \u0432\u0441\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0441\u043e \u0441\u0442\u0430\u0442\u0443\u0441\u0430\u043c\u0438 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c app.use(express.bodyParser()); \/\/ \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c, \u0434\u043b\u044f \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430 JSON \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445 app.use(express.methodOverride()); \/\/ \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 put \u0438 delete app.use(app.router); \/\/ \u043c\u043e\u0434\u0443\u043b\u044c \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0437\u0430\u0434\u0430\u043d\u0438\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u043f\u0443\u0442\u0435\u0439 app.use(express.static(path.join(__dirname, &quot;public&quot;))); \/\/ \u043e\u0442\u0434\u0430\u0435\u043c \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 index.html \u0438\u0437 \u043f\u0430\u043f\u043a\u0438 public\/  app.get('\/api', function (req, res) {     res.send('API is running'); });  app.listen(1337, function(){     console.log('Express server listening on port 1337'); }); <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c localhost:1337\/api \u0432\u0435\u0440\u043d\u0435\u0442 \u043d\u0430\u0448\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435. localhost:1337 \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442 index.html.<\/p>\n<p>  \u0422\u0443\u0442 \u043c\u044b \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043e\u0448\u0438\u0431\u043e\u043a.<\/p>\n<p>  <a name=\"p2\"><\/a><\/p>\n<h4>2. Error handling<\/h4>\n<p>  \u0421\u043f\u0435\u0440\u0432\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u0434\u043b\u044f \u043b\u043e\u0433\u0433\u0438\u043d\u0433\u0430 <a href=\"https:\/\/github.com\/flatiron\/winston\">Winston<\/a>. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u044b \u0435\u0433\u043e \u0431\u0443\u0434\u0435\u043c \u0447\u0435\u0440\u0435\u0437 \u0441\u0432\u043e\u044e \u043e\u0431\u0435\u0440\u0442\u043a\u0443. \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0432 \u043a\u043e\u0440\u043d\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 <code>npm i winston<\/code>, \u0437\u0430\u0442\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0430\u043f\u043a\u0443 libs\/ \u0438 \u0442\u0430\u043c \u0444\u0430\u0439\u043b log.js.<\/p>\n<pre><code class=\"javascript\">var winston = require('winston');  function getLogger(module) {     var path = module.filename.split('\/').slice(-2).join('\/'); \/\/\u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u043c \u043c\u0435\u0442\u043a\u0443 \u0441 \u0438\u043c\u0435\u043d\u0435\u043c \u0444\u0430\u0439\u043b\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435      return new winston.Logger({         transports : [             new winston.transports.Console({                 colorize:   true,                 level:      'debug',                 label:      path             })         ]     }); }  module.exports = getLogger; <\/code><\/pre>\n<p>  \u041c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 1 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442 \u0434\u043b\u044f \u043b\u043e\u0433\u043e\u0432 \u2014 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c. \u0422\u0430\u043a \u0436\u0435 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u043b\u0438 \u0444\u0430\u0439\u043b. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u043b\u043e\u0433\u0433\u0435\u0440 \u0432 \u043d\u0430\u0448 server.js.<\/p>\n<pre><code class=\"javascript\">var express         = require('express'); var path            = require('path'); \/\/ \u043c\u043e\u0434\u0443\u043b\u044c \u0434\u043b\u044f \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430 \u043f\u0443\u0442\u0438 var log             = require('.\/libs\/log')(module); var app = express();  app.use(express.favicon()); \/\/ \u043e\u0442\u0434\u0430\u0435\u043c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0443\u044e \u0444\u0430\u0432\u0438\u043a\u043e\u043d\u043a\u0443, \u043c\u043e\u0436\u0435\u043c \u0437\u0434\u0435\u0441\u044c \u0436\u0435 \u0441\u0432\u043e\u044e \u0437\u0430\u0434\u0430\u0442\u044c app.use(express.logger('dev')); \/\/ \u0432\u044b\u0432\u043e\u0434\u0438\u043c \u0432\u0441\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0441\u043e \u0441\u0442\u0430\u0442\u0443\u0441\u0430\u043c\u0438 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c app.use(express.bodyParser()); \/\/ \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c, \u0434\u043b\u044f \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430 JSON \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445 app.use(express.methodOverride()); \/\/ \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 put \u0438 delete app.use(app.router); \/\/ \u043c\u043e\u0434\u0443\u043b\u044c \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0437\u0430\u0434\u0430\u043d\u0438\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u043f\u0443\u0442\u0435\u0439 app.use(express.static(path.join(__dirname, &quot;public&quot;))); \/\/ \u043e\u0442\u0434\u0430\u0435\u043c \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 index.html \u0438\u0437 \u043f\u0430\u043f\u043a\u0438 public\/  app.get('\/api', function (req, res) {     res.send('API is running'); });  app.listen(1337, function(){     log.info('Express server listening on port 1337'); }); <\/code><\/pre>\n<p>  \u041d\u0430\u0448\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0442\u0435\u043f\u0435\u0440\u044c \u043a\u0440\u0430\u0441\u0438\u0432\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043e\u0448\u0438\u0431\u043e\u043a 404 \u0438 500.<\/p>\n<pre><code class=\"javascript\">app.use(function(req, res, next){     res.status(404);     log.debug('Not found URL: %s',req.url);     res.send({ error: 'Not found' });     return; });  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; });  app.get('\/ErrorExample', function(req, res, next){     next(new Error('Random error!')); }); <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c, \u0435\u0441\u043b\u0438 \u043d\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u043f\u0443\u0442\u0435\u0439, Express \u0432\u0435\u0440\u043d\u0435\u0442 \u043d\u0430\u0448\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435. \u041f\u0440\u0438 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u0439 \u043e\u0448\u0438\u0431\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u0430\u043a \u0436\u0435 \u043d\u0430\u0448 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a, \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u043e\u0431\u0440\u0430\u0442\u0438\u0432\u0448\u0438\u0441\u044c \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 localhost:1337\/ErrorExample.<\/p>\n<p>  <a name=\"p3\"><\/a><\/p>\n<h4>3. RESTful API endpoints, CRUD<\/h4>\n<p>  \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u0443\u0442\u0438 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043d\u0435\u043a\u0438\u0445 \u00ab\u0441\u0442\u0430\u0442\u0435\u0439\u00bb(articles). \u041d\u0430 \u0445\u0430\u0431\u0440\u0435 \u0435\u0441\u0442\u044c \u043f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u0430\u044f <a href=\"http:\/\/habrahabr.ru\/post\/181988\/\">\u0441\u0442\u0430\u0442\u044c\u044f<\/a>, \u043e\u0431\u044a\u044f\u0441\u043d\u044f\u044e\u0449\u0430\u044f, \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0443\u0434\u043e\u0431\u043d\u043e\u0435 API. \u041b\u043e\u0433\u0438\u043a\u043e\u0439 \u0438\u0445 \u043d\u0430\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043f\u043e\u043a\u0430 \u043d\u0435 \u0431\u0443\u0434\u0435\u043c, \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u044d\u0442\u043e \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u0448\u0430\u0433\u0435, \u0441 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<pre><code class=\"javascript\">app.get('\/api\/articles', function(req, res) {     res.send('This is not implemented now'); });  app.post('\/api\/articles', function(req, res) {     res.send('This is not implemented now'); });  app.get('\/api\/articles\/:id', function(req, res) {     res.send('This is not implemented now'); });  app.put('\/api\/articles\/:id', function (req, res){     res.send('This is not implemented now');     });  app.delete('\/api\/articles\/:id', function (req, res){     res.send('This is not implemented now'); }); <\/code><\/pre>\n<p>  \u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f post\/put\/delete \u043f\u043e\u0441\u043e\u0432\u0435\u0442\u0443\u044e \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u043e\u0431\u0435\u0440\u0442\u043a\u0443 \u043d\u0430\u0434 cURL \u2014 <a href=\"https:\/\/github.com\/jkbr\/httpie\">httpie<\/a>. \u0414\u0430\u043b\u0435\u0435 \u044f \u0431\u0443\u0434\u0443 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0438\u043c\u0435\u043d\u043d\u043e \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u044d\u0442\u043e\u0433\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430.<\/p>\n<p>  <a name=\"p4\"><\/a><\/p>\n<h4>4. MongoDB &#038; Mongoose.js<\/h4>\n<p>  \u0412\u044b\u0431\u0438\u0440\u0430\u044f \u0421\u0423\u0411\u0414, \u044f \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e\u0432\u0430\u043b\u0441\u044f \u043e\u043f\u044f\u0442\u044c-\u0442\u0430\u043a\u0438 \u0441\u0442\u0440\u0435\u043c\u043b\u0435\u043d\u0438\u0435\u043c \u0438\u0437\u0443\u0447\u0438\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u043d\u043e\u0432\u043e\u0435. <a href=\"http:\/\/www.mongodb.org\/\">MongoDB<\/a> \u2014 \u0441\u0430\u043c\u0430\u044f \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u0430\u044f NoSQL \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442-\u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0421\u0423\u0411\u0414. <a href=\"http:\/\/mongoosejs.com\/\">Mongoose.js<\/a> \u2014 \u043e\u0431\u0435\u0440\u0442\u043a\u0430, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0430\u044f \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0443\u0434\u043e\u0431\u043d\u044b\u0435 \u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u0445\u0435\u043c\u044b \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432.<\/p>\n<p>  \u0421\u043a\u0430\u0447\u0438\u0432\u0430\u0435\u043c \u0438 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c <a href=\"http:\/\/www.mongodb.org\/downloads\">MongoDB<\/a>. \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c mongoose: <code>npm i mongoose<\/code>. \u0420\u0430\u0431\u043e\u0442\u0443 \u0441 \u0431\u0434 \u044f \u0432\u044b\u0434\u0435\u043b\u0438\u043b \u0432 \u0444\u0430\u0439\u043b libs\/mongoose.js.<\/p>\n<pre><code class=\"javascript\">var mongoose    = require('mongoose'); var log         = require('.\/log')(module);  mongoose.connect('mongodb:\/\/localhost\/test1'); var db = mongoose.connection;  db.on('error', function (err) {     log.error('connection error:', err.message); }); db.once('open', function callback () {     log.info(&quot;Connected to DB!&quot;); });  var Schema = mongoose.Schema;  \/\/ Schemas var Images = new Schema({     kind: {         type: String,         enum: ['thumbnail', 'detail'],         required: true     },     url: { type: String, required: true } });  var Article = new Schema({     title: { type: String, required: true },     author: { type: String, required: true },     description: { type: String, required: true },     images: [Images],     modified: { type: Date, default: Date.now } });  \/\/ validation Article.path('title').validate(function (v) {     return v.length &gt; 5 && v.length &lt; 70; });  var ArticleModel = mongoose.model('Article', Article);  module.exports.ArticleModel = ArticleModel; <\/code><\/pre>\n<p>  \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0444\u0430\u0439\u043b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u0431\u0430\u0437\u0435, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043e\u0431\u044a\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0441\u0445\u0435\u043c\u044b \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432. \u0421\u0442\u0430\u0442\u044c\u0438 \u0431\u0443\u0434\u0443\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a. \u0420\u0430\u0437\u043d\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u044b\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043c\u043e\u0436\u043d\u043e \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0442\u0430\u043a \u0436\u0435 \u0437\u0434\u0435\u0441\u044c.<\/p>\n<p>  \u041d\u0430 \u0434\u0430\u043d\u043d\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043c\u043e\u0434\u0443\u043b\u044c <a href=\"https:\/\/github.com\/flatiron\/nconf\">nconf<\/a> \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043f\u0443\u0442\u0438 \u043a \u0411\u0414 \u0432 \u043d\u0435\u043c. \u0422\u0430\u043a \u0436\u0435 \u0432 \u043a\u043e\u043d\u0444\u0438\u0433 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u043c \u043f\u043e\u0440\u0442, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u0441\u0435\u0440\u0432\u0435\u0440. \u041c\u043e\u0434\u0443\u043b\u044c \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 <code>npm i nconf<\/code>. \u041e\u0431\u0435\u0440\u0442\u043a\u043e\u0439 \u0431\u0443\u0434\u0435\u0442 libs\/config.js<\/p>\n<pre><code class=\"javascript\">var nconf = require('nconf');  nconf.argv()     .env()     .file({ file: '.\/config.json' });  module.exports = nconf; <\/code><\/pre>\n<p>  \u041e\u0442\u0441\u044e\u0434\u0430 \u0441\u043b\u0435\u0434\u0443\u0435\u0442, \u0447\u0442\u043e \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c config.json \u0432 \u043a\u043e\u0440\u043d\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<pre><code class=\"javascript\">{     &quot;port&quot; : 1337,     &quot;mongoose&quot;: {         &quot;uri&quot;: &quot;mongodb:\/\/localhost\/test1&quot;     } } <\/code><\/pre>\n<p>  \u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f mongoose.js (\u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0448\u0430\u043f\u043a\u0435):<\/p>\n<pre><code class=\"javascript\">var config      = require('.\/config');  mongoose.connect(config.get('mongoose:uri')); <\/code><\/pre>\n<p>  \u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f server.js:<\/p>\n<pre><code class=\"javascript\">var config          = require('.\/libs\/config');  app.listen(config.get('port'), function(){     log.info('Express server listening on port ' + config.get('port')); }); <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u043e\u0431\u0430\u0432\u0438\u043c CRUD actions \u0432 \u043d\u0430\u0448\u0438 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u043f\u0443\u0442\u0438.<\/p>\n<pre><code class=\"javascript\">var log             = require('.\/libs\/log')(module); var ArticleModel    = require('.\/libs\/mongoose').ArticleModel;  app.get('\/api\/articles', function(req, res) {     return ArticleModel.find(function (err, articles) {         if (!err) {             return res.send(articles);         } else {             res.statusCode = 500;             log.error('Internal error(%d): %s',res.statusCode,err.message);             return res.send({ error: 'Server error' });         }     }); });  app.post('\/api\/articles', function(req, res) {     var article = new ArticleModel({         title: req.body.title,         author: req.body.author,         description: req.body.description,         images: req.body.images     });      article.save(function (err) {         if (!err) {             log.info(&quot;article created&quot;);             return res.send({ status: 'OK', article:article });         } else {             console.log(err);             if(err.name == 'ValidationError') {                 res.statusCode = 400;                 res.send({ error: 'Validation error' });             } else {                 res.statusCode = 500;                 res.send({ error: 'Server error' });             }             log.error('Internal error(%d): %s',res.statusCode,err.message);         }     }); });  app.get('\/api\/articles\/:id', function(req, res) {     return ArticleModel.findById(req.params.id, function (err, article) {         if(!article) {             res.statusCode = 404;             return res.send({ error: 'Not found' });         }         if (!err) {             return res.send({ status: 'OK', article:article });         } else {             res.statusCode = 500;             log.error('Internal error(%d): %s',res.statusCode,err.message);             return res.send({ error: 'Server error' });         }     }); });  app.put('\/api\/articles\/:id', function (req, res){     return ArticleModel.findById(req.params.id, function (err, article) {         if(!article) {             res.statusCode = 404;             return res.send({ error: 'Not found' });         }          article.title = req.body.title;         article.description = req.body.description;         article.author = req.body.author;         article.images = req.body.images;         return article.save(function (err) {             if (!err) {                 log.info(&quot;article updated&quot;);                 return res.send({ status: 'OK', article:article });             } else {                 if(err.name == 'ValidationError') {                     res.statusCode = 400;                     res.send({ error: 'Validation error' });                 } else {                     res.statusCode = 500;                     res.send({ error: 'Server error' });                 }                 log.error('Internal error(%d): %s',res.statusCode,err.message);             }         });     }); });  app.delete('\/api\/articles\/:id', function (req, res){     return ArticleModel.findById(req.params.id, function (err, article) {         if(!article) {             res.statusCode = 404;             return res.send({ error: 'Not found' });         }         return article.remove(function (err) {             if (!err) {                 log.info(&quot;article removed&quot;);                 return res.send({ status: 'OK' });             } else {                 res.statusCode = 500;                 log.error('Internal error(%d): %s',res.statusCode,err.message);                 return res.send({ error: 'Server error' });             }         });     }); }); <\/code><\/pre>\n<p>  \u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f Mongoose \u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u043c \u0441\u0445\u0435\u043c\u0430\u043c \u2014 \u0432\u0441\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043f\u0440\u0435\u0434\u0435\u043b\u044c\u043d\u043e \u043f\u043e\u043d\u044f\u0442\u043d\u044b. \u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u0440\u043e\u043c\u0435 node.js \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c mongoDB \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 <code>mongod<\/code>. <code>mongo<\/code> \u2014 \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0411\u0414, \u0441\u0430\u043c \u0441\u0435\u0440\u0432\u0438\u0441 \u2014 <code>monod<\/code>. \u0421\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432 \u0431\u0430\u0437\u0435 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043d\u0443\u0436\u043d\u043e.<\/p>\n<p>  \u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e httpie:<\/p>\n<pre><code class=\"javascript\">http POST http:\/\/localhost:1337\/api\/articles title=TestArticle author='John Doe' description='lorem ipsum dolar sit amet' images:='[{&quot;kind&quot;:&quot;thumbnail&quot;, &quot;url&quot;:&quot;http:\/\/habrahabr.ru\/images\/write-topic.png&quot;}, {&quot;kind&quot;:&quot;detail&quot;, &quot;url&quot;:&quot;http:\/\/habrahabr.ru\/images\/write-topic.png&quot;}]'  http http:\/\/localhost:1337\/api\/articles  http http:\/\/localhost:1337\/api\/articles\/52306b6a0df1064e9d000003  http PUT http:\/\/localhost:1337\/api\/articles\/52306b6a0df1064e9d000003 title=TestArticle2 author='John Doe' description='lorem ipsum dolar sit amet' images:='[{&quot;kind&quot;:&quot;thumbnail&quot;, &quot;url&quot;:&quot;http:\/\/habrahabr.ru\/images\/write-topic.png&quot;}, {&quot;kind&quot;:&quot;detail&quot;, &quot;url&quot;:&quot;http:\/\/habrahabr.ru\/images\/write-topic.png&quot;}]'  http DELETE http:\/\/localhost:1337\/api\/articles\/52306b6a0df1064e9d000003 <\/code><\/pre>\n<p>  \u041f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430 \u0434\u0430\u043d\u043d\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u043c\u043e\u0436\u043d\u043e \u0432\u0437\u0433\u043b\u044f\u043d\u0443\u0442\u044c \u043d\u0430 <a href=\"https:\/\/github.com\/ealeksandrov\/NodeAPI\/tree\/e8764a97f9c70fb6eae102fda7237e745d9e99ac\">GitHub<\/a>.<\/p>\n<p>  <a name=\"p5\"><\/a><\/p>\n<h4>5. Access control \u2014 OAuth 2.0, Passport.js<\/h4>\n<p>  \u0414\u043b\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u044f \u043f\u0440\u0438\u0431\u0435\u0433\u043d\u0443 \u043a OAuth 2. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u044d\u0442\u043e \u0438\u0437\u0431\u044b\u0442\u043e\u0447\u043d\u043e, \u043d\u043e \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043e\u0431\u043b\u0435\u0433\u0447\u0430\u0435\u0442 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 OAuth-\u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430\u043c\u0438. \u041a \u0442\u043e\u043c\u0443 \u0436\u0435, \u044f \u043d\u0435 \u043d\u0430\u0448\u0435\u043b \u0440\u0430\u0431\u043e\u0447\u0438\u0445 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 user-password OAuth2 flow \u0434\u043b\u044f Node.js.<br \/>  \u041d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0437\u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0435\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u0435\u0434\u0438\u0442\u044c <a href=\"http:\/\/passportjs.org\/\">Passport.js<\/a>. \u0414\u043b\u044f OAuth2-\u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u0441\u044f \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043e\u0442 \u0442\u043e\u0433\u043e \u0436\u0435 \u0430\u0432\u0442\u043e\u0440\u0430 \u2014 <a href=\"https:\/\/github.com\/jaredhanson\/oauth2orize\">oauth2orize<\/a>. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438, \u0442\u043e\u043a\u0435\u043d\u044b \u0431\u0443\u0434\u0443\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u0432 MongoDB.<br \/>  \u0421\u043f\u0435\u0440\u0432\u0430 \u043d\u0443\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u0441\u0435 \u043c\u043e\u0434\u0443\u043b\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f:  <\/p>\n<ul>\n<li>Faker<\/li>\n<li>oauth2orize<\/li>\n<li>passport<\/li>\n<li>passport-http<\/li>\n<li>passport-http-bearer<\/li>\n<li>passport-oauth2-client-password<\/li>\n<\/ul>\n<p>  \u0417\u0430\u0442\u0435\u043c, \u0432 mongoose.js \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0445\u0435\u043c\u044b \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0442\u043e\u043a\u0435\u043d\u043e\u0432:<\/p>\n<pre><code class=\"javascript\">var crypto      = require('crypto');  \/\/ User var User = new Schema({     username: {         type: String,         unique: true,         required: true     },     hashedPassword: {         type: String,         required: true     },     salt: {         type: String,         required: true     },     created: {         type: Date,         default: Date.now     } });  User.methods.encryptPassword = function(password) {     return crypto.createHmac('sha1', this.salt).update(password).digest('hex');     \/\/more secure - return crypto.pbkdf2Sync(password, this.salt, 10000, 512); };  User.virtual('userId')     .get(function () {         return this.id;     });  User.virtual('password')     .set(function(password) {         this._plainPassword = password;         this.salt = crypto.randomBytes(32).toString('base64');         \/\/more secure - this.salt = crypto.randomBytes(128).toString('base64');         this.hashedPassword = this.encryptPassword(password);     })     .get(function() { return this._plainPassword; });   User.methods.checkPassword = function(password) {     return this.encryptPassword(password) === this.hashedPassword; };  var UserModel = mongoose.model('User', User);  \/\/ Client var Client = new Schema({     name: {         type: String,         unique: true,         required: true     },     clientId: {         type: String,         unique: true,         required: true     },     clientSecret: {         type: String,         required: true     } });  var ClientModel = mongoose.model('Client', Client);  \/\/ AccessToken var AccessToken = new Schema({     userId: {         type: String,         required: true     },     clientId: {         type: String,         required: true     },     token: {         type: String,         unique: true,         required: true     },     created: {         type: Date,         default: Date.now     } });  var AccessTokenModel = mongoose.model('AccessToken', AccessToken);  \/\/ RefreshToken var RefreshToken = new Schema({     userId: {         type: String,         required: true     },     clientId: {         type: String,         required: true     },     token: {         type: String,         unique: true,         required: true     },     created: {         type: Date,         default: Date.now     } });  var RefreshTokenModel = mongoose.model('RefreshToken', RefreshToken);  module.exports.UserModel = UserModel; module.exports.ClientModel = ClientModel; module.exports.AccessTokenModel = AccessTokenModel; module.exports.RefreshTokenModel = RefreshTokenModel; <\/code><\/pre>\n<p>  \u0412\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e password \u2014 \u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u0430\u043a mongoose \u043c\u043e\u0436\u0435\u0442 \u0432 \u043c\u043e\u0434\u0435\u043b\u0438 \u0432\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0443\u0434\u043e\u0431\u043d\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443. \u041f\u0440\u043e \u0445\u044d\u0448\u0438, \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u044b \u0438 \u0441\u043e\u043b\u044c \u2014 \u043d\u0435 \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f, \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u0432\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<p>  \u0412 config.json \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438 \u0442\u043e\u043a\u0435\u043d\u0430:  <\/p>\n<pre><code class=\"javascript\">{     &quot;port&quot; : 1337,     &quot;security&quot;: {         &quot;tokenLife&quot; : 3600     },     &quot;mongoose&quot;: {         &quot;uri&quot;: &quot;mongodb:\/\/localhost\/testAPI&quot;     } } <\/code><\/pre>\n<p>  \u0412\u044b\u0434\u0435\u043b\u0438\u043c \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u043e\u0434\u0443\u043b\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 OAuth2 \u0438 \u043b\u043e\u0433\u0438\u043a\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438. \u0412 oauth.js \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u00ab\u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438\u00bb passport.js, \u043c\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c 3 \u0438\u0437 \u043d\u0438\u0445 \u2014 2 \u043d\u0430 OAuth2 username-password flow, 1 \u043d\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0442\u043e\u043a\u0435\u043d\u0430.<\/p>\n<pre><code class=\"javascript\">var config                  = require('.\/config'); var passport                = require('passport'); var BasicStrategy           = require('passport-http').BasicStrategy; var ClientPasswordStrategy  = require('passport-oauth2-client-password').Strategy; var BearerStrategy          = require('passport-http-bearer').Strategy; var UserModel               = require('.\/mongoose').UserModel; var ClientModel             = require('.\/mongoose').ClientModel; var AccessTokenModel        = require('.\/mongoose').AccessTokenModel; var RefreshTokenModel       = require('.\/mongoose').RefreshTokenModel;  passport.use(new BasicStrategy(     function(username, password, done) {         ClientModel.findOne({ clientId: username }, function(err, client) {             if (err) { return done(err); }             if (!client) { return done(null, false); }             if (client.clientSecret != password) { return done(null, false); }              return done(null, client);         });     } ));  passport.use(new ClientPasswordStrategy(     function(clientId, clientSecret, done) {         ClientModel.findOne({ clientId: clientId }, function(err, client) {             if (err) { return done(err); }             if (!client) { return done(null, false); }             if (client.clientSecret != clientSecret) { return done(null, false); }              return done(null, client);         });     } ));  passport.use(new BearerStrategy(     function(accessToken, done) {         AccessTokenModel.findOne({ token: accessToken }, function(err, token) {             if (err) { return done(err); }             if (!token) { return done(null, false); }              if( Math.round((Date.now()-token.created)\/1000) &gt; config.get('security:tokenLife') ) {                 AccessTokenModel.remove({ token: accessToken }, function (err) {                     if (err) return done(err);                 });                 return done(null, false, { message: 'Token expired' });             }              UserModel.findById(token.userId, function(err, user) {                 if (err) { return done(err); }                 if (!user) { return done(null, false, { message: 'Unknown user' }); }                  var info = { scope: '*' }                 done(null, user, info);             });         });     } )); <\/code><\/pre>\n<p>  \u0417\u0430 \u0432\u044b\u0434\u0430\u0447\u0443 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u0430 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 oauth2.js. \u041e\u0434\u043d\u0430 exchange-\u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044f \u2014 \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u0430 \u043f\u043e username-password flow, \u0435\u0449\u0435 \u043e\u0434\u043d\u0430 \u2014 \u043d\u0430 \u043e\u0431\u043c\u0435\u043d refresh_token.<\/p>\n<pre><code class=\"javascript\">var oauth2orize         = require('oauth2orize'); var passport            = require('passport'); var crypto              = require('crypto'); var config              = require('.\/config'); var UserModel           = require('.\/mongoose').UserModel; var ClientModel         = require('.\/mongoose').ClientModel; var AccessTokenModel    = require('.\/mongoose').AccessTokenModel; var RefreshTokenModel   = require('.\/mongoose').RefreshTokenModel;  \/\/ create OAuth 2.0 server var server = oauth2orize.createServer();  \/\/ Exchange username & password for access token. server.exchange(oauth2orize.exchange.password(function(client, username, password, scope, done) {     UserModel.findOne({ username: username }, function(err, user) {         if (err) { return done(err); }         if (!user) { return done(null, false); }         if (!user.checkPassword(password)) { return done(null, false); }          RefreshTokenModel.remove({ userId: user.userId, clientId: client.clientId }, function (err) {             if (err) return done(err);         });         AccessTokenModel.remove({ userId: user.userId, clientId: client.clientId }, function (err) {             if (err) return done(err);         });          var tokenValue = crypto.randomBytes(32).toString('base64');         var refreshTokenValue = crypto.randomBytes(32).toString('base64');         var token = new AccessTokenModel({ token: tokenValue, clientId: client.clientId, userId: user.userId });         var refreshToken = new RefreshTokenModel({ token: refreshTokenValue, clientId: client.clientId, userId: user.userId });         refreshToken.save(function (err) {             if (err) { return done(err); }         });         var info = { scope: '*' }         token.save(function (err, token) {             if (err) { return done(err); }             done(null, tokenValue, refreshTokenValue, { 'expires_in': config.get('security:tokenLife') });         });     }); }));  \/\/ Exchange refreshToken for access token. server.exchange(oauth2orize.exchange.refreshToken(function(client, refreshToken, scope, done) {     RefreshTokenModel.findOne({ token: refreshToken }, function(err, token) {         if (err) { return done(err); }         if (!token) { return done(null, false); }         if (!token) { return done(null, false); }          UserModel.findById(token.userId, function(err, user) {             if (err) { return done(err); }             if (!user) { return done(null, false); }              RefreshTokenModel.remove({ userId: user.userId, clientId: client.clientId }, function (err) {                 if (err) return done(err);             });             AccessTokenModel.remove({ userId: user.userId, clientId: client.clientId }, function (err) {                 if (err) return done(err);             });              var tokenValue = crypto.randomBytes(32).toString('base64');             var refreshTokenValue = crypto.randomBytes(32).toString('base64');             var token = new AccessTokenModel({ token: tokenValue, clientId: client.clientId, userId: user.userId });             var refreshToken = new RefreshTokenModel({ token: refreshTokenValue, clientId: client.clientId, userId: user.userId });             refreshToken.save(function (err) {                 if (err) { return done(err); }             });             var info = { scope: '*' }             token.save(function (err, token) {                 if (err) { return done(err); }                 done(null, tokenValue, refreshTokenValue, { 'expires_in': config.get('security:tokenLife') });             });         });     }); }));   \/\/ token endpoint exports.token = [     passport.authenticate(['basic', 'oauth2-client-password'], { session: false }),     server.token(),     server.errorHandler() ] <\/code><\/pre>\n<p>  \u0414\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u044d\u0442\u0438\u0445 \u043c\u043e\u0434\u0443\u043b\u0435\u0439, \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 server.js:<\/p>\n<pre><code class=\"javascript\">var oauth2          = require('.\/libs\/oauth2');  app.use(passport.initialize());  require('.\/libs\/auth');  app.post('\/oauth\/token', oauth2.token);  app.get('\/api\/userInfo',     passport.authenticate('bearer', { session: false }),         function(req, res) {             \/\/ req.authInfo is set using the `info` argument supplied by             \/\/ `BearerStrategy`.  It is typically used to indicate scope of the token,             \/\/ and used in access control checks.  For illustrative purposes, this             \/\/ example simply returns the scope in the response.             res.json({ user_id: req.user.userId, name: req.user.username, scope: req.authInfo.scope })         } ); <\/code><\/pre>\n<p>  \u0414\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0437\u0430\u0449\u0438\u0442\u0430 \u0441\u0442\u043e\u0438\u0442 \u043d\u0430 \u0430\u0434\u0440\u0435\u0441\u0435 localhost:1337\/api\/userInfo.<\/p>\n<p>  \u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u2014 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0432 \u0411\u0414. \u041f\u0440\u0438\u0432\u0435\u0434\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 Node.js, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0438 \u0443\u0434\u0430\u043b\u0438\u0442 \u043b\u0438\u0448\u043d\u0438\u0435 \u0438\u0437 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0439. \u041f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0431\u044b\u0441\u0442\u0440\u043e \u043e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0431\u0430\u0437\u0443 \u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u0440\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438, \u0432\u0430\u043c, \u0434\u0443\u043c\u0430\u044e, \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043e\u0434\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \ud83d\ude42<\/p>\n<pre><code class=\"javascript\">var log                 = require('.\/libs\/log')(module); var mongoose            = require('.\/libs\/mongoose').mongoose; var UserModel           = require('.\/libs\/mongoose').UserModel; var ClientModel         = require('.\/libs\/mongoose').ClientModel; var AccessTokenModel    = require('.\/libs\/mongoose').AccessTokenModel; var RefreshTokenModel   = require('.\/libs\/mongoose').RefreshTokenModel; var faker               = require('Faker');  UserModel.remove({}, function(err) {     var user = new UserModel({ username: &quot;andrey&quot;, password: &quot;simplepassword&quot; });     user.save(function(err, user) {         if(err) return log.error(err);         else log.info(&quot;New user - %s:%s&quot;,user.username,user.password);     });      for(i=0; i&lt;4; i++) {         var user = new UserModel({ username: faker.random.first_name().toLowerCase(), password: faker.Lorem.words(1)[0] });         user.save(function(err, user) {             if(err) return log.error(err);             else log.info(&quot;New user - %s:%s&quot;,user.username,user.password);         });     } });  ClientModel.remove({}, function(err) {     var client = new ClientModel({ name: &quot;OurService iOS client v1&quot;, clientId: &quot;mobileV1&quot;, clientSecret:&quot;abc123456&quot; });     client.save(function(err, client) {         if(err) return log.error(err);         else log.info(&quot;New client - %s:%s&quot;,client.clientId,client.clientSecret);     }); }); AccessTokenModel.remove({}, function (err) {     if (err) return log.error(err); }); RefreshTokenModel.remove({}, function (err) {     if (err) return log.error(err); });  setTimeout(function() {     mongoose.disconnect(); }, 3000); <\/code><\/pre>\n<p>  \u0415\u0441\u043b\u0438 \u0432\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u043c, \u0434\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0430\u043c \u0442\u0430\u043a \u0436\u0435 \u043f\u043e\u0434\u043e\u0439\u0434\u0443\u0442. \u041d\u0430\u043f\u043e\u043c\u043d\u044e, \u0447\u0442\u043e \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e <a href=\"https:\/\/github.com\/jkbr\/httpie\">httpie<\/a>.<\/p>\n<pre><code class=\"bash\">http POST http:\/\/localhost:1337\/oauth\/token grant_type=password client_id=mobileV1 client_secret=abc123456 username=andrey password=simplepassword  http POST http:\/\/localhost:1337\/oauth\/token grant_type=refresh_token client_id=mobileV1 client_secret=abc123456 refresh_token=TOKEN  http http:\/\/localhost:1337\/api\/userinfo Authorization:'Bearer TOKEN' <\/code><\/pre>\n<p>  <b>\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435!<\/b> \u041d\u0430 production-\u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 HTTPS, \u044d\u0442\u043e \u043f\u043e\u0434\u0440\u0430\u0437\u0443\u043c\u0435\u0432\u0430\u0435\u0442\u0441\u044f \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0435\u0439 OAuth 2. \u0418 \u043d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u043f\u0440\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0435 \u0445\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0430\u0440\u043e\u043b\u0435\u0439. \u0420\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c https \u043d\u0430 \u0434\u0430\u043d\u043d\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043d\u0435\u0441\u043b\u043e\u0436\u043d\u043e, \u0432 \u0441\u0435\u0442\u0438 \u043c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432.<br \/>  \u041d\u0430\u043f\u043e\u043c\u043d\u044e, \u0447\u0442\u043e \u0432\u0435\u0441\u044c \u043a\u043e\u0434 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442\u0441\u044f \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u043d\u0430 <a href=\"https:\/\/github.com\/ealeksandrov\/NodeAPI\">GitHub<\/a>.<br \/>  \u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c <code>npm install<\/code> \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438, \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c <code>mongod<\/code>, <code>node dataGen.js<\/code> (\u0434\u043e\u0436\u0434\u0430\u0442\u044c\u0441\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f), \u0430 \u0437\u0430\u0442\u0435\u043c <code>node server.js<\/code>.<\/p>\n<p>  \u0415\u0441\u043b\u0438 \u043a\u0430\u043a\u0443\u044e-\u0442\u043e \u0447\u0430\u0441\u0442\u044c \u0441\u0442\u0430\u0442\u044c\u0438 \u0441\u0442\u043e\u0438\u0442 \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0443\u043a\u0430\u0436\u0438\u0442\u0435 \u043d\u0430 \u044d\u0442\u043e \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445. \u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0438 \u0434\u043e\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u043f\u043e \u043c\u0435\u0440\u0435 \u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u044f \u043e\u0442\u0437\u044b\u0432\u043e\u0432.<\/p>\n<p>  \u041f\u043e\u0434\u0432\u043e\u0434\u044f \u0438\u0442\u043e\u0433, \u0445\u043e\u0447\u0443 \u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u0447\u0442\u043e node.js \u2014 \u043a\u043b\u0430\u0441\u0441\u043d\u043e\u0435, \u0443\u0434\u043e\u0431\u043d\u043e\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435. MongoDB \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442-\u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u043c \u2014 \u043e\u0447\u0435\u043d\u044c \u043d\u0435\u043f\u0440\u0438\u0432\u044b\u0447\u043d\u044b\u0439, \u043d\u043e \u043d\u0435\u0441\u043e\u043c\u043d\u0435\u043d\u043d\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442, \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u044f \u0435\u0449\u0435 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b. \u0412\u043e\u043a\u0440\u0443\u0433 Node.js \u2014 \u043e\u0447\u0435\u043d\u044c \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043a\u043e\u043c\u043c\u044c\u044e\u043d\u0438\u0442\u0438, \u0433\u0434\u0435 \u0435\u0441\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e open-source \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043e\u043a. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u043e\u0437\u0434\u0430\u0442\u0435\u043b\u044c oauth2orize \u0438 passport.js \u2014 Jared Hanson \u0441\u0434\u0435\u043b\u0430\u043b \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u043e\u0431\u043b\u0435\u0433\u0447\u0430\u044e\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c. \t\t\t<\/p>\n<div class=\"clear\"><\/div>\n<\/p><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"http:\/\/habrahabr.ru\/post\/193458\/\"> http:\/\/habrahabr.ru\/post\/193458\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"content html_format\"> \t\t\t\u042f, \u0431\u0443\u0434\u0443\u0447\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u043c \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u0447\u0430\u0441\u0442\u043e \u043d\u0443\u0436\u0434\u0430\u044e\u0441\u044c \u0432 backend-\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043f\u0440\u043e\u0447\u0435\u0433\u043e. \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0434\u043b\u044f \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c BaaS (Parse, Backendless, etc\u2026). \u041d\u043e \u0441\u0432\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u2014 \u044d\u0442\u043e \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u043e\u043b\u0435\u0435 \u0443\u0434\u043e\u0431\u043d\u043e \u0438 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u043d\u043e.<\/p>\n<p>  \u0418 \u044f \u0432\u0441\u0435 \u0436\u0435 \u0440\u0435\u0448\u0438\u043b \u0438\u0437\u0443\u0447\u0438\u0442\u044c \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0435 \u0434\u043b\u044f \u043c\u0435\u043d\u044f \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0435\u0439\u0447\u0430\u0441 \u0432\u0435\u0441\u044c\u043c\u0430 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b \u0438 \u043f\u043e\u0437\u0438\u0446\u0438\u043e\u043d\u0438\u0440\u0443\u044e\u0442\u0441\u044f, \u043a\u0430\u043a \u043b\u0435\u0433\u043a\u043e \u043e\u0441\u0432\u0430\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043d\u043e\u0432\u0438\u0447\u043a\u0430\u043c\u0438 \u0438 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u044e\u0449\u0438\u0435 \u0433\u043b\u0443\u0431\u043e\u043a\u0438\u0445 \u0437\u043d\u0430\u043d\u0438\u0439 \u0438 \u043e\u043f\u044b\u0442\u0430 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432. \u0412\u043e\u0442 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u0432\u043c\u0435\u0441\u0442\u0435, \u043c\u043e\u0436\u0435\u0442 \u043b\u0438 \u043d\u0435\u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0441\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b\u0439 \u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u0431\u044d\u043a\u0435\u043d\u0434.<\/p>\n<p>  \u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043e \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 REST API \u0434\u043b\u044f \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 Node.js \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 Express.js \u0438 \u043c\u043e\u0434\u0443\u043b\u044f Mongoose.js \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 MongoDB. \u0414\u043b\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043f\u0440\u0438\u0431\u0435\u0433\u043d\u0435\u043c \u043a \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438 OAuth 2.0 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0434\u0443\u043b\u0435\u0439 OAuth2orize \u0438 Passport.js.<\/p>\n<p>  \u041f\u0438\u0448\u0443 \u0441 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e\u0433\u043e \u043d\u043e\u0432\u0438\u0447\u043a\u0430. \u0420\u0430\u0434 \u043b\u044e\u0431\u044b\u043c \u043e\u0442\u0437\u044b\u0432\u0430\u043c \u0438 \u043f\u043e\u043f\u0440\u0430\u0432\u043a\u0430\u043c \u043f\u043e \u043a\u043e\u0434\u0443 \u0438 \u043b\u043e\u0433\u0438\u043a\u0435!<\/p>\n<h5>\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435<\/h5>\n<p>  <\/p>\n<ol>\n<li><a href=\"#p1\">Node.js + Express.js, \u043f\u0440\u043e\u0441\u0442\u043e\u0439 web-\u0441\u0435\u0440\u0432\u0435\u0440<\/a><\/li>\n<li><a href=\"#p2\">Error handling<\/a><\/li>\n<li><a href=\"#p3\">RESTful API endpoints, CRUD<\/a><\/li>\n<li><a href=\"#p4\">MongoDB &#038; Mongoose.js<\/a><\/li>\n<li><a href=\"#p5\">Access control \u2014 OAuth 2.0, Passport.js<\/a><\/li>\n<\/ol>\n<p>  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-193458","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/193458","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=193458"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/193458\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=193458"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=193458"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=193458"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}