Перейти к содержанию

REST API

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

Концепция REST API

Основная идеи REST API заключается в разделении разных операций (чаще всего CRUD) при обращении к одному и тому же URL с помощью HTTP методов, основные из которых:

  • GET - используется для получения данных;
  • POST - используется для создания новой записи(ей);
  • PUT - используется для обновления уже существующей записи(ей);
  • PATCH - используется для обновления, но только тогда, когда изменяется идентификатор записи(ей);
  • DELETE - используется для удаления записи(ей).

Пример REST API

Рассмотрим пример создания Node.js REST API. Для простоты данные будут храниться в файле.

app.js

process.env.NODE_ENV = 'development'

const express = require('express'),
  app = express(),
  fs = require('fs'),
  bodyParser = require('body-parser')

const host = '127.0.0.1'
const port = 7000

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))

let file = 'data.json'

if ((process.env.NODE_ENV = 'test')) file = 'data-test.json'

app.use((req, res, next) => {
  fs.readFile(file, (err, data) => {
    if (err) return res.status(500).send({ message: 'Error while getting users' })

    req.users = JSON.parse(data)

    next()
  })
})

app
  .route('/api/users')
  .get((req, res) => {
    if (req.query.id) {
      if (req.users.hasOwnProperty(req.query.id)) return res.status(200).send({ data: req.users[req.query.id] })
      else return res.status(404).send({ message: 'User not found.' })
    } else if (!req.users) return res.status(404).send({ message: 'Users not found.' })

    return res.status(200).send({ data: req.users })
  })
  .post((req, res) => {
    if (req.body.user && req.body.user.id) {
      if (req.users.hasOwnProperty(req.body.user.id)) return res.status(409).send({ message: 'User already exists.' })

      req.users[req.body.user.id] = req.body.user

      fs.writeFile(file, JSON.stringify(req.users), (err, response) => {
        if (err) return res.status(500).send({ message: 'Unable create user.' })

        return res.status(200).send({ message: 'User created.' })
      })
    } else return res.status(400).send({ message: 'Bad request.' })
  })
  .put((req, res) => {
    if (req.body.user && req.body.user.id) {
      if (!req.users.hasOwnProperty(req.body.user.id)) return res.status(404).send({ message: 'User not found.' })

      req.users[req.body.user.id] = req.body.user

      fs.writeFile(file, JSON.stringify(req.users), (err, response) => {
        if (err) return res.status(500).send({ message: 'Unable update user.' })

        return res.status(200).send({ message: 'User updated.' })
      })
    } else return res.status(400).send({ message: 'Bad request.' })
  })
  .delete((req, res) => {
    if (req.query.id) {
      if (req.users.hasOwnProperty(req.query.id)) {
        delete req.users[req.query.id]

        fs.writeFile(file, JSON.stringify(req.users), (err, response) => {
          if (err) return res.status(500).send({ message: 'Unable delete user.' })

          return res.status(200).send({ message: 'User deleted.' })
        })
      } else return res.status(404).send({ message: 'User not found.' })
    } else return res.status(400).send({ message: 'Bad request.' })
  })

app.listen(port, host, () => console.log(`Server listens http://${host}:${port}`))

Использование глаголов в названии маршрута при создании REST API считается плохой практикой и будет концептуально неправильным.

Здесь сразу предусмотрено наличие файла для тестирования приложения. Использование того или иного файла зависит от заданного переменной process.env.NODE_ENV окружения. Тестирование API рассмотрено отдельно.

В приведенном примере для одного маршрута /api/users определяются разные обработчики, каждый из которых вызывается в зависимости от используемого при запросе HTTP-метода.

Для приведенного примера подразумевается, что в файле data.json по умолчанию находится пустой объект.

Версионность REST API

При внесении изменений в логику работы вашего REST API, если оно является публичным, рекомендуется оставлять работоспособной предыдущую версию, чтобы предотвратить возникновение критических ошибок у пользователей прошлой версии. Версионность можно указывать прямо в URL.

app.route('/api/v1/users')

Если изначально дальнейшее развитие API не планировалось и версионность не предусматривалась, но потом все же пришлось вносить изменения, то номер версии можно добавить в уже новые маршруты. Главное - оставить стабильной предыдущий вариант.

app.route('/api/users')
app.route('/api/v2/users')