{"id":415958,"date":"2024-06-30T00:40:00","date_gmt":"2024-06-30T00:40:00","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=415958"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=415958","title":{"rendered":"<span>Node.js: \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f API<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/4-\/hn\/8w\/4-hn8wkkaeafijehpfybkgcounq.png\" data-src=\"https:\/\/habrastorage.org\/webt\/4-\/hn\/8w\/4-hn8wkkaeafijehpfybkgcounq.png\"\/>  <\/p>\n<p>  \u041f\u0440\u0438\u0432\u0435\u0442, \u0434\u0440\u0443\u0437\u044c\u044f!<\/p>\n<p>  <\/p>\n<p>\u0412 \u044d\u0442\u043e\u043c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u043c \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0435 \u044f \u0445\u043e\u0447\u0443 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0432\u0430\u043c, \u043a\u0430\u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439, \u043d\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e-\u0442\u0430\u043a\u0438 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f <code>API<\/code>.<\/p>\n<p>  <\/p>\n<p>\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0430\u0434\u043c\u0438\u043d\u043a\u0430 \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 (\u0434\u0430\u043b\u0435\u0435 \u2014 \u043f\u0440\u043e\u0435\u043a\u0442\u044b) \u043f\u0443\u0442\u0435\u043c \u0438\u0445 \u043d\u0430\u0431\u043e\u0440\u0430 (\u0432\u0432\u043e\u0434\u0430) \u0438\u043b\u0438 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\/\u0432\u0441\u0442\u0430\u0432\u043a\u0438, \u043b\u0438\u0431\u043e \u043f\u0443\u0442\u0435\u043c \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u0430;<\/li>\n<li>\u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435;<\/li>\n<li>\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c, \u0447\u0442\u0435\u043d\u0438\u0435 \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0444\u0430\u0439\u043b\u043e\u0432 \u043d\u0430 \u043b\u044e\u0431\u043e\u043c \u0443\u0440\u043e\u0432\u043d\u0435 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438;<\/li>\n<li>\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0439 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u0438 \u0438\u0445 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 \u0430\u0434\u043c\u0438\u043d\u043a\u0435;<\/li>\n<li>\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432;<\/li>\n<li>\u0443\u043d\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 <code>GET<\/code>, <code>POST<\/code>, <code>PUT<\/code> \u0438 <code>DELETE<\/code> \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u043b\u044e\u0431\u043e\u043c\u0443 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u043c\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0443, \u0432\u043a\u043b\u044e\u0447\u0430\u044f <code>GET-\u0437\u0430\u043f\u0440\u043e\u0441\u044b<\/code>, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438 \u0441\u0442\u0440\u043e\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430;<\/li>\n<li>\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0441\u0442\u0440\u043e\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 <code>sort<\/code>, <code>order<\/code>, <code>limit<\/code> \u0438 <code>offset<\/code>;<\/li>\n<li>\u0438 \u043c\u043d\u043e\u0433\u043e\u0435 \u0434\u0440\u0443\u0433\u043e\u0435.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041d\u0430\u0448\u0430 \u0430\u0434\u043c\u0438\u043d\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/z_\/bm\/ge\/z_bmgez08qrefh2udyodneqniqi.png\" data-src=\"https:\/\/habrastorage.org\/webt\/z_\/bm\/ge\/z_bmgez08qrefh2udyodneqniqi.png\"\/>  <\/p>\n<p>  \u0414\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0439 \u0441\u0442\u0438\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f <a href=\"https:\/\/getbootstrap.com\/\"><code>Bootstrap<\/code><\/a>.<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/github.com\/harryheman\/Blog-Posts\/tree\/master\/node-mock-api\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a>.<\/p>\n<p>  <\/p>\n<p>\u0420\u0430\u0437\u0443\u043c\u0435\u0435\u0442\u0441\u044f, \u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u044b \u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c, \u0441\u0440\u0430\u0437\u0443 \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d \u043d\u0435 \u043f\u043e\u0439\u0434\u0435\u0448\u044c, \u043d\u043e \u043f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043e\u0432\u0435\u0441\u0442\u0438 \u0435\u0433\u043e \u0434\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f \u043d\u0435 \u0441\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u0442\u0440\u0443\u0434\u0430.<\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u044b \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f 2 \u0432\u0430\u0436\u043d\u044b\u0445 \u0443\u0441\u043b\u043e\u0432\u0438\u0439:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0444\u043e\u0440\u043c\u0430\u0442 \u0434\u0430\u043d\u043d\u044b\u0445 \u2014 <code>JSON<\/code>;<\/li>\n<li>\u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0444\u043e\u0440\u043c\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u2014 \u043c\u0430\u0441\u0441\u0438\u0432.<\/li>\n<\/ul>\n<p>  <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em>: \u0441\u0442\u0430\u0442\u044c\u044f \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u0430, \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043d\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0449\u0438\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432, \u0445\u043e\u0442\u044f, \u0441\u043c\u0435\u044e \u043d\u0430\u0434\u0435\u044f\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0438 \u043e\u043f\u044b\u0442\u043d\u044b\u0435 \u043d\u0430\u0439\u0434\u0443\u0442 \u0432 \u043d\u0435\u0439 \u0447\u0442\u043e-\u043d\u0438\u0431\u0443\u0434\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435 \u0434\u043b\u044f \u0441\u0435\u0431\u044f.<\/p>\n<p>  <\/p>\n<p>\u0412\u044b \u0433\u043e\u0442\u043e\u0432\u044b? \u0422\u043e\u0433\u0434\u0430 \u0432\u043f\u0435\u0440\u0435\u0434.<\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<h2 id=\"podgotovka-proekta\">\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h2>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u043d\u0435\u0435, \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442 \u0438 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">mkdir mock-api cd !$  yarn init -y # or npm init -y  yarn add express multer nodemon open-cli very-simple-fetch # or npm i ...<\/code><\/pre>\n<p>  <\/p>\n<p>\u0417\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/expressjs.com\/ru\/\"><code>express<\/code><\/a> \u2014 <code>Node.js-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a<\/code> \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/li>\n<li><a href=\"https:\/\/github.com\/expressjs\/multer\/blob\/master\/doc\/README-ru.md\"><code>multer<\/code><\/a> \u2014 \u043e\u0431\u0435\u0440\u0442\u043a\u0430 \u043d\u0430\u0434 <a href=\"https:\/\/www.npmjs.com\/package\/busboy\"><code>busboy<\/code><\/a>, \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 <code>multipart\/form-data<\/code>, \u0447\u0430\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0430\u044f \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0444\u0430\u0439\u043b\u043e\u0432<\/li>\n<li><a href=\"https:\/\/www.npmjs.com\/package\/nodemon\"><code>nodemon<\/code><\/a> \u2014 \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438<\/li>\n<li><a href=\"https:\/\/www.npmjs.com\/package\/open-cli\"><code>open-cli<\/code><\/a> \u2014 \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0432\u043a\u043b\u0430\u0434\u043a\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 \u043f\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c\u0443 \u0430\u0434\u0440\u0435\u0441\u0443<\/li>\n<li><a href=\"https:\/\/www.npmjs.com\/package\/very-simple-fetch\"><code>very-simple-fetch<\/code><\/a> \u2014 \u043e\u0431\u0435\u0440\u0442\u043a\u0430 \u043d\u0430\u0434 <a href=\"https:\/\/developer.mozilla.org\/ru\/docs\/Web\/API\/Fetch_API\"><code>Fetch API<\/code><\/a>, \u0443\u043f\u0440\u043e\u0449\u0430\u044e\u0449\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u043d\u044b\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u043c<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c <code>package.json<\/code>, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0432 \u043d\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0444\u0430\u0439\u043b \u0441\u0435\u0440\u0432\u0435\u0440\u0430 (<code>index.js<\/code>) \u043a\u0430\u043a \u043c\u043e\u0434\u0443\u043b\u044c \u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438:<\/p>\n<p>  <\/p>\n<pre><code class=\"json\">{  \"type\": \"module\",  \"scripts\": {    \"dev\": \"open-cli http:\/\/localhost:5000 &amp;&amp; nodemon index.js\"  } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041a\u043e\u043c\u0430\u043d\u0434\u0430 <code>dev<\/code> \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0432\u043a\u043b\u0430\u0434\u043a\u0443 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <code>http:\/\/localhost:5000<\/code> (\u0430\u0434\u0440\u0435\u0441, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0443\u0449\u0435\u043d \u0441\u0435\u0440\u0432\u0435\u0440) \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043a\u043e\u0434 \u0432 \u0444\u0430\u0439\u043b\u0435 <code>index.js<\/code> (\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438).<\/p>\n<p>  <\/p>\n<p>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439:<\/p>\n<p>  <\/p>\n<ul>\n<li><code>projects<\/code> \u2014 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432<\/li>\n<li><code>public<\/code> \u2014 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f \u0441\u043e \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u043c\u0438 \u0444\u0430\u0439\u043b\u0430\u043c\u0438 \u0434\u043b\u044f \u0430\u0434\u043c\u0438\u043d\u043a\u0438<\/li>\n<li><code>routes<\/code> \u2014 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f \u0434\u043b\u044f \u0440\u043e\u0443\u0442\u043e\u0432<\/li>\n<li><code>index.js<\/code> \u2014 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0444\u0430\u0439\u043b \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/li>\n<li><code>utils.js<\/code> \u2014 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041f\u043e\u0436\u0430\u043b\u0443\u0439, \u043f\u0440\u043e\u0435\u043a\u0442 \u0433\u043e\u0442\u043e\u0432 \u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435. \u041d\u0435 \u0431\u0443\u0434\u0435\u043c \u043e\u0442\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0442\u044c \u043d\u0430 \u0437\u0430\u0432\u0442\u0440\u0430 \u0442\u043e, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e <del>\u043e\u0442\u043b\u043e\u0436\u0438\u0442\u044c \u043d\u0430 \u043f\u043e\u0441\u043b\u0435\u0437\u0430\u0432\u0442\u0440\u0430<\/del> \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u0435\u0439\u0447\u0430\u0441.<\/p>\n<p>  <\/p>\n<h2 id=\"server-marshrutizator-dlya-proektov-i-utility\">\u0421\u0435\u0440\u0432\u0435\u0440, \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u0438 \u0443\u0442\u0438\u043b\u0438\u0442\u044b<\/h2>\n<p>  <\/p>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 <code>index.js<\/code> \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c <code>express<\/code>, \u043f\u043e\u043b\u043d\u044b\u0439 \u043f\u0443\u0442\u044c \u043a \u0442\u0435\u043a\u0443\u0449\u0435\u0439 (\u0440\u0430\u0431\u043e\u0447\u0435\u0439) \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0438 \u0440\u043e\u0443\u0442\u044b \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432;<\/li>\n<li>\u0441\u043e\u0437\u0434\u0430\u0435\u043c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 <code>Express-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f<\/code>;<\/li>\n<li>\u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a\u043e\u0432 (\u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0445 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432): \u0434\u043b\u044f \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0444\u0430\u0439\u043b\u043e\u0432, \u0434\u043b\u044f \u0440\u0430\u0437\u0431\u043e\u0440\u0430 (\u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430) \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 <code>JSON<\/code>, \u0434\u043b\u044f \u0434\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f <code>URL<\/code>;<\/li>\n<li>\u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0440\u043e\u0443\u0442 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0444\u0430\u0439\u043b\u043e\u0432 \u0438\u0437 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <code>node_modules<\/code>;<\/li>\n<li>\u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0440\u043e\u0443\u0442\u044b \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432;<\/li>\n<li>\u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043e\u0448\u0438\u0431\u043e\u043a;<\/li>\n<li>\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u043e\u0440\u0442 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440.<\/li>\n<\/ul>\n<p>  <\/p>\n<pre><code class=\"javascript\">import express from 'express' import { __dirname } from '.\/utils.js' import projectRoutes from '.\/routes\/project.routes.js'  const app = express()  app.use(express.static('public')) app.use(express.json()) app.use(express.urlencoded({ extended: true }))  app.get('\/node_modules\/*', (req, res) => {  res.sendFile(`${__dirname}\/${req.url}`) })  app.use('\/project', projectRoutes)  \/\/ \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435: \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043e\u0448\u0438\u0431\u043e\u043a \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u043c \u0432 \u0446\u0435\u043f\u043e\u0447\u043a\u0435 \u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a\u043e\u0432 app.use((err, req, res, next) => {  console.error(err.message || err)  res.sendStatus(err.status || 500) })  const PORT = process.env.PORT || 5000 app.listen(PORT, () => {  console.log(`? -> ${PORT}`) })<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u043e\u0434\u0443\u043c\u0430\u0435\u043c \u043e \u0442\u043e\u043c, \u043a\u0430\u043a\u0438\u0435 \u0440\u043e\u0443\u0442\u044b \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u043c\u0438. \u041a\u0430\u043a \u043d\u0430\u0441\u0447\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432:<\/p>\n<p>  <\/p>\n<ul>\n<li><code>GET<\/code> \u2014 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0439 \u0432\u0441\u0435\u0445 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432<\/li>\n<li><code>GET<\/code> \u2014 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043f\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044e<\/li>\n<li><code>POST<\/code> \u2014 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/li>\n<li><code>POST<\/code> \u2014 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/li>\n<li><code>DELETE<\/code> \u2014 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041c\u044b \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0440\u043e\u0443\u0442 \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0447\u0435\u0440\u0435\u0437 <code>PUT-\u0437\u0430\u043f\u0440\u043e\u0441<\/code>, \u043d\u043e \u0432 \u044d\u0442\u043e\u043c \u043d\u0435\u0442 \u043e\u0441\u043e\u0431\u043e\u0433\u043e \u0441\u043c\u044b\u0441\u043b\u0430 \u2014 \u043f\u0440\u043e\u0449\u0435 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u043e\u0432\u044b\u043c.<\/p>\n<p>  <\/p>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 <code>routes\/project.routes.js<\/code> \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0440\u043e\u0443\u0442\u0435\u0440 \u0438\u0437 <code>express<\/code> \u0438 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0438\u0437 <code>utils.js<\/code>;<\/li>\n<li>\u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u0440\u043e\u0443\u0442\u0435\u0440\u0430;<\/li>\n<li>\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0438\u0437 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0445 \u0432\u044b\u0448\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430.<\/li>\n<\/ul>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { Router } from 'express' \/\/ \u043c\u044b \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u0430\u0436\u0434\u0443\u044e \u0438\u0437 \u044d\u0442\u0438\u0445 \u0443\u0442\u0438\u043b\u0438\u0442 \u0434\u0430\u043b\u0435\u0435 import {  getFileNames,  createFile,  readFile,  removeFile,  uploadFile } from '..\/utils.js'  export default Router()<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0446\u0435\u043f\u043e\u0447\u043a\u043e\u0439 (\u043e\u0434\u0438\u043d \u0437\u0430 \u0434\u0440\u0443\u0433\u0438\u043c) \u0438\u0434\u0443\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438.<\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043f\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044e:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\"> .get('\/', async (req, res, next) => {    \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438\u0437 \u0441\u0442\u0440\u043e\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 - `?project_name=todos`    const { project_name } = req.query     \/\/ \u0435\u0441\u043b\u0438 `URL` \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0441\u0442\u0440\u043e\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0437\u043d\u0430\u0447\u0438\u0442,    \/\/ \u044d\u0442\u043e \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0439 \u0432\u0441\u0435\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432    \/\/ \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c\u0443 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0443    if (!project_name) return next()     try {      \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442      const project = await readFile(project_name)       \/\/ \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0435\u0433\u043e      res.status(200).json(project)    } catch (e) {      \/\/ \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u043e\u0448\u0438\u0431\u043a\u0443 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0443 \u043e\u0448\u0438\u0431\u043e\u043a      next(e)    }  })<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0439 \u0432\u0441\u0435\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\"> .get('\/', async (req, res, next) => {    try {      \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432      const projects = (await getFileNames()) || []       \/\/ \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0438\u0445      res.status(200).json(projects)    } catch (e) {      next(e)    }  })<\/code><\/pre>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\"> .post('\/create', async (req, res, next) => {    \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u0438\u0437 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430    const { project_name, project_data } = req.body     try {      \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442      await createFile(project_data, project_name)       \/\/ \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u043c \u043e\u0431 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u043c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430      res.status(201).json({ message: `Project \"${project_name}\" created` })    } catch (e) {      next(e)    }  })<\/code><\/pre>\n<p>  <\/p>\n<p>\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\"> .post(    '\/upload',    \/\/ `multer`; \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0435\u043c\u044b\u0439 \u0435\u043c\u0443 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 -    \/\/ \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044f, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0435\u0433\u043e \u0434\u0430\u043d\u043d\u044b\u0435, \u0432 \u0442\u0435\u043b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0434\u043e\u043b\u0436\u043d\u043e \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u043c\u0443 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044e    uploadFile.single('project_data_upload'),    (req, res, next) => {      \/\/ \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u043c \u043e\u0431 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430      res.status(201).json({        message: `Project \"${req.body.project_name}\" uploaded`      })    }  )<\/code><\/pre>\n<p>  <\/p>\n<p>\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\"> .delete('\/', async (req, res, next) => {    \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438\u0437 \u0441\u0442\u0440\u043e\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430    const { project_name } = req.query     try {      \/\/ \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442      await removeFile(project_name)       \/\/ \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u043c \u043e\u0431 \u0443\u0441\u043f\u0435\u0445\u0435      res.status(201).json({ message: `Project \"${project_name}\" removed` })    } catch (e) {      next(e)    }  })<\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u0448\u0438\u0431\u043a\u0430, \u0432\u043e\u0437\u043d\u0438\u043a\u0448\u0430\u044f \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043b\u044e\u0431\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u0430 \u0432 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0439 \u0432 <code>index.js<\/code> \u2014 \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a. \u041d\u0435\u0447\u0442\u043e \u043f\u043e\u0445\u043e\u0436\u0435\u0435 \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0438 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435.<\/p>\n<p>  <\/p>\n<p>\u0412\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u0432 \u0444\u0430\u0439\u043b\u0435 <code>utils.js<\/code> \u2014 \u043f\u043e\u0436\u0430\u043b\u0443\u0439, \u0441\u0430\u043c\u0430\u044f \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430\u044f \u0438 \u043f\u043e\u043b\u0435\u0437\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0430. \u042f \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u043b\u0441\u044f \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u044d\u0442\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0431\u044b\u043b\u0438 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0441 \u0442\u043e\u0439 \u0446\u0435\u043b\u044c\u044e, \u0447\u0442\u043e\u0431\u044b \u0432\u044b \u043c\u043e\u0433\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u0432 \u0441\u0432\u043e\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u0431\u0435\u0437 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439.<\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u0438\u043c\u043f\u043e\u0440\u0442\u0430 \u043c\u043e\u0434\u0443\u043b\u0435\u0439, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u043d\u043e\u0433\u043e (\u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e\u0433\u043e) \u043f\u0443\u0442\u0438 \u043a \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0438 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 (\u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438), \u0430 \u0442\u0430\u043a\u0436\u0435 \u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f 2 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u0443\u0442\u0438\u043b\u0438\u0442:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0444\u0430\u0439\u043b\u0430 \u0438\u043b\u0438 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u043f\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044e \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435;<\/li>\n<li>\u0434\u043b\u044f \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u044f \u043f\u0443\u0442\u0438 \u043d\u0430 1 &#171;\u0435\u0434\u0438\u043d\u0438\u0446\u0443&#187;.<\/li>\n<\/ul>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { dirname } from 'path' import { fileURLToPath } from 'url' import { promises as fs } from 'fs' import multer from 'multer'  \/\/ \u043f\u043e\u043b\u043d\u044b\u0439 \u043f\u0443\u0442\u044c \u043a \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 export const __dirname = dirname(fileURLToPath(import.meta.url)) \/\/ \u043f\u0443\u0442\u044c \u043a \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u043c\u0438 const ROOT_PATH = `${__dirname}\/projects`  \/\/ \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043d\u0435\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e \u0444\u0430\u0439\u043b\u0430 const notExist = (e) => e.code === 'ENOENT' \/\/ \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u044f \u043f\u0443\u0442\u0438 \u043d\u0430 \u0435\u0434\u0438\u043d\u0438\u0446\u0443 \/\/ \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0443\u0442\u044c `path\/to\/file` \u043f\u043e\u0441\u043b\u0435 \u0432\u044b\u0437\u043e\u0432\u0430 \u044d\u0442\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \/\/ \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 `path\/to` const truncPath = (p) => p.split('\/').slice(0, -1).join('\/')<\/code><\/pre>\n<p>  <\/p>\n<p>\u0423\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0444\u0430\u0439\u043b\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 3 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430: \u0434\u0430\u043d\u043d\u044b\u0435, \u043f\u0443\u0442\u044c \u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e `json`) export async function createFile(fileData, filePath, fileExt = 'json') {  \/\/ \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u043b\u043d\u044b\u0439 \u043f\u0443\u0442\u044c \u043a \u0444\u0430\u0439\u043b\u0443  const fileName = `${ROOT_PATH}\/${filePath}.${fileExt}`   try {    \/\/ \u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0444\u0430\u0439\u043b    \/\/ \u043f\u0440\u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0438 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0434\u043b\u044f \u0444\u0430\u0439\u043b\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u043d\u044b\u043c \u043f\u0443\u0442\u0435\u043c    \/\/ \u0444\u0430\u0439\u043b\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f `...\/data\/todos.json`, \u0432\u044b\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435    if (fileExt === 'json') {      await fs.writeFile(fileName, JSON.stringify(fileData, null, 2))    } else {      await fs.writeFile(fileName, fileData)    }  } catch (err) {    \/\/ \u0435\u0441\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0430 \u0441\u0432\u044f\u0437\u0430\u043d\u0430 \u0441 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0435\u0439    if (notExist(err)) {      \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0435\u0435 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u043e (\u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0443\u0440\u043e\u0432\u043d\u0435\u0439 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438)      await fs.mkdir(truncPath(`${ROOT_PATH}\/${filePath}`), {        recursive: true      })      \/\/ \u0438 \u0441\u043d\u043e\u0432\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c `createFile` \u0441 \u0442\u0435\u043c\u0438 \u0436\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 - \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u044f      return createFile(fileData, filePath, fileExt)    }    \/\/ \u0435\u0441\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0430 \u043d\u0435 \u0441\u0432\u044f\u0437\u0430\u043d\u0430 \u0441 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0435\u0439    \/\/ \u044d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u043d\u044f\u0442\u044c\u0441\u044f \u0438\u0437 \u0443\u0442\u0438\u043b\u0438\u0442\u044b \u0432 \u0440\u043e\u0443\u0442 \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443 \u0432 \u0446\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a    throw err  } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0423\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0444\u0430\u0439\u043b\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043f\u0443\u0442\u044c \u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 export async function readFile(filePath, fileExt = 'json') {  \/\/ \u043f\u043e\u043b\u043d\u044b\u0439 \u043f\u0443\u0442\u044c  const fileName = `${ROOT_PATH}\/${filePath}.${fileExt}`   \/\/ \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0444\u0430\u0439\u043b\u0430  let fileHandler = null  try {    \/\/ `fs.open()` \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0444\u0430\u0439\u043b\u0430 \u043f\u0440\u0438 \u043d\u0430\u043b\u0438\u0447\u0438\u0438 \u0444\u0430\u0439\u043b\u0430    \/\/ \u0438\u043b\u0438 \u0432\u044b\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u0442 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0438 \u0444\u0430\u0439\u043b\u0430    \/\/ \u044d\u0442\u043e \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c\u044b\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043d\u0430\u043b\u0438\u0447\u0438\u044f \u0444\u0430\u0439\u043b\u0430    fileHandler = await fs.open(fileName)     \/\/ \u0447\u0438\u0442\u0430\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430    const fileContent = await fileHandler.readFile('utf-8')     \/\/ \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0435\u0433\u043e    return fileExt === 'json' ? JSON.parse(fileContent) : fileContent  } catch (err) {    \/\/ \u0435\u0441\u043b\u0438 \u0444\u0430\u0439\u043b \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442    \/\/ \u0432\u044b \u043f\u043e\u0439\u043c\u0435\u0442\u0435 \u043f\u043e\u0447\u0435\u043c\u0443 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043a\u0443\u044e \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0443 \u043e\u0448\u0438\u0431\u043a\u0438,    \/\/ \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u0440\u043e\u0443\u0442\u0430\u043c \u0434\u043b\u044f `API`    if (notExist(err)) {      throw { status: 404, message: 'Not found' }    }    \/\/ \u0435\u0441\u043b\u0438 \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u0434\u0440\u0443\u0433\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430    throw err  } finally {    \/\/ \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0444\u0430\u0439\u043b\u0430    fileHandler?.close()  } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0423\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0444\u0430\u0439\u043b\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043f\u0443\u0442\u044c \u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 export async function removeFile(filePath, fileExt = 'json') {  \/\/ \u043f\u043e\u043b\u043d\u044b\u0439 \u043f\u0443\u0442\u044c  const fileName = `${ROOT_PATH}\/${filePath}.${fileExt}`   try {    \/\/ \u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0444\u0430\u0439\u043b    await fs.unlink(fileName)     \/\/ \u043d\u0430\u043c \u0442\u0430\u043a\u0436\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e, \u0435\u0441\u043b\u0438 \u0442\u0430\u043a\u043e\u0432\u0430\u044f \u0438\u043c\u0435\u0435\u0442\u0441\u044f    \/\/ \u043c\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0443\u0442\u0438\u043b\u0438\u0442\u0435 \u043f\u0443\u0442\u044c, \u0441\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u043d\u044b\u0439 \u043d\u0430 \u0435\u0434\u0438\u043d\u0438\u0446\u0443, \u0442.\u0435. \u0431\u0435\u0437 \u0443\u0447\u0435\u0442\u0430 \u043f\u0443\u0442\u0438 \u0444\u0430\u0439\u043b\u0430    await removeDir(truncPath(`${ROOT_PATH}\/${filePath}`))  } catch (err) {    \/\/ \u0435\u0441\u043b\u0438 \u0444\u0430\u0439\u043b \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442    if (notExist(err)) {      throw { status: 404, message: 'Not found' }    }    \/\/ \u0435\u0441\u043b\u0438 \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u0434\u0440\u0443\u0433\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430    throw err  } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0423\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043f\u0443\u0442\u044c \u043a \u0443\u0434\u0430\u043b\u044f\u0435\u043c\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0438 \u043f\u0443\u0442\u044c \u043a \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438, \/\/ \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0438\u043c\u0435\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u043c\u0438 async function removeDir(dirPath, rootPath = ROOT_PATH) {  \/\/ \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c\u0441\u044f, \u0435\u0441\u043b\u0438 \u0434\u043e\u0441\u0442\u0438\u0433\u043b\u0438 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438  if (dirPath === rootPath) return   \/\/ \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043b\u0438 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f \u043f\u0443\u0441\u0442\u043e\u0439  \/\/ \u0434\u043b\u0438\u043d\u0430 \u0435\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e \u0434\u043e\u043b\u0436\u043d\u0430 \u0440\u0430\u0432\u043d\u044f\u0442\u044c\u0441\u044f 0  const isEmpty = (await fs.readdir(dirPath)).length &lt; 1   \/\/ \u0435\u0441\u043b\u0438 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0443\u0441\u0442\u043e\u0439  if (isEmpty) {    \/\/ \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0435\u0435    await fs.rmdir(dirPath)     \/\/ \u0438... \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u044f    \/\/ \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u0439 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438 \u043c\u044b \u0441\u043e\u043a\u0440\u0430\u0449\u0430\u0435\u043c \u043f\u0443\u0442\u044c \u043d\u0430 \u0435\u0434\u0438\u043d\u0438\u0446\u0443,    \/\/ \u043f\u043e\u043a\u0430 \u043d\u0435 \u043f\u043e\u0434\u043d\u0438\u043c\u0435\u043c\u0441\u044f \u0434\u043e \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438    removeDir(truncPath(dirPath), rootPath)  } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0415\u0449\u0435 \u043e\u0434\u043d\u0430 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0438 (\u043e\u0431\u0435\u0449\u0430\u044e, \u0447\u0442\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f) \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0439 \u0432\u0441\u0435\u0445 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043f\u0443\u0442\u044c \u043a \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 export async function getFileNames(path = ROOT_PATH) {  \/\/ \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0434\u043b\u044f \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432  let fileNames = []   try {    \/\/ \u0447\u0438\u0442\u0430\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438    const files = await fs.readdir(path)     \/\/ \u0435\u0441\u043b\u0438 \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0444\u0430\u0439\u043b    \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043c\u0430\u0441\u0441\u0438\u0432 \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f\u043c\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432    if (files.length &lt; 1) return fileNames     \/\/ \u0438\u043d\u0430\u0447\u0435 \u043f\u0435\u0440\u0435\u0431\u0438\u0440\u0430\u0435\u043c \u0444\u0430\u0439\u043b\u044b    for (let file of files) {      \/\/ \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u043f\u0443\u0442\u044c \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430      file = `${path}\/${file}`       \/\/ \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c, \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043b\u0438 \u0444\u0430\u0439\u043b \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0435\u0439      const isDir = (await fs.stat(file)).isDirectory()       \/\/ \u0435\u0441\u043b\u0438 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f      if (isDir) {        \/\/ \u043f\u0440\u0438\u0431\u0435\u0433\u0430\u0435\u043c \u043a \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0438        fileNames = fileNames.concat(await getFileNames(file))      } else {        \/\/ \u0435\u0441\u043b\u0438 \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0433\u043e \u043f\u0443\u0442\u044c \u0432 \u043c\u0430\u0441\u0441\u0438\u0432        fileNames.push(file)      }    }     return fileNames  } catch (err) {    if (notExist(err)) {      throw { status: 404, message: 'Not found' }    }    throw err  } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043d\u0443\u0436\u043d\u0430 \u043d\u0430\u043c \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u043c\u0438 \u2014 \u044d\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u043e\u0432. \u0414\u0430\u043d\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0442\u0430\u043a\u043e\u0439 \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0439, \u043a\u0430\u043a \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0435. \u041e\u043d\u0430 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0430 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 <code>multipart\/form-data<\/code>, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u043c\u0438 \u0432\u043f\u043e\u043b\u043d\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044f:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a\u0430 \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u043e\u0432 export const uploadFile = multer({  storage: multer.diskStorage({    \/\/ \u043f\u0443\u043d\u043a\u0442 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f - \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f \u0434\u043b\u044f \u0444\u0430\u0439\u043b\u043e\u0432    destination: (req, file, cb) => {      \/\/ \u0432\u0430\u0436\u043d\u043e: \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0447\u0430\u0441\u0442\u044c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0442\u044c \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u0444\u0430\u0439\u043b\u0430      \/\/ \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0435\u0441\u043b\u0438 \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f `data\/todos`, \u0442\u043e \u0444\u0430\u0439\u043b \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f `todos.json`      \/\/ \u043c\u044b \u0442\u0430\u043a\u0436\u0435 \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0444\u0430\u0439\u043b\u0430 \u0438\u0437 \u043f\u0443\u0442\u0438 \u043a \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438      const dirPath = `${ROOT_PATH}\/${req.body.project_name.replace(        file.originalname.replace('.json', ''),        ''      )}`      \/\/ \u0437\u0434\u0435\u0441\u044c \u043c\u044b \u0438\u0441\u0445\u043e\u0434\u0438\u043c \u0438\u0437 \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0447\u0442\u043e \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f \u0434\u043b\u044f \u0444\u0430\u0439\u043b\u0430 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442      \/\/ \u0441 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0435\u0439 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0441\u043b\u0443\u0447\u0438\u0442\u0441\u044f      fs.mkdir(dirPath, { recursive: true }).then(() => {        cb(null, dirPath)      })    },    \/\/ \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0444\u0430\u0439\u043b\u0430    filename: (_, file, cb) => {      cb(null, file.originalname)    }  }) })<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412\u0441\u0435, \u0447\u0442\u043e \u043d\u0430\u043c \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u043c\u0438 \u2014 \u044d\u0442\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0430\u0434\u043c\u0438\u043d\u043a\u0438.<\/p>\n<p>  <\/p>\n<h2 id=\"klient\">\u041a\u043b\u0438\u0435\u043d\u0442<\/h2>\n<p>  <\/p>\n<p>\u0420\u0430\u0437\u043c\u0435\u0442\u043a\u0430 (<code>public\/index.html<\/code>):<\/p>\n<p>  <\/p>\n<pre><code class=\"html\">&lt;head>  &lt;!-- \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 -->  &lt;title>Mock API&lt;\/title>  &lt;!-- \u0438\u043a\u043e\u043d\u043a\u0430 -->  &lt;link rel=\"icon\" href=\"icon.png\" \/>  &lt;!-- \u0413\u0443\u0433\u043b-\u0448\u0440\u0438\u0444\u0442 -->  &lt;link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\" \/>  &lt;link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin \/>  &lt;link    href=\"https:\/\/fonts.googleapis.com\/css2?family=Montserrat&amp;display=swap\"    rel=\"stylesheet\"  \/>  &lt;!-- bootstrap -->  &lt;link    rel=\"stylesheet\"    href=\"https:\/\/stackpath.bootstrapcdn.com\/bootstrap\/4.3.1\/css\/bootstrap.min.css\"    integrity=\"sha384-ggOyR0iXCbMQv3Xipma34MD+dH\/1fQ784\/j6cY\/iJTQUOhcWr7x9JvoRxT2MZw1T\"    crossorigin=\"anonymous\"  \/>  &lt;!-- bootstrap-icons -->  &lt;link    rel=\"stylesheet\"    href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap-icons@1.3.0\/font\/bootstrap-icons.css\"  \/>  &lt;!-- \u0441\u0442\u0438\u043b\u0438 -->  &lt;link rel=\"stylesheet\" href=\"style.css\" \/> &lt;\/head> &lt;body>  &lt;div class=\"container\">    &lt;header>      &lt;h1 class=\"text-center my-3\">Mock API&lt;\/h1>    &lt;\/header>     &lt;main>      &lt;div>        &lt;h3 class=\"my-3\">My projects&lt;\/h3>        &lt;!-- \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 -->        &lt;ul class=\"list-group\" id=\"project_list\">&lt;\/ul>      &lt;\/div>       &lt;div>        &lt;h3 class=\"my-3\">New project&lt;\/h3>        &lt;!-- \u0444\u043e\u0440\u043c\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 -->        &lt;form id=\"project_create\">          &lt;div class=\"mb-2\">            &lt;label for=\"project_name\" class=\"form-label\">Project name&lt;\/label>            &lt;!-- \u043f\u043e\u043b\u0435 \u0434\u043b\u044f \u0432\u0432\u043e\u0434\u0430 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 -->            &lt;input              type=\"text\"              class=\"form-control mt-2\"              name=\"project_name\"              id=\"project_name\"              aria-describedby=\"project_name\"              placeholder=\"Project name\"            \/>          &lt;\/div>           &lt;details class=\"mt-4\">            &lt;summary>Enter or paste project data&lt;\/summary>            &lt;!-- \u043f\u043e\u043b\u0435 \u0434\u043b\u044f \u0432\u0432\u043e\u0434\u0430\/\u0432\u0441\u0442\u0430\u0432\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 -->            &lt;textarea              class=\"form-control mt-2\"              name=\"project_data\"              id=\"project_data_paste\"              rows=\"10\"            >&lt;\/textarea>          &lt;\/details>          &lt;div class=\"mt-4\">            &lt;label for=\"project_data_upload\" class=\"form-label\"              >Upload project data&lt;\/label            >            &lt;!-- \u043f\u043e\u043b\u0435 \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u0430 \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 -->            &lt;!-- \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e JSON-\u0444\u0430\u0439\u043b\u044b \u0432 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u043c \u0447\u0438\u0441\u043b\u0435 -->            &lt;input              class=\"form-control mt-2\"              type=\"file\"              accept=\".json\"              name=\"project_data_upload\"              id=\"project_data_upload\"              aria-describedby=\"project_data_upload\"            \/>          &lt;\/div>          &lt;!-- \u043a\u043d\u043e\u043f\u043a\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 -->          &lt;button class=\"btn btn-success my-4\">Create project&lt;\/button>        &lt;\/form>      &lt;\/div>    &lt;\/main>  &lt;\/div>   &lt;!-- \u0441\u043a\u0440\u0438\u043f\u0442-\u043c\u043e\u0434\u0443\u043b\u044c -->  &lt;script src=\"script.js\" type=\"module\">&lt;\/script> &lt;\/body><\/code><\/pre>\n<p>  <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em> \u043d\u0430 <code>id<\/code> \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432. \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0441 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u043c <code>id<\/code> \u0441\u0442\u0430\u043d\u043e\u0432\u044f\u0442\u0441\u044f \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 <code>window<\/code>, \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0442\u0430\u043a\u0438\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u043c \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e, \u0442.\u0435. \u0431\u0435\u0437 \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0442\u0430\u043a\u0438\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432, \u043a\u0430\u043a <code>querySelector()<\/code>.<\/p>\n<p>  <\/p>\n<p>\u041d\u0430 \u0441\u0442\u0438\u043b\u044f\u0445 \u044f \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0435 \u0431\u0443\u0434\u0443: \u0432\u0441\u0435, \u0447\u0442\u043e \u043c\u044b \u0442\u0430\u043c \u0434\u0435\u043b\u0430\u0435\u043c \u2014 \u044d\u0442\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0448\u0440\u0438\u0444\u0442 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0438 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u0435\u043c \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0443\u044e \u0448\u0438\u0440\u0438\u043d\u0443 <code>.container<\/code>.<\/p>\n<p>  <\/p>\n<p>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a <code>public\/script.js<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c <code>very-simple-fetch<\/code>, \u0444\u0438\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u0443\u0442\u0438\u043b\u0438\u0442\u0443 \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u0433\u043e, \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043b\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0439 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 <code>JSON<\/code>, \u0438 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 <code>URL<\/code> \u0441\u0435\u0440\u0432\u0435\u0440\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import simpleFetch from '\/node_modules\/very-simple-fetch\/index.js' import todos from '.\/data\/todos.js' import { isJson } from '.\/utils.js'  simpleFetch.baseUrl = 'http:\/\/localhost:5000\/project'<\/code><\/pre>\n<p>  <\/p>\n<p>\u0424\u0438\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 (<code>public\/data\/todos.js<\/code>):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">export default [  {    id: '1',    text: 'Eat',    done: true,    edit: false  },  {    id: '2',    text: 'Code',    done: true,    edit: false  },  {    id: '3',    text: 'Sleep',    done: false,    edit: false  },  {    id: '4',    text: 'Repeat',    done: false,    edit: false  } ]<\/code><\/pre>\n<p>  <\/p>\n<p>\u0423\u0442\u0438\u043b\u0438\u0442\u0430 (<code>public\/utils.js<\/code>):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">export const isJson = (item) => {  try {    item = JSON.parse(item)  } catch (e) {    return false  }   if (typeof item === 'object' &amp;&amp; item !== null) {    return true  }   return false }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">async function fetchProjects() {  \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u043e\u0448\u0438\u0431\u043a\u0443  \/\/ `customCache: false` \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043a\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432  const { data, error } = await simpleFetch.get({ customCache: false })   \/\/ \u0435\u0441\u043b\u0438 \u043f\u0440\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430  if (error) {    return console.error(error)  }   \/\/ \u043e\u0447\u0438\u0449\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432  project_list.innerHTML = ''   \/\/ \u0435\u0441\u043b\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u043d\u0435\u0442  if (data.length &lt; 1) {    \/\/ \/*html*\/ - \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 `es6-string-html` \u0434\u043b\u044f VSCode    \/\/ \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u043e\u0434\u0441\u0432\u0435\u0442\u043a\u0443 \u0438 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0432 \u0448\u0430\u0431\u043b\u043e\u043d\u043d\u044b\u0445 \u043b\u0438\u0442\u0435\u0440\u0430\u043b\u0430\u0445    return (project_list.innerHTML = \/*html*\/ `      &lt;li        class=\"list-group-item d-flex align-items-center\"      >        You have no projects. Why don't create one?      &lt;\/li>    `)  }   \/\/ \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0443\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a, \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432  const projects = data.map((p) =>    p.replace(\/.+projects\\\/\/, '').replace('.json', '')  )   \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430  \/\/ \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b `data-*`  for (const p of projects) {    project_list.innerHTML += \/*html*\/ `      &lt;li        class=\"list-group-item d-flex align-items-center\"        data-name=\"${p}\"      >        &lt;span class=\"flex-grow-1\">          ${p}        &lt;\/span>        &lt;button          class=\"btn btn-outline-success\"          data-action=\"edit\"        >          &lt;i class=\"bi bi-pencil\">&lt;\/i>        &lt;\/button>        &lt;button          class=\"btn btn-outline-danger\"          data-action=\"remove\"        >          &lt;i class=\"bi bi-trash\">&lt;\/i>        &lt;\/button>      &lt;\/li>    `  } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0438\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">function initProject(name, data) {  project_name.value = name  project_data_paste.value = isJson(data) ? data : JSON.stringify(data, null, 2) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432. \u041e\u043d\u0430 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043a\u043d\u043e\u043f\u043e\u043a \u0434\u043b\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0444\u043e\u0440\u043c\u044b \u2014 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438\u043b\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">function initHandlers() {  \/\/ ... }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043a\u043d\u043e\u043f\u043e\u043a:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043a\u043d\u043e\u043f\u043e\u043a \u0434\u0435\u043b\u0435\u0433\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0441\u043f\u0438\u0441\u043a\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 - \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0443 `ul` project_list.onclick = ({ target }) => {  \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443  const button = target.matches('button') ? target : target.closest('button')   \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0438\u043f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438  const { action } = button.dataset   \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430  const { name } = target.closest('li').dataset   if (button &amp;&amp; action &amp;&amp; name) {    switch (action) {      case 'edit':        \/\/ \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430        return editProject(name)      case 'remove':        \/\/ \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430        return removeProject(name)      default:        return    }  } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0444\u043e\u0440\u043c\u044b:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">project_create.onsubmit = async (e) => {  e.preventDefault()   \/\/ \u043f\u0440\u043e\u0435\u043a\u0442 \u0434\u043e\u043b\u0436\u0435\u043d \u0438\u043c\u0435\u0442\u044c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435  if (!project_name.value.trim()) return   \/\/ \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 \u043e\u0442\u0432\u0435\u0442\u0430 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430  let data, response   \/\/ \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e `&lt;input type=\"file\" \/>` \u0444\u0430\u0439\u043b  \/\/ \u0438\u043c\u0435\u0435\u0442 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 \u043f\u0435\u0440\u0435\u0434 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c `&lt;textarea>`  if (project_data_upload.value) {    \/\/ `multipart\/form-data`    \/\/ \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u0435\u0439 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u044e\u0442 \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438    \/\/ \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 `name` \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 `input` \u0438 `textarea`    data = new FormData(project_create)     \/\/ \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u043b\u0438\u0448\u043d\u0435\u0435 \u043f\u043e\u043b\u0435    data.delete('project_data_paste')     \/\/ \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043e\u0442\u0432\u0435\u0442    response = await simpleFetch.post('\/upload', data, {      \/\/ `multer` \u0442\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0431\u044b\u043b\u0438 \u043f\u0443\u0441\u0442\u044b\u043c\u0438      headers: {}    })  } else {    \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430    data = project_data_paste.value.trim()    \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430    const name = project_name.value.trim()     \/\/ \u0435\u0441\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u043b\u0438 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442    if (!data || !name) return     \/\/ \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0442\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430    const body = {      project_name: name,      project_data: isJson(data) ? JSON.parse(data) : data    }     \/\/ \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043e\u0442\u0432\u0435\u0442    response = await simpleFetch.post('\/create', body)  }   \/\/ \u043e\u0447\u0438\u0449\u0430\u0435\u043c \u043f\u043e\u043b\u044f  project_name.value = ''  project_data_paste.value = ''  project_data_upload.value = ''   \/\/ \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043e\u0442\u0432\u0435\u0442\u0430  \/\/ \u0432\u0430\u0436\u043d\u043e: \u0434\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432  \/\/ \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0436\u0434\u0430\u0442\u044c \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043e\u0442\u0432\u0435\u0442\u0430  await handleResponse(response) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">async function editProject(name) {  \/\/ \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0432 \u0432\u0438\u0434\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430  const { data, error } = await simpleFetch.get(`?project_name=${name}`)   if (error) {    return console.error(error)  }   \/\/ \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445  initProject(name, data) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">async function removeProject(name) {  \/\/ \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0432 \u0432\u0438\u0434\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430  const response = await simpleFetch.remove(`?project_name=${name}`)   \/\/ \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043e\u0442\u0432\u0435\u0442\u0430  await handleResponse(response) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043e\u0442\u0432\u0435\u0442\u0430 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">async function handleResponse(response) {  \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u043e\u0448\u0438\u0431\u043a\u0443 \u0438\u0437 \u043e\u0442\u0432\u0435\u0442\u0430  const { data, error } = response   \/\/ \u0435\u0441\u043b\u0438 \u043f\u0440\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 \u043e\u0442\u0432\u0435\u0442\u0430 \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430  if (error) {    return console.error(error)  }   \/\/ \u0432\u044b\u0432\u043e\u0434\u0438\u043c \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e\u0431 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u043e\u0439 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438  console.log(data.message)   \/\/ \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432  await fetchProjects() }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043d\u0430\u0448\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 fetchProjects() \/\/ \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \/\/ initProject(\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430) initProject('todos', todos) \/\/ \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 initHandlers()<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0448\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u043c\u0438.<\/p>\n<p>  <\/p>\n<p>\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn dev # or npm run dev<\/code><\/pre>\n<p>  <\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u043e\u0432\u0430\u044f \u0432\u043a\u043b\u0430\u0434\u043a\u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <code>http:\/\/localhost:5000<\/code>:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/k9\/ih\/qy\/k9ihqyadr2xptnecjgzn14trfqs.png\" data-src=\"https:\/\/habrastorage.org\/webt\/k9\/ih\/qy\/k9ihqyadr2xptnecjgzn14trfqs.png\"\/>  <\/p>\n<p>  \u0423 \u043d\u0430\u0441 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c <code>Create project<\/code>. \u0412 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <code>projects<\/code> \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0444\u0430\u0439\u043b <code>todos.json<\/code>, \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/ea\/ov\/ay\/eaovay2rmwayppvx0pos6fth1fg.png\" data-src=\"https:\/\/habrastorage.org\/webt\/ea\/ov\/ay\/eaovay2rmwayppvx0pos6fth1fg.png\"\/>  <\/p>\n<p>  \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b. \u0412\u0432\u043e\u0434\u0438\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>data\/todos<\/code>, \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u043f\u0443\u0442 <code>JSON-\u0444\u0430\u0439\u043b<\/code> \u0438\u0437 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <code>public\/data<\/code>, \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c <code>Create project<\/code>. \u0412 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <code>projects<\/code> \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f <code>data<\/code> \u0441 \u0444\u0430\u0439\u043b\u043e\u043c \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/sr\/6c\/ux\/sr6cuxrfq_nfq8e7ue9smqjnsdc.png\" data-src=\"https:\/\/habrastorage.org\/webt\/sr\/6c\/ux\/sr6cuxrfq_nfq8e7ue9smqjnsdc.png\"\/>  <\/p>\n<p>  \u041d\u0430\u0436\u0430\u0442\u0438\u0435 \u043a\u043d\u043e\u043f\u043e\u043a \u0434\u043b\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u043c.<\/p>\n<p>  <\/p>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u043c\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0438\u0440\u0443\u0435\u0442 \u0432 \u0448\u0442\u0430\u0442\u043d\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435. \u041d\u043e \u043f\u043e\u043a\u0430 \u043e\u043d \u043d\u0435 \u0443\u043c\u0435\u0435\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0441\u043e \u0441\u0442\u043e\u0440\u043e\u043d\u044b. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u043c.<\/p>\n<p>  <\/p>\n<h2 id=\"routy-dlya-api\">\u0420\u043e\u0443\u0442\u044b \u0434\u043b\u044f <code>API<\/code><\/h2>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <code>api.routes.js<\/code> \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <code>routes<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0440\u043e\u0443\u0442\u0435\u0440 \u0438\u0437 <code>express<\/code>, \u0443\u0442\u0438\u043b\u0438\u0442\u044b \u0438\u0437 <code>utils.js<\/code> \u0438 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u0440\u043e\u0443\u0442\u0435\u0440\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { Router } from 'express' import { createFile, readFile, queryMap, areEqual } from '..\/utils.js'  export default Router()<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u0441\u0430\u043c\u043e\u0433\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u2014 <code>POST-\u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/code> \u043d\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">.post('*', async (req, res, next) => {  try {    \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442    const project = await readFile(req.url)     \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043f\u0443\u0442\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e    const newProject = project.concat(req.body)     \/\/ \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442    await createFile(newProject, req.url)     \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442    res.status(201).json(newProject)  } catch (e) {    next(e)  } })<\/code><\/pre>\n<p>  <\/p>\n<p><code>DELETE-\u0437\u0430\u043f\u0440\u043e\u0441<\/code> \u043d\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ `slug` - \u044d\u0442\u043e \u043b\u044e\u0431\u043e\u0439 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \/\/ \u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u043d\u0430\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043a\u0430\u043a \u0443\u0433\u043e\u0434\u043d\u043e, \u043d\u043e, \u043e\u0431\u044b\u0447\u043d\u043e, \u0438\u043c\u0435\u043d\u0443\u0435\u0442\u0441\u044f \u043a\u0430\u043a `slug` \u0438\u043b\u0438 `param` \/\/ \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0442\u0430\u043a\u0438\u043c \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f `id` \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \/\/ \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0442\u0435\u043a\u0441\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 .delete('*\/:slug', async (req, res, next) => {  \/\/ \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438\u043c\u0435\u044e\u0442 \u0432\u0438\u0434 `{ '0': '\/todos', slug: '1' }`  \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u043f\u0443\u0442\u044c \u0438 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440  const [url, slug] = Object.values(req.params)   try {    \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442    const project = await readFile(url)     \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043f\u0443\u0442\u0435\u043c \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0438 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e    const newProject = project.filter(      (p) => !Object.values(p).find((v) => v === slug)    )     \/\/ \u0435\u0441\u043b\u0438 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0438 \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u044b \u0440\u0430\u0432\u043d\u044b, \u0437\u043d\u0430\u0447\u0438\u0442,    \/\/ \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043e    \/\/ \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0445\u0430\u043a    if (areEqual(project, newProject)) {      throw { status: 404, message: 'Not found' }    }     \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442    await createFile(newProject, url)     \/\/ \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0435\u0433\u043e    res.status(201).json(newProject)  } catch (e) {    next(e)  } })<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412\u043e\u0442 \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 (<code>utils.js<\/code>):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">export function areEqual(a, b) {  if (a === b) return true   if (a instanceof Date &amp;&amp; b instanceof Date) return a.getTime() === b.getTime()   if (!a || !b || (typeof a !== 'object' &amp;&amp; typeof b !== 'object'))    return a === b   if (a.prototype !== b.prototype) return false   const keys = Object.keys(a)   if (keys.length !== Object.keys(b).length) return false   return keys.every((k) => areEqual(a[k], b[k])) }<\/code><\/pre>\n<p>  <\/p>\n<p><code>PUT-\u0437\u0430\u043f\u0440\u043e\u0441<\/code> \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043f\u043e\u0445\u043e\u0436\u0435:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">.put('*\/:slug', async (req, res, next) => {  const [url, slug] = Object.values(req.params)   try {    const project = await readFile(url)     \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043f\u0443\u0442\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e    const newProject = project.map((p) => {      if (Object.values(p).find((v) => v === slug)) {        return { ...p, ...req.body }      } else return p    })     \/\/ \u0435\u0441\u043b\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0440\u0430\u0432\u043d\u044b...    if (areEqual(project, newProject)) {      throw { status: 404, message: 'Not found' }    }     await createFile(newProject, url)     res.status(201).json(newProject)  } catch (e) {    next(e)  } })<\/code><\/pre>\n<p>  <\/p>\n<p>\u0427\u0442\u043e \u043a\u0430\u0441\u0430\u0435\u0442\u0441\u044f <code>GET-\u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/code> \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0442\u043e \u0441 \u043d\u0438\u043c \u0432\u0441\u0435 \u043d\u0435 \u0442\u0430\u043a \u043f\u0440\u043e\u0441\u0442\u043e. \u041c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0438\u043c\u0435\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u043a\u0430\u043a \u043f\u0440\u043e\u0435\u043a\u0442 \u0446\u0435\u043b\u0438\u043a\u043e\u043c, \u0442\u0430\u043a \u0438 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u043d\u0435\u0433\u043e. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0443 \u043d\u0430\u0441 \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0438\u0445 \u043a\u0430\u043a \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430 (\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430) \u2014 \u0432 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442, \u0442\u0430\u043a \u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u0442\u0440\u043e\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u2014 \u0432 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u043c\u0430\u0441\u0441\u0438\u0432.<\/p>\n<p>  <\/p>\n<p><code>GET-\u0437\u0430\u043f\u0440\u043e\u0441<\/code> \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">.get('*', async (req, res, next) => {  \/\/ \u0435\u0441\u043b\u0438 \u0437\u0430\u043f\u0440\u043e\u0441 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 `?`, \u0437\u043d\u0430\u0447\u0438\u0442, \u043e\u043d \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0441\u0442\u0440\u043e\u043a\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0430  \/\/ \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c\u0443 \u0440\u043e\u0443\u0442\u0443  if (req.url.includes('?')) {    return next()  }   try {    \/\/ \u043f\u0440\u043e\u0431\u0443\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442    const project = await readFile(req.url)     \/\/ \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0435\u0433\u043e    res.status(200).json(project)  } catch (e) {    \/\/ `throw { status: 404, message: 'Not found' }`    \/\/ \u0435\u0441\u043b\u0438 \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043c\u044b \u0438\u043c\u0435\u0435\u043c \u0434\u0435\u043b\u043e \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c    \/\/ \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0443    if (e.status === 404) {      \/\/ \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c\u0443 \u0440\u043e\u0443\u0442\u0443      return next()    }     \/\/ \u0434\u0440\u0443\u0433\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430    next(e)  } })<\/code><\/pre>\n<p>  <\/p>\n<p><code>GET-\u0437\u0430\u043f\u0440\u043e\u0441<\/code> \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0443 \u0438\u043b\u0438 \u0441\u0442\u0440\u043e\u043a\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">.get('*\/:slug', async (req, res, next) => {  let project = null   try {    \/\/ \u0435\u0441\u043b\u0438 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0441\u0442\u0440\u043e\u043a\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0430    if (req.url.includes('?')) {      \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442, \u0443\u0434\u0430\u043b\u044f\u044f \u0441\u0442\u0440\u043e\u043a\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0430      project = await readFile(req.url.replace(\/\\?.+\/, ''))       \/\/ `req.query` - \u044d\u0442\u043e \u043e\u0431\u044a\u0435\u043a\u0442 \u0432\u0438\u0434\u0430 `{ id: '1' }`, \u0435\u0441\u043b\u0438 \u0441\u0442\u0440\u043e\u043a\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0431\u044b\u043b\u0430 `?id=1`      \/\/ \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0441\u043e\u0431\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c      \/\/ \u043e\u0431 \u0443\u0442\u0438\u043b\u0438\u0442\u0435 `queryMap` \u043c\u044b \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435      const notQueryKeyValues = Object.entries(req.query).filter(        ([k]) => !queryMap[k] &amp;&amp; k !== 'order'      )       \/\/ \u0435\u0441\u043b\u0438 \u0438\u043c\u0435\u044e\u0442\u0441\u044f \"\u043e\u0431\u044b\u0447\u043d\u044b\u0435\" \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b      if (notQueryKeyValues.length > 0) {        \/\/ \u0444\u0438\u043b\u044c\u0442\u0440\u0443\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u0438\u0445 \u043e\u0441\u043d\u043e\u0432\u0435        project = project.filter((p) =>          notQueryKeyValues.some(([k, v]) => {            if (p[k]) {              \/\/ \u0443\u043d\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0447\u043d\u043e\u0441\u0442\u0438              return p[k].toString() === v.toString()            }          })        )      }       \/\/ \u0435\u0441\u043b\u0438 \u0441\u0442\u0440\u043e\u043a\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b `sort` \u0438\/\u0438\u043b\u0438 `order`      \/\/ \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0443 \u0434\u0430\u043d\u043d\u044b\u0445      if (req.query['sort'] || req.query['order']) {        project = queryMap.sort(          project,          req.query['sort'],          req.query['order']        )      }       \/\/ \u0435\u0441\u043b\u0438 \u0441\u0442\u0440\u043e\u043a\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 `offset`      \/\/ \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0441\u0434\u0432\u0438\u0433 - \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432      if (req.query['offset']) {        project = queryMap.offset(project, req.query['offset'])      }       \/\/ \u0435\u0441\u043b\u0438 \u0441\u0442\u0440\u043e\u043a\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 `limit`      \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432      if (req.query['limit']) {        project = queryMap.limit(project, req.query['limit'])      }    } else {      \/\/ \u0435\u0441\u043b\u0438 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0441\u0442\u0440\u043e\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430      \/\/ \u0437\u043d\u0430\u0447\u0438\u0442, \u044d\u0442\u043e \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430      \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442      const _project = await readFile(req.params[0])       \/\/ \u043f\u044b\u0442\u0430\u0435\u043c\u0441\u044f \u043d\u0430\u0439\u0442\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0443      for (const item of _project) {        for (const key in item) {          if (item[key] === req.params.slug) {            project = item          }        }      }    }     \/\/ \u0435\u0441\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043e    if (!project || project.length &lt; 1) return res.sendStatus(404)     \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435    res.status(200).json(project)  } catch (e) {    next(e)  } })<\/code><\/pre>\n<p>  <\/p>\n<p>\u0423\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0441\u0442\u0440\u043e\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c (<code>utils.js<\/code>):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u044b `Intl.Collator` \u0434\u043b\u044f \u043b\u043e\u043a\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f \u0441\u0442\u0440\u043e\u043a \u0438 \u0447\u0438\u0441\u0435\u043b const strCollator = new Intl.Collator() const numCollator = new Intl.Collator([], { numeric: true })  export const queryMap = {  \/\/ \u0441\u0434\u0432\u0438\u0433 \u0438\u043b\u0438 \u043f\u0440\u043e\u043f\u0443\u0441\u043a \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432  offset: (items, count) => items.slice(count),  \/\/ \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432  limit: (items, count) => items.slice(0, count),  \/\/ \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432  \/\/ \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0441\u043e\u0440\u0442\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u043f\u043e `id` \u0438 \u043f\u043e \u0432\u043e\u0437\u0440\u0430\u0441\u0442\u0430\u043d\u0438\u044e  sort(items, field = 'id', order = 'asc') {    \/\/ \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c, \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044f \u0434\u043b\u044f \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438 \u0441\u0442\u0440\u043e\u043a\u0430\u043c\u0438    const isString = typeof items[0][field] === 'string' &amp;&amp; Number.isNaN(items[0][field])    \/\/ \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 `Intl.Collator`    const collator = isString ? strCollator : numCollator    \/\/ \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0443    return items.sort((a, b) => order.toLowerCase() === 'asc'        ? collator.compare(a[field], b[field])        : collator.compare(b[field], a[field])    )  } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418\u0442\u0430\u043a, \u0443 \u043d\u0430\u0441 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u043c\u0438 \u0438 <code>API<\/code> \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438. \u0412 \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043c\u044b \u0443\u0436\u0435 \u0443\u0431\u0435\u0434\u0438\u043b\u0438\u0441\u044c. \u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u0447\u0442\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a <code>API<\/code> \u0442\u0430\u043a\u0436\u0435 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e.<\/p>\n<p>  <\/p>\n<h2 id=\"rest-client\"><code>REST Client<\/code><\/h2>\n<p>  <\/p>\n<p>\u0422\u0440\u0435\u043c\u044f \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u043c\u0438 \u0440\u0435\u0448\u0435\u043d\u0438\u044f\u043c\u0438 \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f <code>API<\/code> \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/curl.se\/\"><code>curl<\/code><\/a> \u2014 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438<\/li>\n<li><a href=\"https:\/\/www.postman.com\/\"><code>postman<\/code><\/a> \u0438\u043b\u0438 <a href=\"https:\/\/insomnia.rest\/download\"><code>insomnia<\/code><\/a> \u2014 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b<\/li>\n<li><a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=humao.rest-client\"><code>REST Client<\/code><\/a> \u2014 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0434\u043b\u044f <code>VSCode<\/code><\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u042f \u043f\u043e\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>REST Client<\/code>, \u043e\u0434\u043d\u0430\u043a\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c, \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043b\u0435\u0433\u043a\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438 \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u0445.<\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 <code>REST Client<\/code> \u0432 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0444\u0430\u0439\u043b \u0441 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435\u043c <code>.http<\/code>, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>test.http<\/code> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044f:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">### ### todos ### GET http:\/\/localhost:5000\/api\/todos  ### GET http:\/\/localhost:5000\/api\/todos\/2  ### GET http:\/\/localhost:5000\/api\/todos\/5  ### GET http:\/\/localhost:5000\/api\/todos?text=Sleep  ### GET http:\/\/localhost:5000\/api\/todos?text=Test  ### GET http:\/\/localhost:5000\/api\/todos?done=true  ### POST http:\/\/localhost:5000\/api\/todos content-type: application\/json  {  \"id\": \"5\",  \"text\": \"Test\",  \"done\": false,  \"edit\": false }  ### POST http:\/\/localhost:5000\/api\/todos content-type: application\/json  [  {    \"id\": \"6\",    \"text\": \"Test2\",    \"done\": false,    \"edit\": false  },  {    \"id\": \"7\",    \"text\": \"Test3\",    \"done\": true,    \"edit\": false  } ]  ### PUT http:\/\/localhost:5000\/api\/todos\/2 content-type: application\/json  {  \"text\": \"Test\",  \"done\": false }  ### DELETE http:\/\/localhost:5000\/api\/todos\/5  ### ### query ### GET http:\/\/localhost:5000\/api\/todos?limit=2  ### GET http:\/\/localhost:5000\/api\/todos?offset=2&amp;limit=1  ### GET http:\/\/localhost:5000\/api\/todos?offset=3&amp;limit=2  ### GET http:\/\/localhost:5000\/api\/todos?sort=id&amp;order=desc  ### GET http:\/\/localhost:5000\/api\/todos?sort=title&amp;order=desc  ### GET http:\/\/localhost:5000\/api\/todos?sort=text  ### GET http:\/\/localhost:5000\/api\/todos?sort=text&amp;order=desc&amp;offset=1&amp;limit=2  ### ### data\/todos ### GET http:\/\/localhost:5000\/api\/data\/todos  ### GET http:\/\/localhost:5000\/api\/data\/todos\/2  ### GET http:\/\/localhost:5000\/api\/data\/todos\/5  ### GET http:\/\/localhost:5000\/api\/data\/todos?text=Sleep  ### GET http:\/\/localhost:5000\/api\/data\/todos?text=Test  ### GET http:\/\/localhost:5000\/api\/data\/todos?done=false  ### POST http:\/\/localhost:5000\/api\/data\/todos content-type: application\/json  {  \"id\": \"5\",  \"text\": \"Test\",  \"done\": false,  \"edit\": false }  ### POST http:\/\/localhost:5000\/api\/data\/todos content-type: application\/json  [  {    \"id\": \"6\",    \"text\": \"Test2\",    \"done\": true,    \"edit\": false  },  {    \"id\": \"7\",    \"text\": \"Test3\",    \"done\": false,    \"edit\": false  } ]  ### PUT http:\/\/localhost:5000\/api\/data\/todos\/3 content-type: application\/json  {  \"text\": \"Test\",  \"done\": true }  ### DELETE http:\/\/localhost:5000\/api\/data\/todos\/7<\/code><\/pre>\n<p>  <\/p>\n<p>\u0417\u0434\u0435\u0441\u044c:<\/p>\n<p>  <\/p>\n<ul>\n<li><code>###<\/code> \u2014 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u0432<\/li>\n<li><code>GET<\/code>, <code>POST<\/code> \u0438 \u0442.\u0434. \u2014 \u043c\u0435\u0442\u043e\u0434 \u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/li>\n<li><code>content-type: application\/json<\/code> \u2014 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/li>\n<li><code>[ ... ]<\/code> \u0438\u043b\u0438 <code>{ ... }<\/code> \u2014 \u0442\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430<\/li>\n<li>\u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0438 \u0442\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0434\u043e\u043b\u0436\u043d\u044b \u0440\u0430\u0437\u0434\u0435\u043b\u044f\u0442\u044c\u0441\u044f \u043f\u0443\u0441\u0442\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u043e\u0439<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041d\u0430\u0434 \u043a\u0430\u0436\u0434\u044b\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u0432 <code>VSCode<\/code> \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u043a\u043d\u043e\u043f\u043a\u0430 \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/xf\/xm\/f4\/xfxmf4ssyzdygi4hqpg1web-kxc.png\" data-src=\"https:\/\/habrastorage.org\/webt\/xf\/xm\/f4\/xfxmf4ssyzdygi4hqpg1web-kxc.png\"\/>  <\/p>\n<p>  \u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f <code>API<\/code> \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u0432\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430: <code>todos<\/code> \u0438 <code>data\/todos<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0412\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u043f\u0430\u0440\u043e\u0447\u043a\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432.<\/p>\n<p>  <\/p>\n<p><code>GET-\u0437\u0430\u043f\u0440\u043e\u0441<\/code> \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 <code>todos<\/code>:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/xz\/9j\/-q\/xz9j-q3dqazj9tyjbrttn9hljjq.png\" data-src=\"https:\/\/habrastorage.org\/webt\/xz\/9j\/-q\/xz9j-q3dqazj9tyjbrttn9hljjq.png\"\/>  <\/p>\n<p>  <code>GET-\u0437\u0430\u043f\u0440\u043e\u0441<\/code> \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0441 \u0442\u0435\u043a\u0441\u0442\u043e\u043c <code>Sleep<\/code> \u0438\u0437 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 <code>todos<\/code> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u0442\u0440\u043e\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 (\u0442\u0430\u043a\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u2014 <code>GET http:\/\/localhost:5000\/api\/todos\/Sleep<\/code>):<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/hc\/qx\/me\/hcqxmebxxk8qsrrraa6ivx-95-0.png\" data-src=\"https:\/\/habrastorage.org\/webt\/hc\/qx\/me\/hcqxmebxxk8qsrrraa6ivx-95-0.png\"\/>  <\/p>\n<p>  <code>POST-\u0437\u0430\u043f\u0440\u043e\u0441<\/code> \u043d\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442 <code>todos<\/code>:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/mg\/ma\/lj\/mgmaljygogo8xdfqfcjkftg2ybi.png\" data-src=\"https:\/\/habrastorage.org\/webt\/mg\/ma\/lj\/mgmaljygogo8xdfqfcjkftg2ybi.png\"\/>  <\/p>\n<p>  <code>GET-\u0437\u0430\u043f\u0440\u043e\u0441<\/code> \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0432\u0442\u043e\u0440\u043e\u0439 \u0438 \u0442\u0440\u0435\u0442\u044c\u0435\u0439 \u0437\u0430\u0434\u0430\u0447 \u0438\u0437 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 <code>todos<\/code>, \u043e\u0442\u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043f\u043e \u043f\u043e\u043b\u044e <code>text<\/code> \u043f\u043e \u0443\u0431\u044b\u0432\u0430\u043d\u0438\u044e:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/_g\/92\/la\/_g92laa3vqc0shq0j7yufcgv1dc.png\" data-src=\"https:\/\/habrastorage.org\/webt\/_g\/92\/la\/_g92laa3vqc0shq0j7yufcgv1dc.png\"\/>  <\/p>\n<p>  \u0418 \u0442.\u0434.<\/p>\n<p>  <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em>, \u0447\u0442\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u0434\u043d\u0438\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c \u043d\u0435 \u0432\u043b\u0438\u044f\u044e\u0442 \u043d\u0430 \u0434\u0440\u0443\u0433\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u044b.<\/p>\n<p>  <\/p>\n<p>\u0412\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e, \u0438\u0437\u0443\u0447\u0438\u0442\u0435 \u043e\u0442\u0432\u0435\u0442\u044b \u0438 \u043c\u044b\u0441\u043b\u0435\u043d\u043d\u043e \u0441\u0432\u044f\u0436\u0438\u0442\u0435 \u0438\u0445 \u0441 \u0440\u043e\u0443\u0442\u0430\u043c\u0438, \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u043c\u0438 \u0432 <code>api.routes.js<\/code>.<\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u0436\u0430\u043b\u0443\u0439, \u044d\u0442\u043e \u0432\u0441\u0435, \u0447\u0435\u043c \u044f \u0445\u043e\u0442\u0435\u043b \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441 \u0432\u0430\u043c\u0438 \u0432 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435.<\/p>\n<p>  <\/p>\n<p>\u0411\u0443\u0434\u0443 \u0440\u0430\u0434 \u043b\u044e\u0431\u043e\u0439 \u0444\u043e\u0440\u043c\u0435 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0439 \u0441\u0432\u044f\u0437\u0438.<\/p>\n<p>  <\/p>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0438 \u0445\u043e\u0440\u043e\u0448\u0435\u0433\u043e \u0434\u043d\u044f!<\/p>\n<p>  <\/p>\n<hr\/>\n<p>  <\/p>\n<blockquote><p><a href=\"https:\/\/t.me\/timewebru\"><b>\u041d\u043e\u0432\u043e\u0441\u0442\u0438, \u043e\u0431\u0437\u043e\u0440\u044b \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432 \u0438 \u043a\u043e\u043d\u043a\u0443\u0440\u0441\u044b \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b Timeweb.Cloud \u2014 \u0432 \u043d\u0430\u0448\u0435\u043c Telegram-\u043a\u0430\u043d\u0430\u043b\u0435<\/b><\/a> \u21a9<\/p><\/blockquote>\n<p><a href=\"https:\/\/timeweb.cloud\/?i=106909\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/b5\/pj\/of\/b5pjofdoxth14ro-rjsrn7sbmiy.png\" data-src=\"https:\/\/habrastorage.org\/webt\/b5\/pj\/of\/b5pjofdoxth14ro-rjsrn7sbmiy.png\"\/><\/a><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/583588\/\"> https:\/\/habr.com\/ru\/articles\/583588\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/4-\/hn\/8w\/4-hn8wkkaeafijehpfybkgcounq.png\" data-src=\"https:\/\/habrastorage.org\/webt\/4-\/hn\/8w\/4-hn8wkkaeafijehpfybkgcounq.png\"\/>  <\/p>\n<p>  \u041f\u0440\u0438\u0432\u0435\u0442, \u0434\u0440\u0443\u0437\u044c\u044f!<\/p>\n<p>  <\/p>\n<p>\u0412 \u044d\u0442\u043e\u043c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u043c \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0435 \u044f \u0445\u043e\u0447\u0443 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0432\u0430\u043c, \u043a\u0430\u043a \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439, \u043d\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e-\u0442\u0430\u043a\u0438 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f <code>API<\/code>.<\/p>\n<p>  <\/p>\n<p>\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0430\u0434\u043c\u0438\u043d\u043a\u0430 \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 (\u0434\u0430\u043b\u0435\u0435 \u2014 \u043f\u0440\u043e\u0435\u043a\u0442\u044b) \u043f\u0443\u0442\u0435\u043c \u0438\u0445 \u043d\u0430\u0431\u043e\u0440\u0430 (\u0432\u0432\u043e\u0434\u0430) \u0438\u043b\u0438 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\/\u0432\u0441\u0442\u0430\u0432\u043a\u0438, \u043b\u0438\u0431\u043e \u043f\u0443\u0442\u0435\u043c \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u0430;<\/li>\n<li>\u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435;<\/li>\n<li>\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c, \u0447\u0442\u0435\u043d\u0438\u0435 \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0444\u0430\u0439\u043b\u043e\u0432 \u043d\u0430 \u043b\u044e\u0431\u043e\u043c \u0443\u0440\u043e\u0432\u043d\u0435 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438;<\/li>\n<li>\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0439 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u0438 \u0438\u0445 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 \u0430\u0434\u043c\u0438\u043d\u043a\u0435;<\/li>\n<li>\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432;<\/li>\n<li>\u0443\u043d\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 <code>GET<\/code>, <code>POST<\/code>, <code>PUT<\/code> \u0438 <code>DELETE<\/code> \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u043b\u044e\u0431\u043e\u043c\u0443 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u043c\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0443, \u0432\u043a\u043b\u044e\u0447\u0430\u044f <code>GET-\u0437\u0430\u043f\u0440\u043e\u0441\u044b<\/code>, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438 \u0441\u0442\u0440\u043e\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430;<\/li>\n<li>\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0441\u0442\u0440\u043e\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 <code>sort<\/code>, <code>order<\/code>, <code>limit<\/code> \u0438 <code>offset<\/code>;<\/li>\n<li>\u0438 \u043c\u043d\u043e\u0433\u043e\u0435 \u0434\u0440\u0443\u0433\u043e\u0435.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041d\u0430\u0448\u0430 \u0430\u0434\u043c\u0438\u043d\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/z_\/bm\/ge\/z_bmgez08qrefh2udyodneqniqi.png\" data-src=\"https:\/\/habrastorage.org\/webt\/z_\/bm\/ge\/z_bmgez08qrefh2udyodneqniqi.png\"\/>  <\/p>\n<p>  \u0414\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0439 \u0441\u0442\u0438\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f <a href=\"https:\/\/getbootstrap.com\/\"><code>Bootstrap<\/code><\/a>.<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/github.com\/harryheman\/Blog-Posts\/tree\/master\/node-mock-api\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a>.<\/p>\n<p>  <\/p>\n<p>\u0420\u0430\u0437\u0443\u043c\u0435\u0435\u0442\u0441\u044f, \u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u044b \u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c, \u0441\u0440\u0430\u0437\u0443 \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d \u043d\u0435 \u043f\u043e\u0439\u0434\u0435\u0448\u044c, \u043d\u043e \u043f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043e\u0432\u0435\u0441\u0442\u0438 \u0435\u0433\u043e \u0434\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f \u043d\u0435 \u0441\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u0442\u0440\u0443\u0434\u0430.<\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u044b \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f 2 \u0432\u0430\u0436\u043d\u044b\u0445 \u0443\u0441\u043b\u043e\u0432\u0438\u0439:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0444\u043e\u0440\u043c\u0430\u0442 \u0434\u0430\u043d\u043d\u044b\u0445 \u2014 <code>JSON<\/code>;<\/li>\n<li>\u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0444\u043e\u0440\u043c\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u2014 \u043c\u0430\u0441\u0441\u0438\u0432.<\/li>\n<\/ul>\n<p>  <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em>: \u0441\u0442\u0430\u0442\u044c\u044f \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u0430, \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043d\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0449\u0438\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432, \u0445\u043e\u0442\u044f, \u0441\u043c\u0435\u044e \u043d\u0430\u0434\u0435\u044f\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0438 \u043e\u043f\u044b\u0442\u043d\u044b\u0435 \u043d\u0430\u0439\u0434\u0443\u0442 \u0432 \u043d\u0435\u0439 \u0447\u0442\u043e-\u043d\u0438\u0431\u0443\u0434\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435 \u0434\u043b\u044f \u0441\u0435\u0431\u044f.<\/p>\n<p>  <\/p>\n<p>\u0412\u044b \u0433\u043e\u0442\u043e\u0432\u044b? \u0422\u043e\u0433\u0434\u0430 \u0432\u043f\u0435\u0440\u0435\u0434.<\/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-415958","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/415958","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=415958"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/415958\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=415958"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=415958"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=415958"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}