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

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

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
process.env.NODE_ENV = 'development';

const express = require('express'),
    app = express(),
    fs = require('fs');

const host = '127.0.0.1';
const port = 7000;

app.use(express.json());
app.use(express.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}`)
);

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

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

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

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

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

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

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

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

1
2
app.route('/api/users');
app.route('/api/v2/users');

Комментарии