В этом руководстве мы узнаем, как создать Rest API в Node.js и Express.js, создав простой API приложения todo. В этом руководстве предполагается наличие среднего уровня знаний JavaScript и опыта работы с командной строкой. Исходный код финального проекта можно найти здесь.

Начало работы

Для начала создайте новую папку и назовите ее todo.

$ mkdir todo
$ cd todo

В папке с задачами создайте новый файл с именем app.js

$ touch app.js

Следующее, что нам нужно сделать, это установить Node. вы можете установить узел здесь. После установки узла мы установим экспресс с помощью npm. Node поставляется с npm, поэтому нам не нужно устанавливать npm.

Инициализировать приложение

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

npm init 

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

Это создаст для вас файл package.json.

Установите зависимости и настройте Babel

$ npm install express —-save

Нам также потребуется установить babel, потому что мы будем писать синтаксис ES6, Babel помогает превратить наши коды с ES6 в ES5. почему мы должны компилировать наш код до ES5? Что ж, есть некоторые функции ES6, которые наши браузеры и узел еще не понимают, а старые браузеры не понимают коды ES6, поэтому мы используем babel для компиляции нашего кода в ES5, чтобы его могли понять как старые браузеры, так и новые браузеры. Наша цель использования babel здесь состоит в том, чтобы функции ES6, которые еще не являются частью узла, могли быть скомпилированы до ES5, который понимает узел.

$ npm install babel-cli --save

Мы также будем устанавливать babel-preset-es2015, пресеты содержат набор плагинов, эти плагины предназначены для функций ES6, установив и настроив babel-preset-es2015, мы сообщаем babel, что нужно преобразовать любые функции ES6, которые мы используем, которые содержатся в предустановки к их эквиваленту ES5.

$ npm install babel-preset-es2015 --save

Теперь нам нужно создать файл .babelrc, в файле babelrc мы настроим babel на использование предустановки es2015, которую мы установили в качестве предустановки при компиляции в ES5.

В корне нашего приложения мы создадим файл .babelrc.

$ touch .babelrc

В файле babelrc введите следующее, чтобы настроить предустановку.

{  
   "presets": ["es2015"]
}

Это говорит babel использовать es2015 в качестве предустановки, обратите внимание, что ES2015 - это другое имя для ES6.

Создать фиктивную базу данных

Последнее, что нам нужно сделать, прежде чем мы начнем создавать наш Apis, - это создать фиктивную базу данных, которую мы будем использовать.

Создайте папку и назовите ее db.

$ mkdir db

В папке создайте файл и назовите его db.js

$ cd db
$ touch db.js

В db.js создайте объект javascript для создания фиктивной базы данных

const todos =  [
    {
      id: 1,
      title: "lunch",
      description: "Go for lunc by 2pm"
    }
];

export default todos;

Настройте приложение и создайте нашу первую конечную точку

А теперь приступим к созданию нашего приложения.

import express from 'express';
import db from './db/db';
// Set up the express app
const app = express();
// get all todos
app.get('/api/v1/todos', (req, res) => {
  res.status(200).send({
    success: 'true',
    message: 'todos retrieved successfully',
    todos: db
  })
});
const PORT = 5000;

app.listen(PORT, () => {
  console.log(`server running on port ${PORT}`)
});

Мы импортировали экспресс, который мы установили в начале курса, app.get делает запрос на получение на сервер с маршрутом / конечной точкой, предоставленной в качестве первого параметра, конечная точка предназначена для возврата всех задач в базе данных. Второй параметр - это функция, которая запускается каждый раз, когда мы достигаем этой конечной точки. функция принимает два параметра: req и res. Объект req содержит информацию о нашем запросе, а объект ответа содержит информацию об ответе и методах, которые мы можем использовать для отправки информации обратно клиенту.

res.status (200) используется для отправки статуса запроса, 200 означает ОК и указывает на успешный запрос. Коды состояния - это способы, с помощью которых клиент, например веб-приложение или мобильное приложение, может проверить, пошло ли что-то не так. действительно заинтересованы. Если возвращается 200, мы можем проверить полезную нагрузку, потому что знаем, что чего-то ожидаем. Подробнее о кодах статуса HTTP можно узнать здесь.

res.send () используется для отправки ответа клиенту, ресурс, переданный в send в качестве параметра, - это то, что отправляется обратно клиенту. в этом случае мы отправляем обратно объект, который содержит некоторую информацию, свойство todos объекта содержит данные, которые мы импортировали в верхней части app.js из нашей фиктивной базы данных.

app.listen создает для нас веб-сервер, он принимает два параметра, первый параметр - это порт, который мы хотим, чтобы наше приложение прослушивало t, независимо от того, какой порт мы предоставляем, в нашем случае 5000 будет порт, на котором будет работать наш сервер в нашей системе. второй параметр является необязательным, это функция обратного вызова того, что должно произойти при создании сервера, в нашем случае мы записываем сообщение в консоль. когда сервер будет создан, мы сможем получить доступ к нашей конечной точке ’/ api / v1 / todos’ оттуда. сервер будет работать на порту 5000 на локальном хосте на нашей машине. так что у нас будет наш маршрут localhost: port / api. Созданная нами конечная точка будет доступна следующим образом: localhost: 5000 / api / v1 / todos.

Теперь, чтобы запустить этот код, перейдем к командной строке. Обычно мы запускаем наше приложение узла из командной строки, как это.

$ node app.js 

Но это вызовет ошибку, потому что наш код находится на ES6, и для успешного выполнения нашего кода нам нужно будет запустить его с помощью babel-node, который скомпилирует его в ES5. babel-node поставляется с babel-cli, которое мы установили в начале курса.

$ node_modules/.bin/babel-node app.js

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

Чтобы протестировать конечную точку, мы перейдем на localhost: 5000 / api / v1 / todos.

Взгляните на «GET» перед localhost: 5000 / api / v1 / todos, помните, что наша конечная точка - это запрос на получение, поэтому мы установили этот раскрывающийся список на «GET», если наша конечная точка была почтовым запросом, то есть у нас было приложение. post, тогда мы бы установили для него POST.

Запускать наш сервер, запуская это каждый раз, сложно, чтобы упростить этот процесс, мы собираемся сделать две вещи: во-первых, мы собираемся установить nodemon, во-вторых, мы создадим скрипт в нашем пакете, json, чтобы запустить наш сервер.

Давайте установим nodemon, запустите в своем терминале следующее.

$ npm install nodemon --save-dev

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

Давайте создадим скрипт в вашем package.json внутри части скрипта, включая это

"start": "node_modules/.bin/nodemon app.js --exec babel-node --"

Вы знаете, что делает babel-node.

Ваши скрипты package.json должны выглядеть так

{
"name": "todo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon app.js --exec babel-node --"
},
"author": "",
"license": "ISC",
"dependencies": {
"babel-cli": "^6.26.0",
"babel-preset-es2015": "^6.24.1"
"express": "^4.16.3"
}
}

Теперь каждый раз, когда мы хотим запустить наше приложение, мы просто запускаем следующий скрипт на терминале.

npm run start

Это запустит стартовый скрипт в нашем файле package.json.

Давайте создадим конечную точку для добавления задач, но перед этим давайте установим пакет под названием body-parser, body-parser анализирует тела входящих запросов в промежуточном программном обеспечении перед вашими обработчиками, доступными в req.bodyproperty.

Когда вы отправляете данные на сервер, данные могут поступать из формы или это может быть json, парсер тела анализирует эти данные, поступающие от клиента, в объекте с именем req.body, поэтому для Например, если у меня есть данные JSON, поступающие от клиента на сервер.

{
   "name": "Ola",
   "school": "unilag"
}

Что делает парсер тела, так это то, что он анализирует эти данные JSON и делает их доступными в req.body как свойство. помните, что req - это первое свойство, которое мы предоставляем для нашего обратного вызова, когда мы делаем запрос API, и помните, что я сказал, что req содержит информацию о запросе, исходящем от клиента, поэтому парсер тела делает данные, поступающие из формы, или любые данные JSON, поступающие от клиента, доступного как свойство в req.body, поэтому мы можем получить доступ к данным JSON из req.body как.

req.body.name
req.body.school

Итак, давайте установим body-parser

$ npm install body-parser --save

Теперь давайте импортируем его в app.js

import bodyParser from 'body-parser';

Теперь мы настроим парсер тела для нашего приложения вот так.

const app = express();
// Parse incoming requests data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

Создать Todo

Теперь перейдем к созданию конечной точки для создания задачи.

app.post('/api/v1/todos', (req, res) => {
  if(!req.body.title) {
    return res.status(400).send({
      success: 'false',
      message: 'title is required'
    });
  } else if(!req.body.description) {
    return res.status(400).send({
      success: 'false',
      message: 'description is required'
    });
  }
 const todo = {
   id: db.length + 1,
   title: req.body.title,
   description: req.body.description
 }
 db.push(todo);
 return res.status(201).send({
   success: 'true',
   message: 'todo added successfully',
   todo
 })
});

В этой конечной точке мы не делаем запрос к серверу для получения данных, а скорее отправляем данные. как мы объяснили, входящие данные анализируются в req.body как свойства, поэтому у нас есть req. body.title и т. д.

Мы создаем объект todo с информацией, которую мы получили от клиента через body-parser, а затем помещаем его в фиктивный массив db в качестве нового todo.

Давайте проверим это в почтальоне, нам нужно будет передать некоторые данные и отправить их на сервер, чтобы проверить это, на почтальоне, когда вы выбираете POST, щелкните тело n перед заголовками, которые находятся перед авторизацией, и передайте свои значения в поле.

localhost: 5000 / api / v1 / todos, убедитесь, что вы установили HTTP-метод в почтальоне на POST.

Взгляните на 'POST' перед localhost: 5000 / api / v1 / todos, помните, что наша конечная точка - это почтовый запрос, поэтому мы установили этот раскрывающийся список на POST. Убедитесь, что переключатель x-www-form-urlencoded установлен или используйте raw, где вы передаете данные JSON, как это.

{
"title": "breakfast",
"description": "get breakfast"
}

Получите одно задание

Теперь давайте создадим конечную точку, чтобы получить одно задание из базы данных. Введите следующее:

app.get('/api/v1/todos/:id', (req, res) => {
  const id = parseInt(req.params.id, 10);
  db.map((todo) => {
    if (todo.id === id) {
      return res.status(200).send({
        success: 'true',
        message: 'todo retrieved successfully',
        todo,
      });
    } 
});
 return res.status(404).send({
   success: 'false',
   message: 'todo does not exist',
  });
});

Как обычно, эта конечная точка принимает два параметра: маршрут и функцию обратного вызова. Другое дело, что маршрут в этой конечной точке имеет: id, иногда мы хотим передать параметры нашим конечным точкам, потому что они потребуются нам в нашем приложении, чтобы передать те параметры, которые мы используем: param.

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

Что происходит в конечной точке выше, так это то, что мы передаем идентификатор задачи, которую хотим получить в качестве параметра для маршрута, чтобы получить значение идентификатора, переданное в маршрут, который мы используем req.params.id, req.params - это объект, который содержит все параметры, переданные маршрутам, мы преобразуем идентификатор в int, а затем мы перебираем нашу фиктивную базу данных db, чтобы найти задачу, идентификатор которой будет равен тому, который мы получили из URL-адреса, тогда соответствующая задача будет вернулся как единственное задание.

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

Теперь давайте добавим новую задачу в базу данных.

Давайте снова получим все задачи из базы данных

Теперь у нас есть новое задание, которое мы только что добавили в базу данных.

Давайте теперь протестируем нашу конечную точку, перейдите по адресу localhost: 5000 / api / v1 / todos /: id, замените: id на идентификатор задачи, которую вы хотите получить. в нашем случае задача с идентификатором 2, установите для метода http значение GET.

Удалить Todo

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

app.delete('/api/v1/todos/:id', (req, res) => {
  const id = parseInt(req.params.id, 10);

  db.map((todo, index) => {
    if (todo.id === id) {
       db.splice(index, 1);
       return res.status(200).send({
         success: 'true',
         message: 'Todo deleted successfuly',
       });
    }
  });


    return res.status(404).send({
      success: 'false',
      message: 'todo not found',
    });

 
});

Так что мы здесь делаем? ну, в основном мы передаем id задачи, которую хотим удалить, в качестве параметра route / api / v1 / todos /: id. мы получаем этот идентификатор с помощью req.params.id, поэтому, чтобы удалить элемент с этим идентификатором, мы должны сначала найти его в базе данных, мы сделали это, сопоставив массив db и проверив идентификатор текущего дела в итерация по идентификатору, полученному из маршрута, пока мы не найдем совпадение, затем мы используем метод массива splice (), чтобы удалить этот элемент из базы данных. Вы можете узнать больше о array.splice и о том, как он работает здесь. В случае, если мы ничего не находим, мы возвращаем пользователю сообщение todo not found.

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

Теперь давайте добавим новую задачу

давай снова принесем все наши задачи

Как вы можете видеть, теперь у нас есть два задачи в нашей базе данных, предыдущая, которая была там изначально (мы жестко запрограммировали это в приложении), - это задача с идентификатором 1, а новая, которую мы добавили, - это задача с идентификатором. из 2.

Теперь давайте удалим задачу с идентификатором 1, перейдя к только что созданной конечной точке. Перейдите к localhost: 5000 / api / v1 / todos /: id, id в этом случае 1, просто замените: id на 1 в конечной точке.

Теперь давайте возьмем всю конечную точку, чтобы увидеть, действительно ли мы удалили задачу.

Теперь у нас есть только задача с идентификатором 2.

Давайте попробуем удалить Todo с несуществующим идентификатором.

Мы получаем сообщение, что задача не найдена.

Обновить Todo

Теперь давайте создадим конечную точку для обновления задач.

app.put('/api/v1/todos/:id', (req, res) => {
  const id = parseInt(req.params.id, 10);
  let todoFound;
  let itemIndex;
  db.map((todo, index) => {
    if (todo.id === id) {
      todoFound = todo;
      itemIndex = index;
    }
  });

  if (!todoFound) {
    return res.status(404).send({
      success: 'false',
      message: 'todo not found',
    });
  }

  if (!req.body.title) {
    return res.status(400).send({
      success: 'false',
      message: 'title is required',
    });
  } else if (!req.body.description) {
    return res.status(400).send({
      success: 'false',
      message: 'description is required',
    });
  }

  const updatedTodo = {
    id: todoFound.id,
    title: req.body.title || todoFound.title,
    description: req.body.description || todoFound.description,
  };

  db.splice(itemIndex, 1, updatedTodo);

  return res.status(201).send({
    success: 'true',
    message: 'todo added successfully',
    updatedTodo,
  });
});

Как обычно, мы получаем идентификатор задачи, которую хотим обновить, из URL-адреса, мы перебираем нашу фиктивную базу данных, чтобы найти задачу с этим идентификатором, если мы не находим задачу, мы возвращаем сообщение пользователю с указанием задачи не найден. Если мы находим задачу, мы получаем новый ввод, предоставленный пользователем, новый ввод анализируется парсером тела в req.body, мы получаем обновленные записи из req.body и создаем с ним обновленный объект задачи. Затем мы используем db.splice, чтобы удалить старый todo, который соответствует нашей итерации, когда мы перебирали фиктивный db, и заменяем его на updatedTodo, который мы создали.

Давай проверим это.

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

Теперь давайте обновим задачу с идентификатором 1.

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