Qual é a maneira correta de usar o módulo postgresql node.js?

95

Estou escrevendo um aplicativo node.js no Heroku e usando o módulo pg . Não consigo descobrir a maneira "certa" de obter um objeto cliente para cada solicitação de consulta ao banco de dados.

A documentação usa código como este:

pg.connect(conString, function(err, client) {
  // Use the client to do things here
});

Mas certamente você não precisa chamar pg.connectdentro de todas as funções que usam o banco de dados, certo? Eu vi outro código que faz isso:

var conString = process.env.DATABASE_URL || "tcp://postgres:1234@localhost/postgres";
var client = new pg.Client(conString);
client.connect();
// client is a global so you can use it anywhere now

Estou inclinado para a segunda opção, pois acredito que a instância de banco de dados gratuita para Heroku é limitada a uma conexão de qualquer maneira, mas há alguma desvantagem em fazer isso dessa maneira? Preciso verificar se meu objeto cliente ainda está conectado antes de usá-lo?

Philip
fonte

Respostas:

158

Eu sou o autor de node-postgres . Em primeiro lugar, peço desculpas porque a documentação não deixou clara a opção certa: a culpa é minha. Vou tentar melhorar. Escrevi um Síntese agora para explicar isso porque a conversa ficou muito longa para o Twitter.

Usar pg.connecté o caminho a seguir em um ambiente web.

O servidor PostgreSQL só pode lidar com 1 consulta por vez por conexão. Isso significa que se você tiver 1 global new pg.Client()conectado ao seu back-end, seu aplicativo inteiro será controlado com base na rapidez com que o postgres pode responder às consultas. Ele literalmente alinhará tudo, enfileirando cada consulta. Sim, é assíncrono e está tudo bem ... mas você não prefere multiplicar sua taxa de transferência por 10x? Use pg.connect definir o pg.defaults.poolSizepara algo lógico (fazemos 25-100, não temos certeza do número certo ainda).

new pg.Clienté para quando você sabe o que está fazendo. Quando você precisa de um único cliente de longa duração por algum motivo ou precisa controlar com muito cuidado o ciclo de vida. Um bom exemplo disso é durante o uso LISTEN/NOTIFY. O cliente de escuta precisa estar por perto, conectado e não compartilhado para que possa lidar com as NOTIFYmensagens de maneira adequada . Outro exemplo seria ao abrir um cliente único para eliminar algumas coisas travadas ou em scripts de linha de comando.

Uma coisa muito útil é centralizar todo o acesso ao seu banco de dados em seu aplicativo em um arquivo. Não espalhe pg.connectchamadas ou novos clientes o tempo todo. Tenha um arquivo parecido db.jscom este:

module.exports = {
   query: function(text, values, cb) {
      pg.connect(function(err, client, done) {
        client.query(text, values, function(err, result) {
          done();
          cb(err, result);
        })
      });
   }
}

Dessa forma, você pode alterar sua implementação de pg.connectpara um pool personalizado de clientes ou o que for e só precisa alterar as coisas em um lugar.

Dê uma olhada no módulo node-pg-query que faz exatamente isso.

brianc
fonte
2
Desculpe, sou bastante novo no SGBD e ainda tenho problemas para entender isso, mas por que não queremos chamadas "pg.connect"? É por simplicidade ou por motivos de desempenho? Por exemplo, chamo pg.connect uma vez em cada uma das rotas que tenho em meu aplicativo básico (todas com o mesmo conString). Está tudo bem? Intuitivamente, parece que está fazendo uma nova conexão com o mesmo banco de dados sempre que eu o chamo (o que não quero), mas ele usa as conexões em pool internamente? Obrigado.
user1164937
Impressionante. Por que você está empregando uma conexão por consulta em vez de uma por solicitação? Estou procurando uma maneira apropriada de compartilhar uma conexão entre várias consultas em uma solicitação e estive pensando em res.locals antes de encontrar sua resposta aqui.
Joe Lapp
2
Oh espere. Parece que sua solução aqui não suporta transações.
Joe Lapp de
Deve ser permalinkado para o github.
Ryan Willis
1
Observe que o pg.connect foi removido após a v7 do node-postgres, também conhecido como pg. Consulte stackoverflow.com/questions/45174120/pg-connect-not-a-function
Colin D
23

Eu sou o autor de pg-promessa , que simplifica o uso de node-postgres por meio de promessas.

Ele aborda as questões sobre a maneira correta de se conectar e desconectar do banco de dados, usando o pool de conexão implementado por node-postgres , entre outras coisas, como transações automatizadas.

Uma solicitação individual na pg-Promessa se resume apenas ao que é relevante para a sua lógica de negócios:

db.any('SELECT * FROM users WHERE status = $1', ['active'])
    .then(data => {
        console.log('DATA:', data);
    })
    .catch(error => {
        console.log('ERROR:', error);
    });

ou seja, você não precisa lidar com a lógica de conexão ao executar consultas, porque você configura a conexão apenas uma vez, globalmente, assim:

const pgp = require('pg-promise')(/*options*/);

const cn = {
    host: 'localhost', // server name or IP address;
    port: 5432,
    database: 'myDatabase',
    user: 'myUser',
    password: 'myPassword'
};
// alternative:
// const cn = 'postgres://username:password@host:port/database';

const db = pgp(cn); // database instance;

Você pode encontrar muitos outros exemplos no tutorial Aprenda por Exemplo ou na página inicial do projeto .

vitaly-t
fonte
Olá, o Heroku só aceita conexões SSL. Em pgeste é especificado por pg.defaults.ssl = true;. Como você faz isso pg-promise?
outubro
@ocram github.com/vitaly-t/pg-promise/wiki/… , ou você pode especificar SSL nos parâmetros de conexão: github.com/vitaly-t/pg-promise/wiki/Connection-Syntax
vitaly-t
Sou novo na maior parte disso: javascript, promises, postgres, etc. e isso é exatamente o que eu precisava. Obrigado!!
Ryan Rodemoyer
1
@ocram Acabei de fazer isso funcionarpgp.pg.defaults.ssl = true;
CharlieC
isso criará várias conexões para melhorar a taxa de transferência do postgres automaticamente quando fornecermos várias solicitações de consulta ao postgres?
domingo
5

A piscina é o caminho a seguir. Algo como este

const { Pool } = require('pg');

    const pool = new Pool({
      connectionString: DATABASE_URL,
      ssl: false,
      max: 20,
      idleTimeoutMillis: 30000,
      connectionTimeoutMillis: 2000,
    });
    module.exports = {
        query: (text, params) => pool.query(text, params)
      }

pode ser usado como db.query('<BEGIN,COMMIT,ROLLBACK,your query,anything')

amar
fonte
1

É melhor criar um pool de pg globalmente e cada vez que você precisar fazer uma operação de banco de dados, use o cliente e depois libere-o de volta para o pool. Assim que todas as operações de banco de dados forem concluídas, finalize o pool usandopool.end()

Código de amostra -

let pool = new pg.Pool(dbConfig);
pool.connect(function(err, client, done) {

if (err) {
    console.error('Error connecting to pg server' + err.stack);
    callback(err);
} else {
    console.log('Connection established with pg db server');

    client.query("select * from employee", (err, res) => {

            if (err) {
                console.error('Error executing query on pg db' + err.stack);
                callback(err);
            } else {
                console.log('Got query results : ' + res.rows.length);


               async.each(res.rows, function(empRecord) {   
                        console.log(empRecord.name);
                });
            }
            client.release();

        });
}

});  

Para obter mais detalhes, você pode consultar minha postagem do blog - Fonte

Aniket Thakur
fonte
-1

Eu estava interessado em um manipulador muito simples para isso, então fiz o meu próprio sem torná-lo muito complicado. Não tenho ilusões de que seja super básico, mas pode ajudar algumas pessoas a começar. Basicamente, ele se conecta, executa consultas e trata os erros para você.

function runQuery(queryString, callback) {
  // connect to postgres database
  pg.connect(postgresDatabase.url,function(err,client,done) {
    // if error, stop here
    if (err) {console.error(err); done(); callback(); return;}
    // execute queryString
    client.query(queryString,function(err,result) {
      // if error, stop here
      if (err) {console.error(err+'\nQuery: '+queryString); done(); callback(); return;}
      // callback to close connection
      done();
      // callback with results
      callback(result.rows);
    });
  });
}

Então você usaria chamando-o desta forma:

runQuery("SELECT * FROM table", function(result) {
  // Whatever you need to do with 'result'
}
JM-AGMS
fonte
Isso nem mesmo libera a conexão de volta ao pool. Isso vai esgotar a piscina bem rápido. O exemplo básico na node-postgrespágina é melhor do que isso.
vitaly-t
-2

É assim que eu faço, uma espécie de "abordagem de todas as opções acima"

Promise = require 'bluebird'
pg = module.exports = require 'pg'

Promise.promisifyAll pg.Client.prototype
Promise.promisifyAll pg.Client
Promise.promisifyAll pg.Connection.prototype
Promise.promisifyAll pg.Connection
Promise.promisifyAll pg.Query.prototype
Promise.promisifyAll pg.Query
Promise.promisifyAll pg

connectionString = process.env.DATABASE_URL

module.exports.queryAsync = (sql, values) ->
  pg.connectAsync connectionString
  .spread (connection, release) ->
    connection.queryAsync sql, values
    .then (result) ->
      console.log result.rows[0]
    .finally ->
      release()
Duane Fields
fonte
1
Assim, você acabará sem gerenciamento de conexão, sem suporte a transações e sem suporte a tarefas. Qual é o ponto então?
vitaly-t
1
Que língua é essa? café? berk
caub