Node.js - Como fazer uma rota post para o registro de uma array de objetos?
A intenção deste artigo é te ensinar como manipular uma array de objetos em uma rota post feita em nodejs e mysql.
Olá à todos,
Espero que este artigo te encontre bem e, sem mais delongas, vamos direto ao ponto.
Caso você queira saber como criar um pequeno servidor usando Node.js e Mysql clique aqui.
Recentemente estou trabalhando em um aplicativo para entregas e ocorreu-me de lidar com um problema que nunca tive antes. Como eu registro em meu banco de dados uma array de objetos relacionados?
A minha primeira idéia foi simplesmente criar um for
onde a cada key
da array eu registraria os valores do objeto, porém isso causava um erro na resposta do node.js que até agora, admito, não compreendo o por quê de ocorrer.
Durante este tutorial irei usar algumas imagens exemplificando supostos casos que você terá de arrays de objetos, sinta-se livre para fazer a modificação que quiser.
No mais, vamos direto ao código.
Primeiro Passo
Crie uma tabela em seu database
onde será salvo a array de objetos. A minha está assim. Sinta-se livre para modifica-la como bem entender.
create table orders(
id int NOT NULL AUTO_INCREMENT,
uuid varchar(200) NOT NULL,
storeId int NOT NULL,
product JSON DEFAULT NULL,
quantity integer,
price DECIMAL(10,2) DEFAULT 0.00
);
Peço que presta atenção em dois valores desta tabela.
- O primeiro sendo
storeId
. Isto será usado para relacionar a ordem do produto com a loja para qual será enviado. - Segundo,
product JSON DEFAULT NULL
, onde será salvo o produto(objeto)product
oriundo de outra tabela, relacionando de forma prática as duas tabelas na chamada do sistema.
ps: não sou especialista em sql, sinta-se livre para comentar abaixo a respeito desta parte.
Uma vez criada a tabela, ela se apresentará assim no MYSQL.
MariaDB [foodstore]> show columns from orders;
+----------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+---------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| uuid | varchar(200) | NO | | NULL | |
| storeId | int(11) | NO | | NULL | |
| product | longtext | YES | | NULL | |
| quantity | int(11) | YES | | NULL | |
| price | decimal(10,2) | YES | | 0.00 | |
+----------+---------------+------+-----+---------+----------------+
Caso queira saber mais sobre Mysql clique aqui.
Uma vez estabelecida a tabela, passamos para a array de objetos.
O objeto:
A array de objetos que iremos salvar na rota será similar a esta, lembrando que em uma situação real a array será enviada pelo usuário através do frontend de sua aplicação.
const list = [
{
id:"3845bf3c-a7b0-476d-8a96-c467b7a4a5b7",
price:2.5,
quantity:3.5,
product:{
storeId:1,
storeName:"Bbq",
id:1,
categoryId:1,
description:"a delicious product"
discoutnValue:null,
price:0.50,
productImage:....
}
},
{
id:"3845bf3c-a7b0-476d-8a96-c467b7a4a5b7",
price:2.5,
quantity:3.5,
product:{
storeId:1,
storeName:"Bbq",
id:1,
categoryId:1,
description:"a delicious product"
discoutnValue:null,
price:0.50,
productImage:....
}
}
....
]
Você pode repetir quantas vezes quiser o objeto dentro da array. Por questão de exemplo, fiz apenas duas vezes.
e agora, a rota.
Criação da Rota
Para salvarmos algo no banco de dados usamos a rota post
.
Primeiro irei te mostrar como eu criei a primeira rota, que me trouxe tanta dor de cabeça, e qual o erro que eu recebia do backend:
router.post("/send", (req, res, next)=>{
const orderDetails = req.body.list;
const query = "insert into orders (uuid, storeId, product, quantity, price) values(?,?,?,?,?)";
....
});
O código acima pode ser dividido em duas partes.
Primeiro crie uma constante para requesitar os dados enviados pelo frontend: const ordeDetails = req.body.list
, lembrando que list
neste caso se refere à array de objetos.
Depois, crie a query que irá inserir os dados em seu bancos de dados, neste caso MYSQL.
const query = "insert into insert into orders (uuid, storeId, product, quantity, price) values(?,?,?,?,?)
Caso tenha interesse em saber mais sobre a escrita de query.
Agora eu tenho minha query e minha array de objetos, porém como inserir este valor no banco de dados? Se você usar apenas o connection
para a realização da chamada, haverá um erro porque você está enviando uma array e o mysql não está esperando por isso. Portanto ele não saberá o que fazer e irá te retornar um erro. Como eu disse acima, a saída então seria a criação de um for
e para cada key
você passaria os valores requisitados pela query.
ficando assim:
router.post("/send", (req, res, next)=>{
const orderDetails = req.body.list;
const query = "insert into orders (uuid, storeId, product, quantity, price) values(?,?,?,?,?)";
for(let key in orderDetails){
const productDetail = JSON.stringify(orderDetails[key].product)
connection.query(query, [orderDetails[key].id, orderDetails[key].product.storeId,productDetail, orderDetails[key].quantity, orderDetails[key].price], (err, result)=>{
if(!err){
return res.status(200).json({message: "Order addded successfully"});
}
else
return res.status(500).json(err);
});
}
});
Desta forma o código irá funcionar, injetando cada objeto dentro da array e os seus respectivos valores dentro do banco de dados. Porém, desta forma, irá ocorrer um erro.
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
O motivo deste erro é porquê o nodejs compreende que você está realizando mais do que um res.json
, um retorno dentro de um retorno, e assim ele quebra o sistema. Se você retirar o retorno res
o código continua a funcionar, porém o frontend não será avisado do sucesso de sua ação. E então, o que fazer?
Se você está até aqui, parabéns. Desculpe pela demora, mas agora vamos direto ao ponto. Como realizar uma rota post para uma array de objetos em node.js e mysql?
Mudaremos apenas um pouco do código acima e a rota irá funcionar. Uma delas é retirar o for
e substitui-lo por um map
que irá retornar uma array com os valores requisitados pela query.
router.post("/send", (req, res, next)=>{
const orderDetails = req.body.list.map((item)=>[
item.id,
item.product.storeId,
JSON.stringify(item.product),
item.quantity,
item.price
])
....
}
Lembre-se de usar o JSON.stringifi()
para transformar o objeto product
em uma string. O mysql apenas compreende string.
Por fim, modificando a query
para:
const query = "insert into orders (uuid, storeId, product, quantity, price) values ?";
É necessário a retirada do `()` porque a biblioteca de conexão com o Mysql está transformando a nossa array de objetos em uma tupla. Recebendo apenas um valor ?
o Mysql irá olhar dentro dele e encontrará cada um dos valores necessários para realizar a sua ação.
De resto o código ficará assim.
router.post("/send", (req, res, next)=>{
const orderDetails = req.body.list.map((item)=>[
item.id,
item.product.storeId,
JSON.stringify(item.product),
item.quantity,
item.price
]);
const query = "insert into orders (uuid, storeId, product, quantity, price) values ?";
connection.query(query, [orderDetails],(err, result)=>{
if(!err){
return res.status(200).json({message:"it' work!"})
} else
return res.status(500).json(err);
})
});
Agora seu código irá rodar sem problemas.
Espero ter ajudado
Bom código. ;)