Лучший способ использовать ООП в Express REST API?

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

У меня есть REST API, использующий Express и mysql. Я настроил mysql:

app.js

//Variables, move these to env
var dbOptions = {
    host: config.db_config.host,
    user: config.db_config.user,
    password: config.db_config.password,
    port: config.db_config.port,
    database: config.db_config.database
};
app.use(myConnection(mysql, dbOptions, 'single'));

а затем я включаю свои маршруты, передавая маршруты промежуточному программному обеспечению приложения и журнала, чтобы я мог использовать их в маршруте:

app.js продолжение

var userRoute = require('./routes/users.js')(app,log);
app.use('/users', userRoute);

На самом деле это немного сбивало с толку, но теперь я понял, мне нужно передать их в модуль, чтобы модуль получил данные.

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

маршруты /users.js

var User = require('../controllers/User.js');

module.exports = (function(app,log) {
var userR = express.Router();

userR.post('/register', function(req,res){
        var email = req.body.email;
        var password = req.body.password;
        var firstName = req.body.first_name;
        var lastName = req.body.last_name;
        var userId;

        try {
            var query;
            var status = 200;
            var response = '';

            var newUser = {
                email: email,
                password:password,
                first_name: firstName,
                last_name: lastName,
                password: password
            };

            var user = new User(req,res);

            user.register(newUser);
...

};

контроллеры/User.js

module.exports = function User(req,res) {
this.id = 0;
    this.register = function(newUser){

        var _this = this;
        var deferred = q.defer();
req.getConnection(function(err,connection){
...

Здесь должен быть какой-то шаблон, который мне не хватает. Я должен просто иметь возможность передать приложение или что-то в этом роде и иметь доступ к req.getConnection и т.д.

Спасибо.


person Z2VvZ3Vp    schedule 28.01.2015    source источник


Ответы (1)


Здесь много чего происходит, но я попробую это.

Моей первой рекомендацией было бы попытаться сделать ваши маршрутизаторы довольно тонкими. Он не помечен, но я предполагаю, что самый большой фрагмент кода, который вы предоставили, — это ваш маршрутизатор. Конечно, есть много мнений, но если бы я этим занимался, вот как выглядел бы мой роутер. Я предполагаю, что вы используете Express 4x.

маршруты /users.js

var User = require('../controllers/user.js');
var userRouter = express.Router();
userRouter.post("/register", User.register);
module.exports = userRouter;

Итак, что мы здесь сделали, так это устранили зависимость от промежуточного программного обеспечения вашего приложения/журнала.

Это означает, что ваш основной файл (который я обычно называю app.js) будет выглядеть так:

app.js

var userRouter = require('./routes/users.js');
app.use('/users', userRouter);

Сюда же можно поместить любое промежуточное ПО по вашему выбору (логирование, анализ тела, обработка ошибок и т. д.).

В более поздних версиях Express приведенный выше код фактически монтирует наш userRouter в «/users». В этом случае у вас теперь будет маршрут «/users/register».

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

контроллеры /user.js

var User = require("../models/user.js")
var register = function(req, res, next){
    var email = req.body.email;
    var password = req.body.password;
    var firstName = req.body.first_name;
    var lastName = req.body.last_name;
    var userId;
    var params = {
            email: email,
            password:password,
            first_name: firstName,
            last_name: lastName,
            password: password
        };
    var newUser = new User(params);

    try {
       newUser.register();
        // do other things...
    }
};
module.exports = {register: register};

Первое, что вы заметите, это то, что у меня будет файл UserModel. Сделав это, мы отделили наш объект модели от этого маршрута. Скажем, например, у нас есть новый маршрут регистрации (может быть, у одного из них есть электронная почта + pw, другой регистрируется через FB, и нам нужно хранить разные вещи). Мы должны иметь возможность использовать ту же функцию (в данном случае user.register), указанную в нашей модели, без необходимости менять целую кучу вещей!

Вот как может выглядеть UserModel:

/модели/user.js

var connection = require("../lib/connection.js");
var User = function(params){
   this.email = params.email;
   // ...etc
};

User.prototype.register = function(newUser){
    connection.getConnection(function(error, connection){
        //connection.doWhatever();
    });
};

module.exports = User;

Теперь мы, наконец, добрались до сути вашего вопроса. Вверху вы увидите, что у нас есть файл подключения. Здесь мы собираемся разместить всю нашу логику, связанную с БД, например, наши пулы соединений.

/lib/connection.js

/*
This will be in some JSON config we'll say
var dbOptions = {
    host: config.db_config.host,
    user: config.db_config.user,
    password: config.db_config.password,
    port: config.db_config.port,
    database: config.db_config.database
};

*/
//This will depend on which version/module/db you're using, but here's what mine looks like
var MySQL = require("mysql");
var config = require("../config/db.json");
connectionPool = MySQL.createPool({host: config.db_config.host, ...});

var getConnection = function(done){
   connectionPool.getConnection(done);
};

module.exports = {getConnection: getConnection};

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

Кроме того, я обычно пишу на CoffeeScript, так что извините за небольшие ошибки. Дайте мне знать, если вам нужны дополнительные разъяснения.

Привет, Бреннан

person Brennan    schedule 30.01.2015
comment
Ух ты! Какой подробный ответ, спасибо. Я все еще перевариваю все это, но теперь становится намного понятнее, как должны быть структурированы экспресс-приложения. Я думаю, что больше всего меня смущало то, что пакет узла myConnection, который я использую, расширяет объект запроса, предоставляя функциональность mysql, что на самом деле не имеет смысла. Не похоже, что он мне вообще нужен. Один первоначальный вопрос: обычно лучше писать функции отдельно, чем экспортировать модуль, как вы это сделали? Я только что запаковал все в module.exports = { ... } Еще раз спасибо. - person Z2VvZ3Vp; 30.01.2015
comment
Ах, я не был уверен насчет бита myConnection. Я никогда не видел этого раньше. Мне логичнее поступать таким образом, потому что не для каждого запроса потребуется подключение к базе данных. Я считаю, что экспортировать все, что находится внизу, чище по двум причинам. Во-первых, вы можете сделать свой код более плоским, вместо того, чтобы сразу начинать с одного уровня в глубину. Во-вторых, при условии, что вы последовательны, вы будете знать, где искать все ваши файлы для экспорта. Этого бы не произошло, если бы вы начали добавлять вспомогательные методы, которые не хотите экспортировать другим способом. - person Brennan; 30.01.2015
comment
Я буду рад ответить на любые другие вопросы, но если вы нашли мой ответ полезным, не забудьте принять его! - person Brennan; 30.01.2015
comment
Как User.register имеет доступ к req,res,next, не передавая их в вызове функции из маршрута? - person Z2VvZ3Vp; 03.02.2015
comment
User.register — это переменная, содержащая функцию с сигнатурой (req, res, next). Маршрутизатор связывает функцию с путем. Вот простой пример того, что происходит. - person Brennan; 03.02.2015
comment
О, точно так же, как в обычном js. Спасибо. Извините, я просто потерялся в этом там. Спасибо за помощь, я должен выпить :) - person Z2VvZ3Vp; 03.02.2015
comment
Не проблема. Рад помочь! - person Brennan; 03.02.2015