首页 > 解决方案 > 如何改进用于生产的节点 js 代码并在文件之间正确拆分?

问题描述

我是 javascript 和 node.js 的新手。

有人可以回答以下问题。1. 如何将 PostgreSQL 部分正确拆分到另一个文件中。2. 我的害虫实践是如何使用 pg 池。3. 我如何改进此代码以进行生产。

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

const pg = require('pg');
const pool = new pg.Pool({
    user: 'admin',
    password: 'test123!',
    host: '127.0.0.1',
    port: '5432',
    database: 'test_db'
});

app.get('/api/recipes', function(req, res){
    pool.connect(function(err, client, done) {
        if(err){
            console.log('Connection failed '+ err);
            res.status(400).send(err);
        }
        client.query('SELECT * FROM recipes;', function(err, result) {
            done();
            if(err){
                console.log('Error with query! ERROR code: ' + err.code);
                res.status(400).send(err);
            }
            else{
                res.status(200).send(result.rows)
            }
        });
    });
});

app.get('/api/recipes/:id', function(req, res){
    var id = req.params.id;
    pool.connect(function(err, client, done) {
        if(err){
            console.log('Connection failed ' + err);
            res.status(400).send(err);
        }
        else{
            client.query('SELECT * FROM recipes WHERE recipes_id = $1;', [id], function(err, result) {
                done();
                if(err){
                    console.log('Error with query! ERROR code: ' + err.code);
                    res.status(400).send(err);
                }
                else{
                    res.status(200).send(result.rows)
                }
            });
        }
    });
});

app.listen(3000,function(){
  console.log('Server listen on port 3000');
});

标签: javascriptnode.jspostgresqlexpress

解决方案


人们有很多方法可以拆分您描述的代码。我一块一块来。

首先,拉出任何可配置的变量并设置一个可以从环境中获取它们的文件(可能使用开发默认值,您可以选择)。你可以使用像commandsconvict这样的库,但老实说,我更喜欢自己编写一个简单的文件来拉取它们:

// ./config.js
module.exports = {
  pool: {
      user: process.env.DB_USER || 'admin',
      password: process.env.DB_PW || 'test123!',
      host: process.env.DB_HOST || '127.0.0.1',
      port: process.env.DB_PORT || '5432',
      database: process.env.DB_NAME || 'test_db'
    }
};

至于您的数据库调用,有些人喜欢使用类似 ORM 的东西,例如sequelize,但我还是倾向于从简单开始并根据需要添加东西。在您的情况下,您应该考虑可以制作通用代码的样板文件,然后将它们包装到仅公开给它真正需要的调用代码的简单模块中。例如,您会注意到您的大多数路由将连接到池,测试错误,然后运行查询(如果没有出错),最后呈现错误或查询结果,对吗?因此,这一切都可以包装到一个相当简单的查询函数中,该函数在内部处理样板文件,并且只使用查询表达式和回调,例如:

// ./db/index.js
const pg = require('pg');

const config = require('./config');
const pool = new pg.Pool(config.pool);

function query(sql, params, callback) {
   // maybe check for valid inputs here or something, but at least normalize in case folks don't pass params

  if(arguments.length < 3) {
     callback = params;
     params = null;
  }

   pool.connect((err, client, done) => {
       // just exit here and let the calling code know there was a problem
       if(err) return callback(err);

       // I haven't tested this w/ the pg library recently, you might have to do two of these if it doesn't like null as a second argument
       client.query(sql, params, (err, result) => {
          if(err) return callback(err);
          done();
          // calling code probably doesn't care about anything but rows, but you can do other stuff here if you prefer
          return callback(null, result.rows);
       });
   });
};

// You can also add additional functions if you want shorthand for doing things like query by ID or with params, or similar
module.exports = { query };

我还认为将 SQL 字符串集中存储在某个地方或模型对象上会很有帮助,只是为了让路由代码注释必须关心这一点。对于使用您的两条路线的超级简单示例,我可能会执行以下操作:

// ./db/queries.js 
module.exports = {
  RECIPES: {
    LIST: 'SELECT * FROM recipes;',
    FIND_BY_ID: 'SELECT * FROM recipes WHERE recipes_id = $1;'
  }
};

好的,所以现在您的路由代码可以非常简单,您只需获取 db 模块并处理查询,让路由担心它与请求和响应有什么关系。人们喜欢的另一种选择是为您的应用程序中的每个模型(例如食谱)实际创建一个模块,将上述两个文件包装成一组静态函数,这样您的路由甚至不知道它们正在专门查询。在这种情况下,调用将类似于Recipe.list(cb)or Recipe.findById(id, cb)。这是几年前 Ruby on Rails 流行起来的一种风格,它在 Node 社区中的接受程度参差不齐,但为了完整起见,我提到它。

// ./routes/recipes.js
const router = require('express').Router();
const db = require('./db');
const queries = require('./db/queries');

router.get('/api/recipes', (req, res, next) => {
  db.query(queries.RECIPES.LIST, (err, rows) => {
    if(err) return next(err);
    return res.send(rows); // status 200 is the default here
  });
});

router.get('/api/recipes/:id', (req, res, next) => {
  const id = req.params.id;
  db.query(queries.RECIPES.FIND_BY_ID, [id], (err, rows) => {
    if (err) return next(err);
    return res.send(rows);
  });
});

最后,在您的主 Express 设置文件中:

// ./app.js
const express = require('express');
const app = express();
const recipeRoutes = require('./routes/recipes') // note if you have an index.js file that gets imported just by calling for the folder, so that's a way to group features as well

app.use(recipeRoutes);

// I'm a big fan of error handling middleware. There's a more complex approach I did in [praeter][4] that gives you http-verb based errors that you can then catch and send the appropriate status, but that's again more complex than you might need here. 

app.use((err, req, res, next) => {
  // this can be as simple or as complex as you like.
  // note it's a best practice to send only "clean" messages to the client, so you don't give away that you're using a Postgres db or other stuff that makes hacking easier. 
  console.error(err);
  res.status(500).send('Oops! Something went wrong!!');
});

显然,有很多方法可以给这只猫剥皮,所以我建议主要是寻找你在哪里重复自己,然后重构以减少重复。此外,如果您有兴趣制作更多的生产就绪应用程序,那么12 要素应用程序是必读的。


推荐阅读