Fala pessoal 👋 , dessa vez vamos falar sobre Raw SQL, Query Builder e ORM's! Bom, como todos sabem o uso de banco de dados é algo muito comum na vida de um DEV, e com isso surgem inúmeras formas de se trabalhar com os bancos. Meu objetivo nesse post é falar sobre as diferenças, pontos positivos e negativos de cada approach.
O que é Raw SQL ?
De forma resumida: é quando você escreve a query nativa em SQL.
Quando a sua aplicação tem conexão com o banco de dados ela possui um canal de comunicação, onde ela pode enviar as queries nativas feitas em SQL. Basicamente, Raw SQL é a maneira mais low-level que sem para falar com o seu banco (escrevendo as queries em SQL). Veja o exemplo a baixo:
const express = require('express');
const client = require('./db');
const app = express();
app.get('/', async (req, res) => {
const result = await client.query(`SELECT * FROM "Users"`)
.then(result => result.rows)
.catch(err => console.error(err));
res.send(result);
});
app.listen(3000, () => console.log('Running on PORT 3000'));
Raw SQL
Vantagens
Aproveitar recursos nativos do SGBD.
Com as queries nativas a aplicação tem mais liberdade para utilizar os recursos nativos que cada SGBD tem a oferecer.
Performance.
Raw Queries tendem a ser mais performáticas por não possuírem abstração e nenhum processo de conversão de código orientado a objeto em SQL.
Desvantagens
SQL Injection.
Exato meus queridos, um dos maiores problemas quando falamos de Raw Queries é o famoso SQL Injection, isso ocorre pelo fato da não tratativa dos input's da aplicação, existem diversas soluções para corrigir esse problema. Mas para entendermos melhor, Olhe o exemplo abaixo:
const express = require('express');
const client = require('./db');
const app = express();
app.get('/', async (req, res) => {
const result = await client.query(`SELECT * FROM "Users" WHERE name = ${req.query.name}`)
.then(result => result.rows)
.catch(err => console.error(err));
res.send(result);
});
app.listen(3000, () => console.log('Running on PORT 3000'));
Hmmmm ...🤔 , interessante, agora olhe um exemplo de uma requisição mal intencionada:
Como vocês podem ver, no query param foi passado o nome e uma instrução SQL "Aonde ocorre o ataque" o nosso retorno acabou devolvendo alguns dados que não deveriam ser retornados. Existem varias maneiras de contornar esse tipo de problema, mas a ideia é demonstrar o que pode acontecer sem a tratativa dos input's e o risco potencial.
Tipagem e Syntax highlighting.
Quando inserimos a nossa query em SQL no código ela fica interpolada por um string da seguinte maneira SELECT * FROM "Users"
o que acontece é que caso tenha algum erro de digitação você não irá conseguir ver o problema no seu editor de código, apenas quando você executar o código e der errado. Isso pode dificultar um pouco o processo de desenvolvimento.
O que é Query Builder ?
De forma resumida: São lib's ou pacotes que executam comandos SQL através de métodos ou funções.
Query Builder são uma alternativa muito interessante, basicamente eles compõem um conjunto de funções escritas em uma linguagem x e que representam os comandos nativos do SQL. No geral o query builder estão um nível a cima na camada de abstração em comparação com o Raw SQL, mas afinal como isso me ajuda 🤔?
Olha esse exemplo:
const express = require('express');
const knexDB = require('./knex');
const app = express();
app.get('/', async (req, res) => {
const knexReturn = await knexDB('Users')
.select('id', 'name')
.where('name', req.query.name)
res.send(knexReturn);
});
app.listen(3000, () => console.log('Running on PORT 3000'));
Agora os comandos nativos do SQL são escritos como funções!
Vantagens
Limpeza dos inputs (Xô SQL Injection).
Limpeza dos input's? Exato! Quando temos uma camada a mais de abstração os métodos e funções ficam responsáveis por realizar a limpeza do input's do usuário, então comandos como SELECT * FROM "Users" WHERE name='' OR name='Admin' AND '1'='1'
não irão ser mais gerados ou executados.
Olhe o exemplo abaixo:
No exemplo acima fizemos a tentativa de um SQL Injection no end-point, mas como resultado, nada foi retornado, por quê🤔? Como eu comentei o query builder tem uma camada a mais de abstração, isso permite que as funções limpem os input's feitos pelos usuários o que resulta no retorno vazio da chamada!
Facilidade na escrita.
No momento que vamos escrever nossas queries algumas vezes erramos os comandos, com o query builder isso fica um pouco mais fácil, veja o exemplo:
Agora nosso editor de código consegue entender exatamente o que queremos fazer 🎉 .
Desvantagens
Pouca abstração (Requer boa compreensão do SQL).
Talvez esse ponto seja um pouco controverso, mas irei explicar, quando temos pouca abstração dos bancos SQL isso requer uma compreensão mais apurada de como utilizar o SQL, em contraste, em abstrações maiores o uso do SQL pode ficar mais ofuscado.
De forma mais objetiva, pouca ou muita abstração vai depender do cenário da aplicação!
Suporte Nativo.
Sabe aquele momento que você escolhe o SGBD especifico só por causa de uma funcionalidade ou syntax, então, isso pode acabar sendo um problema com o Query Builder, claro que depende muito do query builder alguns podem ter os métodos e funções que você deseja utilizar, mas caso não tenha, você tera que fazer o uso do Raw mode.
O uso do Raw Mode nos faz pular a cerca
O que é ORM ?
De forma resumida: ORM's são pedaços de códigos que utilizam a Orientação a Objeto para representar o seu banco de dados.
Se formos comparar o ORM com todos os métodos citados, o ORM esta no topo da pirâmide no quesito abstração.
Pense que ORM fosse um carro do futuro (cheio de abstração), onde você nem precise saber como dirigir um carro para se deslocar.
Os ORM's no geral permitem você fazer as consultas no banco de dado sem que tenha um conhecimento concreto de SQL. Interessante não? Olhe o exemplo:
const express = require('express');
const UsersModel = require('./sequelize');
const app = express();
app.get('/', async (req, res) => {
const result = await UsersModel.findAll({
where: {
name: req.query.name
}
});
res.send(result);
});
app.listen(3000, () => console.log('Running on PORT 3000'));
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize('postgres://postgres:postgres@127.0.0.1:5432/NodeRawQuery');
const UsersModel = sequelize.define('Users', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: DataTypes.STRING,
age: DataTypes.INTEGER,
password: DataTypes.STRING,
}, { timestamps: false });
module.exports = UsersModel;
Se olharmos para a syntax o código fica mais elegante e descritivo, o que e ótimo!
Vantagens
Código mais descritivo.
O uso de ORM's permite que a escrita seja mais clara e descritiva, isso ajuda o desenvolvedor em focar no necessário (business logic).
Versionamento de bancos.
ORM's permitem você versionar seu banco, alguns query builders também, mas e bem relativo a stack em questão.
Desvantagens
Performance.
ORM's são os menos performáticos das soluções que foram citadas, o motivo para isso é por conta da conversão de código OOP para SQL Nativo, esse processo gera um custo para a performance.
Abstração.
ORM's são incríveis e permitem você ter uma produtividade muito boa, mas é muito importante que ao fazer o uso de ORM's o seu conhecimento em SQL seja solido, eventuais problemas podem acontecer e a abstração pode dificultar o processo de debug.
Conclusão
De forma resumida: ORM's e Query Builder's se mostram ótimas soluções, Raw SQL continua sendo uma solução porem precisa de uma tratativa a mais.
Irei sintetizar de forma objetiva os pontos citados no texto em uma tabela abaixo:
Comparativo
Gerenciamento | Abstração | Complexidade | |
---|---|---|---|
Raw SQL | Alto | / | Baixo |
Query Builder | Baixo | Baixo | Baixo |
ORM's | Baixo | Alto | Alto |
De maneira geral, acho o conhecimento solido em SQL muito importante independente da solução, conhecer bem o SQL e fundamental no dia a dia de um desenvolvedor. Espero que tenham gostado! Até a próxima !!
Top comments (0)