Digamos que você não tem um orm, ou simplesmente não quer usar um e decidiu usar o mais próximo possível do nativo. Nesse caso, o mínimo que podemos fazer é usar queries parametrizadas.
Queries parametrizadas (ou consultas
parametrizadas, ou parameterized queries, ou ainda prepared statement)
são basicamente consultas que escrevemos para o banco de dados com
certos códigos para o módulo de acesso ao db pré compilar e nos
proteger de algum código malicioso, ou algum tipo de
SQL Injection.
Pense em parameterized queries como simplesmente variáveis que
você passa para o seu componente de acesso ao banco de dados e
ele garante o tipo e protege contra algum escape maldoso.
Com o node-postgres
, temos duas formas de fazer isso:
- 1) Usando apenas COLUMN = $N
(onde N é a posição desse argumento em
.query(STMT, args1, arg2...)
).
- 2) Passando o COLUMN = $N::TYPE
(onde type pode ser text, int, datetime, etc.)
Aqui vamos desenvolver uma classe que irá cuidar de gerar a segunda forma de query pra gente, será algo bem simples e o problema que ela vai resolver é o de termos que lembrar sempre a posição do parâmetro e seu tipo, facilitando nossa escrita de statements.
Sem mais delongas, eis a nossa criança:
class DbArgs {
constructor () {
this._args = [ ];
}
add (value, type) {
const argIndex = this._args.length + 1;
const fullType = `$${argIndex}::${type}`;
this._args.push({ argIndex, type, value, fullType });
return fullType;
}
toArray () {
// values only!
return this._args.map((arg) => arg.value);
}
}
Essa pequena classe simplesmente guarda nossos parâmetros em um array e retorna o text desejado, pronto para usarmos em nosso statemet, de forma que podemos escrever nosso código assim:
const args = new DbArgs();
const stmt = `
SELECT
text
FROM
blog_post
WHERE
name = ${args.add('%parameterized-query%', 'text')} AND
int_test = ${args.add(1, 'int')} AND
date = ${args.add(new Date().getTime(), 'timestamp')};
`;
const res = client.query(stmt, args.toArray());
Esse código gera o seguinte statement:
SELECT
text
FROM
blog_post
WHERE
name = $1::text AND
int_test = $2::int AND
date = $3::timestamp;
E o args.toArray()
, retorna:
[ '%parameterized-query%', 1, 1518470278513 ]
Bem mais simples que ter que ficar cuidando de ordem e tipo de argumentos, né? :)