В този урок ще започнем нашето пътуване за създаване на REST API с NodeJS заедно с MongoDB база данни. ако нямате опит с Nodejs и MongoDB преди

Вземете този курс, който ви помага да започнете бързо





Защо този урок?

Когато започнах да се уча да програмирам, търсех решение на проблемите си и го намерих. Но проблемът беше, че не знаех защо нещо работи, когато беше и защо не. Трябваше да копирам изходния код на друг и да го използвам, без да знам защо е направено нещо.

Този урок ще ви помогне да преминете през целия шаблонен код, който можете да използвате, и да разберете всяка част от него.

Какво ще правим?

Ние ще направим уебсайт, много подобен на уебсайта Medium със стандарти REST. Ще използваме и следните функционалности:

  • Локално удостоверяване + JWT
  • Потребителят може да създаде публикация
  • Потребителят може да изтрие собствената си публикация и да я актуализира
  • Потребителят може да следва публикацията на друг потребител
  • Потребителят получава известие за публикация, направена от потребител, който той следва
  • Потребителят може да хареса публикация
  • Потребителят може да види списък с всички позиции, които е харесал

Звучи интересно, нали? Нека да видим какво ще използваме, за да направим това прекрасно приложение.

Технически стек за приложението

Ще използваме Javascript, ES6 + ES7 и ще компилираме нашия източник с помощта на babel и Webpack v2. Трябва да сте запознати и с обещанията на JS и работа с async.

За базата данни ще използваме MongoDB.

Целият код в Github ТУК

Настройване на инструментите

За да започнем с част 1 от тази серия, ние ще настроим нашата среда със следните инструменти:

  • Editorconfig
  • Експрес
  • Еслинт
  • Бабел
  • Webpack 2

След като завършим тази публикация, ще имаме готов и работещ прост Express сървър. Да започваме!

Просто създайте нова директория за вашия проект. Нарекох го makenodejsrestapi. Ще използвам пакета yarn, за да инсталирам моите инструменти. Вътре в директорията първо създаваме нов файл, наречен „.gitignore“ и добавяме следното съдържание:

node_modules/

Сега ще инициализираме нашия проект, като изпълним следната команда:

yarn init

Ще ви бъдат задавани различни въпроси, за които просто натискам клавиша Enter и оставям yarn да приеме стойности по подразбиране. След като командата приключи работата си, ще видите нов файл в директорията на проекта, наречен package.json със следното съдържание:

{ 
 “name”: “makenodejsrestapi”, 
 “version”: “1.0.0”, 
 “main”: “index.js”, 
 “license”: “MIT” 
}

Този файл съдържа само метаданните за нашия проект. След това ще започнем да добавяме експреса в нашия проект. Изпълнете следната команда:

yarn add express

Ако този пакет не бъде намерен първоначално, преждата ще отнеме известно време, за да го намери, но със сигурност ще стане. След като командата приключи, нашият package.json ще бъде актуализиран със следното съдържание:

След това създаваме нова директория в нашия проект с име src и създаваме нов файл с име index.js файл в нея. Поставете следното съдържание в него:

import express from 'express';
 const app = express();
 const PORT = process.env.PORT || 3000;
 app.listen(PORT, err => {
     if (err) {
         throw err;
     } else {
         console.log(Server running on port: $ {
             PORT
         }-- - Running on $ {
             process.env.NODE_ENV
         }-- - Make something great!)
     }
 });

Обърнете внимание, че използваме порт 3000, ако портът не е зададен в променливите на средата. Сега ще добавим „скрипт“ в нашия файл package.json, за да можем да използваме dev профил, докато изпълняваме нашия using babel. Ето модифицирания файл:

Сега инсталирайте cross-env с прежда с тази команда:

yarn add cross-env

Това е актуализираният файл package.json:

{
     "name": "makenodejsrestapi",
     "version": "1.0.0",
     "main": "index.js",
     "license": "MIT",
     "scripts": {
         "dev": "NODE_ENV=development node src/index.js"
     },
     "dependencies": {
         "cross-env": "^5.1.3",
         "express": "^4.16.2"
     }
 }

Сега можем да добавим зависимости към babel с тази команда:

yarn add -D babel-preset-env babel-plugin-transform-object-rest-spread

След като изпълните командата, можете да създадете файл, наречен .babelrc, в който можете да предоставите информация за средата и плъгина за приложението. Ето какво ще направим след това:

{
     "presets": [
         ["env", {
             "targets": {
                 "node": "current"
             }
         }]
     ],
     "plugins": [
         ["transform-object-rest-spread", {
             "useBuiltIns": true
         }]
     ]
 }

Плъгинът transform-object-rest-spread се използва за трансформиране на свойствата на останалите за присвояване на деструктуриране на обекти. Сега ще използваме и webpack 2:

yarn add -D webpack babel-core babel-loader webpack-node-externals

Накрая ще конфигурираме уебпакета, както и добавихме неговите зависимости по-горе:

const nodeExternals = require('webpack-node-externals');
 const path = require('path');
 module.exports = {
     target: 'node',
     externals: [nodeExternals()],
     entry: {
         'index': './src/index.js'
     },
     output: {
         path: path.join(__dirname, 'dist'),
         filename: '[name].bundle.js',
         libraryTarget: 'commonjs2',
     },
     module: {
         rules: [{
             test: /\.js$/,
             exclude: /node_modules/,
             use: 'babel-loader'
         }]
     }
 }

Сега изпълняваме и нашия скрипт package.json:

"scripts": { "dev:build": "webpack -w", "dev": "cross-env NODE_ENV=development node dist/index.bundle.js" }

Накрая можем да стартираме нашето приложение:

За да го разгледаме графично, ето изхода, когато стартираме компилацията:

Имайте предвид, че изпълнихме две команди по-горе:

  • Първата команда току-що изгради приложението и подготви изграждането на babel
  • Втората команда всъщност изпълнява заявката и можете да видите изхода в конзолата

Сега най-накрая ще инсталираме и ES Lint:

yarn add -D eslint eslint-config-equimper

Сега направете нов файл, наречен „.eslintrc“ и добавете следното съдържание:

{ “extends” : “equimper” }

След като направите това, ще започнете да получавате предупреждения, ако не следвате правилните ES стандарти. Този инструмент е много полезен, когато трябва да следвате стриктни конвенции за вашия проект.

Някои интересни API на Node от Udemy



Следващ Какво ще добавим сега?

В този раздел ще настроим повече инструменти, необходими за задната част на това приложение:

  • Добавете mongoose, body-parser, morgan, компресия, шлем
  • Конфигурационна папка за настройка
  • Константи за настройка

Добавяне на Mongoose

За да добавите mongoose и други споменати модули към вашето приложение, изпълнете следната команда:

yarn add mongoose body-parser компресионна каска && yarn add -D morgan

Трябва да се отбележи, че в реда, в който посочим модулите, те ще бъдат изтеглени в същия ред.

Само за да се уверя, че сме на една и съща линия, ето как изглежда моят файл package.json:

Сега ще компилираме нашия проект отново с тази команда:

разработка на прежда

Просто се уверете, че проектът все още работи. Сега направете нова конфигурационна папка в папката src и създайте файл с име като constants.js със следното съдържание:

const devConfig = {};
const testConfig = {};
const prodConfig = {};
const defaultConfig = {
PORT: process.env.PORT || 3000,
};
function envConfig(env) {
     switch (env) {
         case 'development':
             return devConfig;
         case 'test':
             return testConfig;
         default:
             return prodConfig;
     }
 }
 
 //Take defaultConfig and make it a single object 
 //So, we have concatenated two objects into one 
 export default { ...defaultConfig,
     ...envConfig(process.env.NODE_ENV),
 };

Сега, като се върнем към файла index.js, ще добавим зависимост за този файл с константи и ще променим препратките към PORT, за да използваме този файл като:

import express from 'express';
import constants from './config/constants';
const app = express();
app.listen(constants.PORT, err => {
    if (err) {
        throw err;
    } else {
        console.log(`Server running on port: ${constants.PORT} --- Running on ${process.env.NODE_ENV} --- Make something great.!`)
    }
});

Сега направете нов файл с име като database.js в конфигурационната папка със следното съдържание:

import mongoose from 'mongoose';
 import constants from './constants';
 
 //Removes the warning with promises 
 mongoose.Promise = global.Promise;
 
 //Connect the db with the url provided 
 try {
     mongoose.connect(constants.MONGO_URL)
 } catch (err) {
     mongoose.createConnection(constants.MONGO_URL)
 }
 mongoose.connection.once('open', () => console.log('MongoDB Running')).on('error', e => {
     throw e;
 })

Също така променихме конфигурацията за mongoose връзка в нашия файл constants.js като:

const devConfig = { MONGO_URL: 'mongodb://localhost/makeanodejsapi-dev', }; 
 const testConfig = { MONGO_URL: 'mongodb://localhost/makeanodejsapi-test', }; 
 const prodConfig = { MONGO_URL: 'mongodb://localhost/makeanodejsapi-prod', };

Това ще гарантира, че използваната база данни е различна, когато стартираме нашето приложение с различни профили и среди. Можете да продължите и да стартирате това приложение отново.

Когато имате база данни, работеща на споменатия порт, можете успешно да започнете да използвате вашето приложение.

Проектиране на междинния софтуер

Сега ще започнем да правим междинния софтуер на приложението.

В папката config създайте нов файл и го наименувайте като middleware.js със следното съдържание:

import morgan from 'morgan';
 import bodyParser from 'body-parser';
 import compression from 'compression';
 import helmet from 'helmet';
 import {
     isPrimitive
 } from 'util';
 const isDev = process.env.NODE_ENV === 'development';
 const isProd = process.env.NODE_ENV === 'production';
 export default app => {
     if (isProd) {
         app.use(compression());
         app.use(helmet());
     }
     app.use(bodyParser.json());
     app.use(bodyParser.urlencoded({
         extended: true
     }));
     if (isDev) {
         app.use(morgan('dev'));
     }
 };

За да използвате тази конфигурация, добавете и импортирането към индексния файл, като:

import express from 'express';
 import constants from './config/constants';
 import './config/database';
 import middlewareConfig from './config/middleware';
 const app = express(); //passing the app instance to middlewareConfig 
 
 middlewareConfig(app);
 app.listen(constants.PORT, err => {
     if (err) {
         throw err;
     } else {
         console.log(`Server running on port: ${constants.PORT} --- Running on ${process.env.NODE_ENV} --- Make something great.!`)
     }
 });

Сега стартирайте вашето приложение и то трябва да обслужва GET заявка на порт 3000!

Някои интересни Node API от Udemy



Регистрирайте потребител

В този раздел ще използваме настройката на MongoDB, която направихме в последния урок, и ще изградим приложение оттам, което позволява на потребителя да се регистрира в нашето приложение. Само за да не пропуснете, последният ни файл package.json изглежда така:

В този раздел ще направим крачка напред, като създадем функционалност, позволяваща на потребител да се регистрира в нашето приложение. Ние също така ще направим потребителския модел, така че данните да могат да се записват в базата данни.

След като приключите с този раздел, ще имате поне следната файлова структура:

Просто следвайте урока как се получава това!

Дефиниране на модела

Ще започнем, като направим нашия потребителски модел. За да направите това, създайте нов файл в src › modules › users и го наименувайте като user.model.js със следното съдържание:

import mongoose, {
     Schema
 } from 'mongoose';
 import validator from 'validator';
 import {
     passwordReg
 } from './user.validations';
 const UserSchema = new Schema({
     email: {
         type: String,
         unique: true,
         required: [true, 'Email is required!'],
         trim: true,
         validate: {
             validator(email) {
                 return validator.isEmail(email);
             },
             message: '{VALUE} is not a valid email!',
         },
     },
     firstName: {
         type: String,
         required: [true, 'FirstName is required!'],
         trim: true,
     },
     lastName: {
         type: String,
         required: [true, 'LastName is required!'],
         trim: true,
     },
     userName: {
         type: String,
         required: [true, 'UserName is required!'],
         trim: true,
         unique: true,
     },
     password: {
         type: String,
         required: [true, 'Password is required!'],
         trim: true,
         minlength: [6, 'Password need to be longer!'],
         validate: {
             validator(password) {
                 return passwordReg.test(password);
             },
             message: '{VALUE} is not a valid password!',
         },
     },
 });
 export default mongoose.model('User', UserSchema);

Току-що дефинирахме схемата на нашия потребителски модел с различни свойства като:

  • Дефинирани свойства за потребител
  • Също така предостави мета-информация за свойствата на техните типове, тяхната уникалност и как тези данни трябва да бъдат валидирани
  • Обърнете внимание как предоставихме и функция за валидиране. Това прави нещата много лесни, когато вмъкваме данните в колекциите на Mongo

Дефиниране на контролера

Сега ще използваме потребителския модел, като го използваме в дефиницията на контролера. Създайте нов файл в src › modules › users и го наименувайте като user.controllers.js със следното съдържание:

import User from './user.model';
 export async function signUp(req, res) {
     try {
         const user = await User.create(req.body);
         return res.status(201).json(user);
     } catch (e) {
         return res.status(500).json(e);
     }
 }

Току-що дефинирахме функция signUp с обект на заявка и отговор като параметри и я създадохме с помощта на потребителския модел, който току-що дефинирахме по-горе.

Ние също така върнахме подходящ отговор заедно с техните кодове, така че потребителят да може да бъде уведомен, ако транзакцията е била успешна.

Определяне на маршрути за приложение

Ние ще определим маршрути за нашето приложение, където можем да посочим картографирането, където потребителят трябва да посети, за да види приложението, което направихме. Създайте нов файл в src › modules › users и го наименувайте като user.routes.js със следното съдържание:

import {
     Router
 } from 'express';
 import * as userController from './user.controllers';
 const routes = new Router();
 routes.post('/signup', userController.signUp);
 export default routes;

Имайте предвид, че това няма да работи при. Трябва да дефинираме module index.js в папката modules със следното съдържание:

import userRoutes from './users/user.routes';
 export default app => {
     app.use('/api/v1/users', userRoutes);
 };

Сега можем да стартираме нашето приложение, което ще бъде действителната първа версия на нашето приложение. Просто трябва да направим окончателни промени в основния файл index.js сега:

Ето актуализираното съдържание:

/* eslint-disable no-console */
 import express from 'express';
 import constants from './config/constants';
 import './config/database';
 import middlewaresConfig from './config/middlewares';
 import apiRoutes from './modules';
 const app = express();
 middlewaresConfig(app);
 app.get('/', (req, res) => {
     res.send('Hello world!');
 });
 apiRoutes(app);
 app.listen(constants.PORT, err => {
     if (err) {
         throw err;
     } else {
         console.log(` Server running on port: ${constants.PORT} --- Running on ${process.env.NODE_ENV} --- Make something great `);
     }
 });

Сега, когато стартираме приложението, все още можем да видим, че нашето приложение работи:

Използване на MongoDB и Postman

Сега ще използваме два необходими инструмента:

  1. Robomongo: Изтеглете го тук. Това е страхотен инструмент за визуализиране на Mongo данни и заявки с него. Освен това е безплатно! Това е достъпно за всички OS платформи.
  2. Пощальон: Изтеглете го тук. Това е инструмент за натискане на API и получаване на отговор. Има страхотни функции за визуализация и можете да запазите и формат на заявка, което ви спестява много време. Отново е безплатно! Това е достъпно за всички OS платформи.

Когато отворите Robomongo и се свържете с вашия локален екземпляр на MongoDB, можете да видите наличната DB за нашата вече:

Вече имаме готова колекция, направена от нашето приложение, тя ще изглежда така:

Това ще бъде празно в момента, тъй като досега не сме създали никакви данни. Ще направим това наистина скоро!

Опит за регистрация на потребител

Нека отворим пощальона сега. Ще ударим API с този URL адрес:

http://localhost:3000/api/v1/users/signup

В пощальон ще изглежда нещо като:

Преди да използваме този API, ще опитаме версия на Hello World. Вижте какво се случва в този API:

Сега да се върнем към API за регистрация. Преди да направим успешно попадение, ще се опитаме да предоставим невалидни стойности и да видим с какви грешки се сблъскваме. В случай на невалиден имейл резултатът е следният:

Сега ще опитаме и с правилни данни. Да пробваме!

Е, историята все още не е приключила. Можем също да видим как данните се вмъкват в базата данни на MongoDB сега:

Отлично!

Някои интересни API на Node от Udemy



Добавяне на още валидации

Въпреки че вече добавихме някои проверки в потребителския модел, какво ще стане, ако искаме да запазим и в друг файл! За да направите това, създайте нов файл в src > modules > users и го наименувайте като user.validations.js със следното съдържание:

import Joi from 'joi';
 export const passwordReg = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}/;
 export default {
     signup: {
         email: Joi.string().email().required(),
         password: Joi.string().regex(passwordReg).required(),
         firstName: Joi.string().required(),
         lastName: Joi.string().required(),
         userName: Joi.string().required(),
     },
 };

След това добавете и тази проверка във файла на рутера:

import {
       Router
   } from 'express';
   import validate   from 'express-validation';
   import * as   userController from './user.controllers';
   import   userValidation from './user.validations';
   const routes = new Router();
   routes.post('/signup', validate(userValidation.signup),   userController.signUp);
   export default   routes;

Забележи това:

  • Добавихме импортиране за валидации от експрес
  • Предадохме и нова функция за валидиране на потребители за регистрация

Сега, когато се опитваме потребител да се регистрира със същите идентификационни данни, получаваме грешка:

Това всъщност не беше заради проверката, а защото се опитваме да вмъкнем друг потребител със същия имейл. Нека опитаме нещо различно:

Сега можем да коригираме това и да видим данните, които се появяват в базата данни MongoDB:

Отлично! Успяхме да добавим мощни валидации към нашия проект.

Шифроване на парола и потребителско влизане

Ще работим повече с нашия потребител. В последния ни урок успяхме да запазим нов потребител. Основният проблем с този подход беше, че паролата на потребителя беше запазена като обикновен текст. Това ще бъде една от промените, които ще направим в нашето приложение сега.

Само за да не пропуснете, последният ни файл package.json изглежда така:

В този урок ще направим крачка напред, като създадем функционалност за криптиране на потребителска парола. Освен това, ние също ще направим промени като:

  • Добавете rimraf и clean dist към изграждането на webpack
  • Шифроване на паролата на потребителя
  • Създайте местна стратегия с паспорт
  • Разрешаване на потребителско влизане

Добавяне на зависимост от rimraf

Ще започнем с добавяне на зависимост от rimraf в нашия проект, като използваме следната команда:

yarn add -D rimraf

Само за да изградите проекта си отново, изпълнете следната команда:

yarn

Сега нека добавим и rimraf към нашия файл package.json:

“scripts”: {
 “clean”: “rimraf dist”,
 “dev:build”: “yarn run clean && webpack -w”,
 “dev”: “cross-env NODE_ENV=development nodemon dist/index.bundle.js”
}

Сега изпълнете следната команда:

yarn dev:build

Когато изпълните тази команда, папката dist ще се обнови и ще се върне, след като процесът на изграждане приключи:

Библиотека за шифроване на парола

Сега ще добавим библиотека към нашия проект, за да можем да шифроваме паролата на потребителя, преди да бъде запазена в базата данни. По този начин можем да гарантираме, че е безопасно, дори ако база данни е хакната.

Изпълнете следната команда:

yarn add bcrypt-nodejs

По този начин библиотеката ще бъде добавена към нашия проект.

Модифициране на модела

Сега ще трябва да модифицираме нашия модел, така че в него да може да бъде зададена криптирана парола, когато в заявката се появи парола с обикновен текст. Добавете следното в user.model.js

UserSchema.pre('save', function(next) {
     if (this.isModified('password')) {
         this.password = this._hashPassword(this.password);
     }
     return next();
 });
 UserSchema.methods = {
     _hashPassword(password) {
         return hashSync(password);
     },
     authenticateUser(password) {
         return compareSync(password, this.password);
     },
 };

В горния код това се отнася до текущия ни потребител, споменат в заявката. Освен това authenticateUser се извиква веднага щом се опитаме да влезем и потребителят предава парола, която е обикновен текст. ние хешираме тази парола и едва след това ще я сравним със стойността в нашата база данни.

Сега нека се опитаме да направим нова заявка и да проверим дали това работи. Ето моята молба:

Когато пусна тази заявка, това е отговорът, който получаваме:

Нека сега проверим базата данни, ще видим подобна сцена и там:

Сега ще бъдем API за вход за нашето приложение.

Използване на паспорт за влизане

Ще използваме библиотека, известна като Passport. Вие сте свободни да използвате всяка друга библиотека за удостоверяване като Facebook, Google и др.

За да продължим, трябва да добавим две библиотеки към нашия проект. Нека направим това, като изпълним следната команда:

yarn add passport passport-local

След като това е направено, нека създадем нова папка в папката src, известна като услуги. Ще създадем нов файл с име auth.services.js в папката services със следното съдържание:

 import passport from 'passport';
 import LocalStrategy from 'passport-local';
 import User from '../modules/users/user.model';
 const localOpts = {
     usernameField: 'email',
 };
 const localStrategy = new LocalStrategy(localOpts, async (email, password, done) => {
     try {
         const user = await User.findOne({
             email
         });
         if (!user) {
             return done(null, false);
         } else if (!user.authenticateUser(password)) {
             return done(null, false);
         }
         return done(null, user);
     } catch (e) {
         return done(e, false);
     }
 });
 passport.use(localStrategy);
 export const authLocal = passport.authenticate('local', {
     session: false
 });

Тук изпробвахме локална стратегия, която ще бъде асинхронна по природа и данните се изпращат до паспортната библиотека като имейл и парола на нашия потребител. След това библиотеката ще удостовери потребителя и ще върне отговора.

Ще добавим и Passport като наш междинен софтуер. Ето модифицирания файл:

 import morgan from 'morgan';
 import bodyParser from 'body-parser';
 import compression from 'compression';
 import helmet from 'helmet';
 import passport from 'passport';
 
 const isDev = process.env.NODE_ENV === 'development';
 const isProd = process.env.NODE_ENV === 'production';
 
 export default app => {
     if (isProd) {
         app.use(compression());
         app.use(helmet());
     }
     app.use(bodyParser.json());
 
     app.use(bodyParser.urlencoded({
         extended: true
     }));
     app.use(passport.initialize());
 
     if (isDev) {
         app.use(morgan('dev'));
     }
 };

Тук инициализирахме библиотеката на Passport с нашето приложение.

Добавяне на вход към контролера

Време е да добавим и функцията за влизане към нашия контролен слой. Добавете следната функция към нашия контролер:

export function login(req, res, next) {
 res.status(200).json(req.user);
 return next();
}

Имайте предвид, че така изглежда нашият окончателен контролен файл:

Предоставяне на маршрут за влизане

Ще трябва да предоставим и маршрут за API за влизане. Ще направим тази промяна във файл user.routes.js. Добавете този маршрут към файла заедно с импортирането:

import {
 authLocal
} from ‘../../services/auth.services’;
routes.post(‘/login’, authLocal, userController.login);

Ето как изглежда нашият окончателен файл:

Изпробване на функцията за влизане

Сега ще изпробваме следния POST API с идентификационните данни, които създадохме преди това:

http://localhost:3000/api/v1/users/login

Когато идентификационните данни са правилни, се случва следното:

Не е ли страхотно? Успяхме да влезем в съществуващ потребител и също така успяхме да защитим паролата му, като я шифровахме.

Добавяне на JWT удостоверяване

Досега можем да регистрираме нов потребител в нашето приложение:

Също така можем да позволим на потребител да влезе в нашето приложение:

Преди да разберем какво ще правим в тази публикация, нека най-накрая да видим как изглежда текущият ни файл package.json:

В този раздел ще добавим следните функции:

  • Ние ще приложим JWT удостоверяване и ще добавим тайна парола
  • Добавете новата библиотека passport-jwt
  • Добавете библиотека jsonwebtoken
  • Изпращане само на задължителни полета като отговор в JSON

Как JSON уеб токенът съхранява данни?

Когато предоставяме данни за криптиране заедно със секретна парола, те се криптират, за да образуват различни части от JWT токен, като например:

Както е показано по-горе, един токен може да съдържа потребителска самоличност и други данни, свързани с него.

Добавяне на JWT тайна

Нека да преминем към нашия constants.js файл и да добавим JWT тайна тук във вече наличната конфигурация на разработчиците:

const devConfig = {
 MONGO_URL: ‘mongodb://localhost/makeanodejsapi-dev’,
 JWT_SECRET: ‘thisisasecret’,
};

След това ще инсталираме две библиотеки, като използваме следната команда:

yarn add jsonwebtoken passport-jwt

Сега преминете към файла с услуги за удостоверяване и услугата JWT във файла с този ред:

import { Strategy as JWTStrategy, ExtractJwt } from ‘passport-jwt’;
import User from ‘../modules/users/user.model’;
import constants from ‘../config/constants’;

След това оставете passport да използва определената стратегия:

// Jwt strategy
 const jwtOpts = {
   jwtFromRequest: ExtractJwt.fromAuthHeader('authorization'),
   secretOrKey: constants.JWT_SECRET,
 };
 
 const jwtStrategy = new JWTStrategy(jwtOpts, async (payload, done) => {
   try {
     //Identify user by ID
     const user = await User.findById(payload._id);
 
     if (!user) {
       return done(null, false);
     }
     return done(null, user);
   } catch (e) {
     return done(e, false);
   }
 });
 
 passport.use(localStrategy);
 passport.use(jwtStrategy);
 
 export const authLocal = passport.authenticate('local', { session: false });
 export const authJwt = passport.authenticate('jwt', { session: false });

За да проверим дали това работи, сега ще използваме частен маршрут в нашия JS файл за маршрут. Крайното съдържание на файла ще изглежда така:

import userRoutes from ‘./users/user.routes’;
import { authJwt } from ‘../services/auth.services’;
export default app => {
 app.use(‘/api/v1/users’, userRoutes);
 app.get(‘/hello’, authJwt, (req, res) => {
 res.send(‘This is a private route!!!!’);
 });
};

Проверка на JWT

Нека опитаме това и да проверим дали JWT работи в Postman сега:

Сега трябва да добавим JWT токен в заявка, която принадлежи само на определен потребител.

Ще добавим функционалност към модела на потребителя, така че той да съдържа и JWT токена, когато потребител влезе. Така че, нека добавим още библиотеки към JS файла на потребителския модел:

import jwt from ‘jsonwebtoken’;
import constants from ‘../../config/constants’;

Сега можем да дешифрираме токен и да получим потребителска информация.

Създаване на JWT токен

Също така ще трябва да създадем метод, който създава токен за потребителя. Нека добавим този метод сега:

UserSchema.methods   = {
   
     createToken() {
       return jwt.sign(
         {
           _id: this._id,
         },
         constants.JWT_SECRET,
       );
     },
     toJSON() {
       return {
         _id: this._id,
         userName: this.userName,
         token: `JWT ${this.createToken()}`,
       };
     },
   };

Използването на метода toJSON() също е важно. Добавихме JWT пред токен, тъй като паспортната библиотека го използва, за да идентифицира JWT токена.

Сега нека опитаме отново да регистрираме потребител:

Този път дори получихме JWT токен в отговор. Този токен ще съдържа и потребителския идентификатор и потребителското име. Сега имаме работещ пример за JWT!

Нека копираме JWT стойността и опитаме частния маршрут сега:

Създаване на публикация по асоцииране на потребител и обект

след това можем да регистрираме нов потребител в нашето приложение:

Също така можем да позволим на потребител да влезе в нашето приложение:

Преди да разберем какво ще правим в тази публикация, нека най-накрая да видим как изглежда текущият ни файл package.json:

В този раздел ще добавим следните функции:

  • Ще създадем нов ресурс за публикация. Сега потребителят може да създаде и публикация
  • Направете потребителя като автор на публикацията
  • Работете по някои проблеми, които създадохме в предишни публикации

Създаване на модел за публикация

Точно това, което направихме за потребителския модел, трябва да бъде направено и за пост модела, както и създаването на нова папка. До края на този урок ще следвате новата папка и файлове във вашия проект:

Ще започнем със създаването на модела Post. Ще включим и валидациите, от които се нуждаем. Нека добавим друга библиотека за уникално валидиране на mongoose:

yarn add mongoose-unique-validator

Ще добавим и нова библиотека Slug. За да направите това, инсталирайте го със следната команда:

yarn add slug

Ако се чудите какво е slugify, URL адресът на публикацията трябва да изглежда като заглавието на публикацията. Това изглежда добре и проблясъкът на публикацията също се вижда в нейния URL адрес, което е добра процедура.

Сега можем да добавим и тази библиотека. Нашият модел ще изглежда така:

import mongoose, { Schema } from 'mongoose';
   import slug from   'slug';
   import   uniqueValidator from 'mongoose-unique-validator';
   
   const   PostSchema = new   Schema({
     title: {
       type: String,
       trim: true,
       required: [true, 'Title   is required!'],
       minlength: [3, 'Title   need to be longer!'],
       unique: true,
     },
     text: {
       type: String,
       trim: true,
       required: [true, 'Text   is required!'],
       minlength: [10, 'Text   need to be longer!'],
     },
     slug: {
       type: String,
       trim: true,
       lowercase: true,
     },
     user: {
       type: Schema.Types.ObjectId,
       ref: 'User',
     },
     favoriteCount: {
       type: Number,
       default: 0,
     },
   }, { timestamps: true   });
   
   PostSchema.plugin(uniqueValidator, {
     message: '{VALUE} already taken!',
   });
   
   PostSchema.pre('validate', function (next) {
     this._slugify();
   
     next();
   });
   
   PostSchema.methods = {
     _slugify() {
       this.slug = slug(this.title);
     },
   };
   
   PostSchema.statics = {
     createPost(args, user) {
       return this.create({
         ...args,
         user,
       });
     },
   };
   
   export default   mongoose.model('Post', PostSchema);

Направихме следното в горния модел:

  • Дефинирани полета за модела Post
  • Добавена проверка за всяко поле
  • Добавено валидиране за цялостния обект Post
  • Слагаме публикацията по нейното заглавие и запазваме и тази стойност

В кода, показан по-горе, ще добавим метода createPost следващия в нашия контролер.

Създаване на Post Controller

Сега ще се нуждаем от контролер, така че потребителят действително да може да извършва операциите, свързани с публикация.

Въз основа на структурата на директорията, показана по-горе, дефинирайте нов файл post.controller.js файл в самия post модул със следното съдържание:

import Post from './post.model';
 
 export async function createPost(req, res) {
   try {
     const post = await Post.createPost(req.body, req.user._id);
     return res.status(201).json(post);
   } catch (e) {
     return res.status(400).json(e);
   }
 }

Връщаме подходящ отговор, когато се сблъскаме с грешка или сме успели успешно да създадем нова публикация.

Създаване на маршрут на публикация

Нека сега създадем маршрута до Post Controller в нашето приложение във файла с име post.route.js под модула posts със следното съдържание:

import { Router } from 'express';
 
 import * as postController from './post.controllers';
 import { authJwt } from '../../services/auth.services';
 
 const routes = new Router();
 
 routes.post(
   '/',
   authJwt,
 );
 
 export default routes;

Нека модифицираме и файла index.js за това. Крайното съдържание ще бъде:

import userRoutes from ‘./users/user.routes’;
import postRoutes from ‘./posts/post.routes’;
export default app => {
 app.use(‘/api/v1/users’, userRoutes);
 app.use(‘/api/v1/posts’, postRoutes);
};

Проверка на API за публикации

Сега ще опитаме POST API, за да създадем нова публикация.

За да започнете, опитайте да влезете като потребител, така че да получите JWT токен, за да натиснете API за създаване на публикация на този URL адрес:

http://localhost:3000/api/v1/posts

Ето какво опитахме и отговора:

Имаме също попълнени полета за дата и плужек. Това също съдържа потребителския идентификатор. Нека видим и тази публикация в MongoDB:

Ако отново използваме този API, за да създадем публикацията, тя ще се провали, тъй като заглавието вече е заето:

Това означава, че нашето валидиране също работи добре.

Определяне на заглавието като задължително

Можем да приложим повече валидации, като например да направим заглавието за публикация задължително.

Нека създадем нов файл с име post.validations.js в модула posts със следното съдържание:

import Joi from 'joi';
   
   export const   passwordReg = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}/;
   export default   {
     signup: {
       body: {
         email:   Joi.string().email().required(),
         password:   Joi.string().regex(passwordReg).required(),
         firstName:   Joi.string().required(),
         lastName:   Joi.string().required(),
         userName:   Joi.string().required(),
       },
     },
   };

Ще трябва да променим и файла с маршрути, за да включим тази проверка. Ето модифицирания файл:

import { Router } from 'express';
 import validate from 'express-validation';
 import * as postController from './post.controllers';
 import { authJwt } from '../../services/auth.services';
 import postValidation from './post.validations';
 
 const routes = new Router();
 routes.post(
   '/',
   authJwt,
   validate(postValidation.createPost),
   postController.createPost,
 );
 
 export default routes;

Успяхме да получим потребителския идентификатор от обекта authJwt, който използваме по-горе. Съобщението, което получаваме сега е:

Скоро ще променим отговора, за да бъде по-грациозен.

Получаване на данни по ID и попълване на обект в друг

след това можем да регистрираме нов потребител в нашето приложение:

Също така можем да позволим на потребител да влезе в нашето приложение:

Също така успяхме да създадем публикация, свързана и с потребител:

В този раздел ще добавим следните функции:

  • Ще получим публикация по нейния идентификатор
  • Ние също така ще създадем контролери и маршрут
  • Ще ви покажем как да попълните потребителска информация в публикация
  • Други библиотеки, които ще използваме

Добавяне на HTTP библиотека за състояние към контролера

За да добавите тази библиотека, изпълнете следната команда:

yarn add http-status

Сега можем да използваме тази библиотека и в нашия потребителски контролер. Нека започнем с импортирането на тази библиотека:

import HTTPStatus from ‘http-status’;

След това, вместо да използваме състояние като 200 и т.н. в нашия контролер, ние ще променим състоянието, предоставено от тази библиотека като:

export   async function signUp(req, res) {
     try {
       const user = await User.create(req.body);
       return res.status(HTTPStatus.CREATED).json(user.toAuthJSON());
     } catch (e) {
       return res.status(HTTPStatus.BAD_REQUEST).json(e);
     }
   }
   
   export function login(req, res, next) {
       res.status(HTTPStatus.OK).json(req.user.toAuthJSON());
     return next();
   }

Ще направим същото и в Post Controller:

import HTTPStatus from 'http-status';
   import Post from   './post.model';
   
   export async function createPost(req, res) {
     try {
       const post = await Post.createPost(req.body, req.user._id);
         return   res.status(HTTPStatus.CREATED).json(post);
     } catch (e) {
       return res.status(HTTPStatus.BAD_REQUEST).json(e);
     }
   }

Получаване на поща по ID

Ще дефинираме нова функция в Post Controller за получаване на публикация по ID:

export async function getPostById(req, res) {
   try {
     const post = await Post.findById(req.params.id);
     return res.status(HTTPStatus.OK).json(post);
   } catch (e) {
     return res.status(HTTPStatus.BAD_REQUEST).json(e);
   }
 }

Нека преминем към дефиниране на маршрута за тази функция:

routes.get(‘/:id’, postController.getPostById);

Тъй като имаме следната публикация в нашата база данни Mongo:

Ще получим тази публикация чрез нашия API:

Проблемът с този отговор е, че си върнахме всички полета, които присъстваха и в MongoDB. Ние не искаме това. Нека променим това в модела Post:

PostSchema.methods = {
   _slugify() {
     this.slug = slug(this.title);
   },
   toJSON() {
     return {
       _id: this._id,
       title: this.title,
       text: this.text,
       createdAt: this.createdAt,
       slug: this.slug,
       user: this.user,
       favoriteCount: this.favoriteCount,
     };
   },
 };

След като сме приложили функцията toJSON() в нашия модел, това е отговорът, който получаваме сега:

Получаване на потребителски данни при публикуване на отговор

Ако видим горното JSON отблизо, всъщност имаме потребителското поле, което съдържа неговия ID. Но какво ще стане, ако искаме и неговата информация в същия обект?

Просто модифицирайте леко функцията getPostById и променете този post const във функцията като:

const post = await Post.findById(req.params.id).populate(‘user’);

Току-що добавихме повикване за попълване и отговорът сега ще бъде:

toJSON също ще работи, когато попълним потребителския обект. Това е проблем тук, тъй като ние също си върнахме полето за токени над това, което никога не трябва да се случва!

Нека променим потребителския модел, за да подобрим това:

UserSchema.methods = {
   _hashPassword(password) {
     ...
   },
   authenticateUser(password) {
     ...
   },
   createToken() {
     ...
   },
   toAuthJSON() {
     ...
   },
   toJSON() {
     return {
       _id: this._id,
       userName: this.userName,
     };
   },

Променихме метода toJSON по-горе, така че полето за токен не е включено в самия отговор.

Проблемът всъщност все още е налице. Нека да видим какво се случва, когато се опитам да вляза потребител:

Вижте, тук също не присъства поле за токен. За да разрешите това, отидете на функцията за влизане в User controller и променете както следва:

export function login(req, res, next) {
 res.status(HTTPStatus.OK).json(req.user.toAuthJSON());
 return next();
}

Сега използвах самата функция toAuthJSON. Ако опитате сега, влизането ще работи както преди!

Получаване на всички данни от базата данни

след това можем да регистрираме нов потребител в нашето приложение:

Също така можем да позволим на потребител да влезе в нашето приложение:

Също така успяхме да създадем публикация, свързана и с потребител:

В този раздел ще добавим следните функции:

  • Работете върху Post Controller, за да добавите повече функционалност

Контролер за разширение

Досега имаме само следната функционалност в нашия Post Controller:

  • Създайте публикация
  • Вземете поща по ID

Сега също ще добавим повече функционалност и ще започнем с получаването на всички публикации в списък.

Получаване на всички публикации

Нека разширим функционалността в нашия Post Controller, като добавим нов метод за получаване на всички публикации:

export   async function getPostsList(req, res) {
     try {
       const posts = await Post.find().populate('user');
       return res.status(HTTPStatus.OK).json(posts);
     } catch (e) {
       return res.status(HTTPStatus.BAD_REQUEST).json(e);
     }
   }

Ето, върнахме постовете. Нека модифицираме файла с маршрута, за да използва тази функция, която добавихме по-горе:

routes.get(‘/’, postController.getPostsList);

Не сме добавили това в удостоверяването в това, за да позволим дори на неавтентифициран потребител да му позволи поне на публикациите. Нека опитаме този API сега:

В момента имаме 11 публикации в базата данни и така API по-горе не показва проблем. Но какво се случва, когато има повече от 50 000 публикации? В такива случаи ще имаме сериозни проблеми с производителността.

Пагинацията на помощ

Можем да върнем ограничен брой публикации въз основа на потребителска заявка. В модела post можем да предоставим параметри за страниране като:

PostSchema.statics   = {
     createPost(args, user) {
       ...
     },
     list({ skip = 0, limit = 5 } = {}) {
       return this.find()
         .sort({ createdAt: -1 })
         .skip(skip)
         .limit(limit)
         .populate('user');
     },
   };

Това, което списъкът прави, е, че първоначално връща само първите 5 публикации. Ако пропускането е 5, функцията за списък ще върне 5 публикации, но след като е пропуснала първите 5 публикации. Нека модифицираме и контролера:

export async function getPostsList(req, res) {
   const limit = parseInt(req.query.limit, 0);
   const skip = parseInt(req.query.skip, 0);
   try {
     const posts = await Post.list({ limit, skip });
     return res.status(HTTPStatus.OK).json(posts);
   } catch (e) {
     return res.status(HTTPStatus.BAD_REQUEST).json(e);
   }
 }

Сега, когато предоставяме тези стойности, получаваме този отговор:

Актуализиране на публикация и добавяне на валидации

след това можем да регистрираме нов потребител в нашето приложение:

Също така можем да позволим на потребител да влезе в нашето приложение:

Също така успяхме да създадем публикация, свързана и с потребител:

В този урок ще добавим следните функции:

  • Ще актуализираме публикация и ще се уверим, че потребителят, който актуализира публикацията, е нейният автор
  • Създайте поле за валидиране

Ще добавим още операции върху публикация в следващите уроци.

Контролер за разширение

Досега имаме само следната функционалност в нашия Post Controller:

  • Създайте поз
  • Вземете поща по ID
  • Вземете списък с всички публикации

Сега също ще добавим повече функционалност и ще започнем, като позволим на потребителя да актуализира публикация.

Актуализиране на публикация

Нека разширим функционалността в нашия Post Controller, като добавим нов метод за актуализиране на публикация:

export   async function updatePost(req, res) {
     try {
       const post = await Post.findById(req.params.id);
       if (!post.user.equals(req.user._id)) {
         return res.sendStatus(HTTPStatus.UNAUTHORIZED);
       }
   
       Object.keys(req.body).forEach(key   => {
         post[key] = req.body[key];
       });
   
       return res.status(HTTPStatus.OK).json(await post.save());
     } catch (e) {
       return res.status(HTTPStatus.BAD_REQUEST).json(e);
     }
   }

Ето какво направихме по-горе:

  • Потвърдете от JWT токена, ако потребителят е същият като потребителя, присъстващ в обект Post
  • Връщаме отговор UNAUTHORIZED, ако потребителят не е същият
  • Ако потребителят е един и същ, получаваме всеки ключ, предаван в заявка, и актуализираме публикацията въз основа на това
  • След като всички актуализации са готови, връщаме отговора OK

Нека променим файла за валидиране, за да използва тази функция, която добавихме по-горе:

import Joi from 'joi';
   
   export default   {
     createPost: {
       body: {
         title: Joi.string().min(3).required(),
         text: Joi.string().min(10).required(),
       },
     },
     updatePost: {
       body: {
         title: Joi.string().min(3),
         text: Joi.string().min(10),
       },
     },
   };

Току-що добавихме проверки във функцията updatePost за минимална дължина от две полета. Сега е време за файла с маршрута:

routes.patch(
   '/:id',
   authJwt,
   validate(postValidation.updatePost),
   postController.updatePost,
 );

Актуализиране на публикация

Сега, след като работата е свършена сега, ще проверим работата, която свършихме по-горе. Нека направим PATCH заявка от Postman по следния начин:

Отлично, проработи! Дори охлювът за публикацията беше актуализиран. Просто се уверете, че имаме този метод в модел за Post:

PostSchema.pre(‘validate’, function (next) {
 this._slugify();
 next();
});

Продължете и опитайте същото и с текста на публикацията.

Изтриване на публикация от оторизиран потребител

Досега можем да регистрираме нов потребител в нашето приложение:

Също така можем да позволим на потребител да влезе в нашето приложение:

Успяхме да създадем публикация, свързана с потребител:

В този урок ще добавим следните функции:

  • Ще позволим на автор да изтрие публикация
  • Функция за оторизация
  • Добавете инструмент, наречен prettie

Контролер за разширение

Досега имаме само следната функционалност в нашия Post Controller:

  • Създайте публикация
  • Вземете поща по ID
  • Вземете списък с всички публикации
  • Актуализиране на публикации

Сега също ще добавим повече функционалност и ще започнем, като позволим на потребителя да изтрие публикация.

Изтриване на публикация

Нека разширим функционалността в нашия Post Controller, като добавим нов метод за изтриване на публикация:

export async function deletePost(req, res) {
     try {
         const post =   await Post.findById(req.params.id);
   
       if (!post.user.equals(req.user._id)) {
         return res.sendStatus(HTTPStatus.UNAUTHORIZED);
       }
   
       await post.remove();
       return res.sendStatus(HTTPStatus.OK);
     } catch (e) {
       return res.status(HTTPStatus.BAD_REQUEST).json(e);
     }
   }

Ето какво направихме по-горе:

  • Потвърдете от JWT токена, ако потребителят е същият като потребителя, присъстващ в обект Post
  • Връщаме НЕОТОРИЗИРАН отговор, ако потребителят не е същият
  • Ако потребителят е същият, премахваме публикацията
  • След като публикацията бъде изтрита, връщаме отговора OK

Сега е време за файла с маршрута:

routes.delete(‘/:id’, authJwt, postController.deletePost);

Изтриване на публикация

След като работата е свършена сега, ще проверим работата, която свършихме по-горе. Нека направим DELETE заявка от Postman по следния начин:

Вече можете да проверите, че тази публикация не присъства в Get all Post API и MongoDB, както и със заявка като:

Добавяне на по-красива библиотека

Можем да добавим по-красива библиотека със следната команда yarn:

yarn add -D prettier

След като това стане, ето моят актуализиран package.json файл:

{
     "name": "makeanodejsrestapi",
     ...,
     "scripts": {
       ...,
       "prettier": "prettier --single-quote   --print-width 80 --trailing-comma all --write 'src/**/*.js'"
     },
     "dependencies": {
       ...
     },
     "devDependencies": {
       ...,
       "prettier": "^1.3.1",
       ...
     }
   }

Показахме само какви промени са направени. Също така ще добавим ES lint библиотека със следната команда:

yarn add -D eslint-config-prettie

Сега ще направим нов файл с име .eslintrc със следния коментар:

{
 “extends”: [
 “equimper”,
 “prettier”
 ]
}

Сега, ако забравите да добавите точка и запетая или отстъпи, просто трябва да изпълните следната команда и те ще бъдат добавени вместо вас:

yarn prettier

Това не е ли магия? :) Това също показва какви файлове са променени:

Ще продължим да използваме тази команда и библиотека, тъй като това наистина улеснява работата ни!

Означаване на публикация като предпочитана и управление на статистиката на публикация

след това можем да регистрираме нов потребител в нашето приложение:

Също така можем да позволим на потребител да влезе в нашето приложение:

Успяхме да създадем публикация, свързана с потребител:

В този раздел ще добавим следните функции:

  • Потребителят може да одобри публикация, когато бъде удостоверен, което също ще увеличи променливата на брояча favoriteCount
  • Променете моделите както за потребител, така и за публикация за това
  • Добавяне на статика за увеличаване/намаляване на Post

Промяна на потребителския модел

Ще добавим ново поле за съхраняване на публикации, избрани от потребителя. Нека редактирате файла user.model.js, за да постигнете това и добавете ново поле веднага след полето за парола:

favorites:   {
         posts: [{
           type: Schema.Types.ObjectId,
           ref: 'Post'
         }]
       }

Ще добавим и функция за използване на това поле:

UserSchema.methods   = {
     _hashPassword(password) {
       ...
     },
     authenticateUser(password) {
       ...
     },
     createToken() {
       ...
     },
     toAuthJSON() {
       ...
     },
     toJSON() {
       ...
     },
   
     _favorites: {
       async posts(postId) {
         if (this.favorites.posts.indexOf(postId)   >= 0) {
           this.favorites.posts.remove(postId);
         } else {
           this.favorites.posts.push(postId);
         }
         return this.save();
       }
     }
   };

Разширяващ пост контролер

Нека добавим функция и тук, за да използваме тази функционалност, която дефинирахме в модела. Започнете с импортиране на in post.controller.js file:

import User from ‘../users/user.model’;

След това извикваме моделната функция User:

export   async function favoritePost(req, res) {
     try {
       const user = await User.findById(req.user._id);
       await   user._favorites.posts(req.params.id);
       return res.sendStatus(HTTPStatus.OK);
     } catch (e) {
       return res.status(HTTPStatus.BAD_REQUEST).json(e);
     }
   }

Нека най-накрая модифицираме нашия post.routes.js файл за достъп до тази функция:

routes.post(‘/:id/favorite’, authJwt, postController.favoritePost);

Сега е време да тествате този маршрут. В Postman направете GET заявка към любимия API, след като изберете Post ID от Database или Get all post API:

След това проверяваме дали това работи от MongoDB:

Запазихме само идентификатора на обекта, защото това ще ни спести от репликиране на данните. Ако натиснете отново същия API, ще видите нещо странно, че ID на публикацията вече е премахнат от любимите в потребителския модел!

Също така запазваме фаворитите в модела Post. Нека го накараме да работи сега. Ще включим тази логика в клас модел Post:

PostSchema.statics = {
   createPost(args, user) {
     ...
   },
   list({ skip = 0, limit = 5 } = {}) {
     ...
   },
 
   incFavoriteCount(postId) {
     return this.findByIdAndUpdate(postId, { $inc: { favoriteCount: 1 } });
   },
 
   decFavoriteCount(postId) {
     return this.findByIdAndUpdate(postId, { $inc: { favoriteCount: -1 } });
   }
 };

Методите incFavoriteCount и decFavoriteCount първо използват метода findByIdAndUpdate на Mongo, за да намерят ID на публикацията и след това използват оператора $inc, за да добавят 1 в случай на увеличаване или -1 в случай на намаляване на любимите.

Нека сега да променим и потребителския модел. Първо добавете този оператор за импортиране:

import Post from ‘../posts/post.model’;

След това променете функционалността на метода _favorites тук:

_favorites:   {
       async posts(postId) {
         if (this.favorites.posts.indexOf(postId)   >= 0) {
           this.favorites.posts.remove(postId);
           await   Post.decFavoriteCount(postId);
         } else {
           this.favorites.posts.push(postId);
           await   Post.incFavoriteCount(postId);
         }
   
         return this.save();
       }
     }

Сега проблемът с потребителския модел, който посочихме по-горе, ще бъде решен и моделът favoriteCount в Post също ще работи:

Ако използвате същия API отново и отново, резултатът няма да се промени. Отлично! Имаме работещи приложни програмни интерфейси (API), където потребителят също може да одобри публикация.

Идентифициране дали дадена публикация вече е любима за потребителя

последния раздел, можем да регистрираме нов потребител в нашето приложение:

Също така можем да позволим на потребител да влезе в нашето приложение:

Успяхме да създадем публикация, свързана с потребител:

Актуализирайте публикация:

И също така изтрийте публикация:

В този раздел ще добавим следните функции:

  • Ще ги изпратим, ако текущата публикация е любима на потребителя или не, така че предният край да може да взема решения въз основа на този факт
  • Ще направим модификация на маршрута и ще работим и върху функциите на контролера

Удължаване на маршрута

Просто трябва да направим много малко промени в нашия post.route.js файл:

routes.get(‘/:id’, authJwt, postController.getPostById);
routes.get(‘/’, authJwt, postController.getPostsList);

Току-що добавихме authJwt в тези два съществуващи реда. След като това стане, ако се опитам да получа списък с публикации без заглавка за оторизация, ще получим грешка:

Разширяващ потребителски модел

Сега ще добавим повече информация към публикацията JSON, ако е благоприятна за текущия потребител Authorized.

Преминете към файла user.model.js и добавете тази функция в _favorites:

isPostIsFavorite(postId)   {
     if (this.favorites.posts.indexOf(postId)   >= 0) {
       return true;
     }
    return false;
    }

Преминете към файла post.controller.js сега и променете функцията getPostById:

export   async function getPostById(req, res) {
     try {
       const promise = await Promise.all([
         User.findById(req.user._id),
           Post.findById(req.params.id).populate('user')
       ]);
   
       const favorite = promise[0]._favorites.isPostIsFavorite(req.params.id);
       const post = promise[1];
   
       return res.status(HTTPStatus.OK).json({
         ...post.toJSON(),
         favorite
       });
     } catch (e) {
       return res.status(HTTPStatus.BAD_REQUEST).json(e);
     }
   }

Тук току-що добавихме ново любимо поле, което ще бъде отразено в API за публикации по следния начин:

Ще модифицираме и нашата функция getPostsList, за да включим Promise и да върнем подходящия отговор:

export   async function getPostsList(req, res) {
     const limit = parseInt(req.query.limit, 0);
     const skip = parseInt(req.query.skip, 0);
     try {
       const promise = await Promise.all([
         User.findById(req.user._id),
         Post.list({ limit, skip })
       ]);
   
       const posts = promise[1].reduce((arr, post) => {
         const favorite = promise[0]._favorites.isPostIsFavorite(post._id);
   
         arr.push({
           ...post.toJSON(),
           favorite
         });
   
         return arr;
       }, []);
   
       return res.status(HTTPStatus.OK).json(posts);
     } catch (e) {
       return res.status(HTTPStatus.BAD_REQUEST).json(e);
     }
   }

Нека стартираме това сега и да получим всички публикации:

Отлично. И накрая, но не на последно място, API има все повече и повече теми, които трябва да знаем, напр. сигурност, лимит на скоростта, най-добри практики. Надявам се да ви хареса това и да не ви губи времето

Кредит

всички знания в тази публикация идват от EQuimper youtube series

ако искате да научите повече за Node API

Node.js: Пълното ръководство за изграждане на RESTful API (2018)

4,7 (1775 оценки) || 10 171 ученици || 15 часа
Научете се да създавате бързи, мащабируеми и сигурни RESTful услуги с Node, Express и MongoDB, от настройката до производството Научете повече .



Разработка на API за начинаещи в Node, Express, ES6 и MongoDB

4.0 (1164 оценки)|| 10 282 ученици || 18 часа
Създаване на API, с които приложенията за iOS и Android могат да общуват Разполагане на API в Node към сървъри на живо Внедряване на живи екземпляри на MongoDB на сървъри, с които API могат да общуват Пишете код в ES6 за разработка на API или за общи програмиране
Научете повече.



или повече на Тема за напреднали

Node JS: Разширени концепции

4,7 (1570 оценки) || 11 815 ученици || 16 часа
Напреднете с Node.Js! Научете кеширане с Redis, ускорете чрез клъстериране и добавете качване на изображения с S3 и Node! Научете повече.



Заключителни бележки:

Публикувам статии за Node, Laravel, React, React Native и всичко останало, свързано с уеб разработката в React Ninja. Бъдете сигурни и ме последвайте в Twitter.

Искате да ме наемете да пиша за вашия блог, вижте моята страница Fiverr

Присъединете се към нашия Бюлетинза да получите най-новото и най-доброто съдържание, за да станете по-добър програмист.

Ако тази публикация е била полезна, моля, щракнете върху бутона пляскане 👏 по-долу няколко пъти, за да покажете подкрепата си! ⬇⬇