Hasta este momento hemos creado unos controladores que nos permiten obtener, crear y eliminar, cuando se elimina algo o cuando se obtiene un único empleado, estuvimos utilizando req.params.id
, qué es una forma de recibir un parámetro desde una URL. Esto sirvió para buscar un elemento y eliminarlo o para buscarlo y devolverlo al cliente. Esta misma idea nos va a servir para poder buscar un único empleado y actualizarlo. Para actualizar un elemento necesitamos enviar la información que queremos modificar y el id
del elemento que queremos actualizar.
Para poder actualizarlo hay que pasarle nuevos datos, para esto también se va a utilizar una propiedad que viene de req.body
.
Entonces para poder actualizar un elemento, voy a utilizar ambos, el req.params.id
y el req.body
, de momento solo vamos a localizar la función updateEmployees
, para hacer esto vamos a ir al archivo employees.controller.js
y vamos a localizar la siguiente función:
const updateEmployees = (req, res) => res.send('actualizando empleados')
Como hicimos con las demás funciones vamos a quitarle la función y le vamos a colocar las llaves, dentro de estas llaves se va a estar recibiendo ambos valores.
const updateEmployees = (req, res) => {
}
Para este caso, mientras, vamos a estar viendo req.params
y req.body
por consola y enviando un mensaje 'recibido'
const {id} = req.params
const {name, salary} = req.body
console.log(id, name, salary)
res.json('Recibido')
Con esto ya tenemos una forma de poder comprobar lo que estamos recibiendo a través del id
, name
y salary
.
Ahora vamos a ir al archivo employees.routes.js
y vamos a agregar el parámetro /;id
en la ruta:
router.put('/employees/:id', updateEmployees )
Guardamos y una vez que tenemos esto listo vamos a enviarlo. Vemos en la consola, que el servidor se está ejecutando normalmente, es decir, que no ha marcado ningún error.
Comprobando los datos recibidos
A partir de aquí vamos a enviar nuestra petición utilizando Thunder client, de todos los empleados voy a decirle que va a actualizar el empleado con id
1.
Esto es tan solo un ejemplo, aún no va a actualizar ningún elemento, porque no estamos enviándole ningún dato para que actualice.
Para hacer esto vamos a dirigirnos a la pestaña body
de Thunder client y enviar un dato tipo JSON. Este dato es un objeto de actualización, donde le vamos a poner un nombre y un salario.
{
"name": "Kate",
"salary": 50
}
Como podemos ver, responde el mensaje 'Recibido'
.
Ahora vamos a ir a la consola de nuestro servidor, y vamos a ver que efectivamente está mostrando el id
del nuevo nombre Kate y el nuevo salario.
Entendiendo la consulta SQL para actualizar los datos.
Estos datos se lo podemos pasar a una consulta SQL
, acá viene el detalle para actualizar un dato. Primero vamos a ir al archivo employees.controller.js
y vamos a modificar la función así:
const updateEmployees = async(req, res) => {
try {
const {id} = req.params
const {name, salary} = req.body
const result = await query('UPDATE employee SET name = ?, salary = ? WHERE id = ?', [name, salary, id])
if(result.affectedRows === 0) return res.status(404).json({
message: 'Empleado no encontrado'
})
console.log(result)
res.sendStatus(204)
} catch (error) {
console.error(error)
res.status(500).send('Error al obtener la información')
}
}
Vamos a utilizar el método query()
de la misma forma como lo hicimos anteriormente, pero en este caso vamos a hacer una consulta UPDATE
.
UPDATE employee SET name = ?, salary = ? WHERE id = ?
Esta consulta podría traducirse que desde la tabla employees
se establece un name
que va a ser igual a un valor que le voy a pasar después. Lo mismo con el salary
para eso ponemos una coma y escribimos salary
es igual un valor que se lo vamos a pasar después. Se necesita establecer un WHERE
para indicar a cuál id se está refiriendo.
Entonces los campos que le voy a pasar van a ser name
, salary
, id
es importante mantener el orden de estos campos para que coincidan con los datos que se le están pasando.
De esta forma vamos a obtener un resultado y lo vamos a guardar en una constante llamada result
. Este valor result
lo vamos a ver por consola.
Guardamos y vemos que el servidor se ha reiniciado y todo sigue funcionando correctamente. Nuevamente, vamos a Thunder client para hacer la misma consulta, para esto vamos a ir a la ruta api/employees/16
realizando una consulta PUT y enviamos el siguiente objeto de actualización:
{
"name" : "Kate",
"salary": 50
}
Acá está buscando el id
16 y cuando lo encuentre va a actualizar el nombre y su salario. Cuando termine de hacer la consulta enviará un mensaje que dice 'Recibido'
, que si vamos a la consola del servidor vamos a ver que nos muestra este objeto:
{
fieldCount: 0,
affectedRows: 1,
insertId: 0,
serverStatus: 2,
warningCount: 0,
message: '(Rows matched: 1 Changed: 1 Warnings: 0',
protocol41: true,
changedRows: 1
}
La propiedad affectedRows
está diciendo que existe 1 fila afectada, es decir que la actualización de ese dato para ese id
fue realizada exitosamente.
Validando la actualización de un elemento
Ahora qué pasa, si se quiere, actualizar un id
que no existe, por ejemplo el id
160 que sabemos que no existe, en este caso vamos a hacer uso de la condición de validación.
if(result.affectedRows === 0) return res.status(404).json({
message: 'Empleado no encontrado'
})
En este ejemplo, si la propiedad affectedRows
===
0
significa que no afectó a ninguna fila, por lo tanto, no actualizó nada, en ese momento debería enviar un mensaje que diga 'empleado no encontrado'
.
Ahora algo que sería de mucha utilidad es que si actualiza algo, que devuelva eso que actualizó.
Devolviendo el dato actualizado
El asunto aquí es que cuando en MySQL actualizamos algo no devuelve el dato que ha actualizado, sino que devuelve un objeto de resultado.
Entonces, para traer ese dato tengo que nuevamente hacer una consulta SELECT
y devolver el dato.
Para hacer esto, tendríamos que, en el momento que estemos seguros de que el empleado fue actualizado, hacer una consulta SELECT
que nos permita obtener ese dato.
Entonces para esto nuevamente vamos a utilizar el método query()
que es el que nos trae nuevamente la conexión junto con pool
, quedaría algo así:
const rows = await query('SELECT * FROM employee WHERE id = ?', [id])
Acá selecciona desde la tabla employees donde el id
es igual al id
, que se le va a pasar, vamos a pasar el id
que justamente ya actualizamos.
Una vez que haga la consulta, va a devolver un arreglo que lo almacena en una constante llamada rows
.
Entonces le vamos a decir que a partir de estas filas me las va a devolver a través del método json()
.
res.json(rows)
Guardamos y vamos a actualizar el empleado 1 a través del método PUT
, podemos ver que ahora me responde un arreglo con un solo objeto.
[
{
"id": 2,
"name": "Katy",
"salary": 500
}
]
Nosotros ahora estamos actualizando un dato y no está bien que devuelva un arreglo. Entonces en la respuesta json()
le vamos a decir que devuelva solamente el primer elemento del arreglo de esta manera.
res.json(rows[0])
Si guardamos y vamos ahora a la extensión Thunder client nuevamente hacemos la consulta, vemos que ahora en vez de un arreglo nos va a dar un objeto.
{
"id": 2,
"name": "Katy",
"salary": 500
}
Utilizar PATCH en lugar de PUT
A partir de aquí pasa algo que tenemos que tener en cuenta, Cuando nosotros utilizamos el método PUT
, Estamos diciendo de cierta forma que quiero actualizar todos los datos de cada empleado.
Es por eso que cuando actualizamos con el método PUT
le pasamos el nombre y el salario, de esa manera estaríamos actualizando toda la entidad.
Pero qué pasa si solo se desea actualizar un dato. Por ejemplo, si solo queremos actualizar el nombre, entonces lo lógico sería que creemos un objeto con el nombre que queremos actualizar y nada más, de esta manera:
{
"name" : "Laura"
}
Si enviamos esta solicitud de esta manera con un solo dato vamos a obtener lo siguiente:
{
"id": 2,
"name": "Laura",
"salary": null
}
Me envía el id
que está bien, el name
: "Laura"
actualizado, que está bien, pero me envía la propiedad salary
en null
cosa que está mal. Está colocando null
porque al momento de enviar, no estamos enviando ese dato, entonces al momento de recibir la consulta en esta parte precisa de la función:
const result = await query('UPDATE employee SET name = ?, salary = ? WHERE id = ?', [name, salary, id])
No le estamos pasando el salario y la función lo toma como undefined
, y al ser undefined
lo que hace la biblioteca mysql
es pasarlo a null
.
Entonces en la base de datos se está actualizando como un null
. Por lo tanto, no está bien porque va a haber ocasiones donde se va a querer actualizar una parte de ese objeto, es decir, una parte de ese empleado. No tengo por qué enviarle nuevamente el mismo dato. Una mejor forma de definir esta ruta es en vez de utilizar el método PUT, vamos a utilizar el método PATCH.
Entendiendo el método PATCH
La petición PATCH es muy similar a la petición PUT, solo que esta petición nos permite actualizar parcialmente un elemento o entidad. Ahora no hay ningún inconveniente real en utilizar PUT o PATCH.
Es solo una consideración de REST. Por ejemplo, REST dice que si queremos actualizar toda la entidad utilicemos el método PUT y si queremos actualizar parcialmente que utilicemos el método PATCH.
Esto es para darle una mejor idea al cliente de lo que realiza esta operación. En este caso en particular viene bastante bien porque vamos a querer actualizar algunos datos y no todos.
La ruta employees.routes.js
quedaría de la siguiente manera.
router.patch('/employees/:id', updateEmployees )
Guardamos y de momento es lo mismo, sin embargo, si vamos a employees.controller.js
en la función updateEmployees
en la parte donde hace la consulta a la base de datos sigue realizando la misma operación.
const result = await query('UPDATE employee SET name = ?, salary = ? WHERE id = ?', [name, salary, id])
Viendo como opera IFNULL()
Pero como solo queremos actualizar parcialmente ese dato, tenemos que utilizar una palabra clave que tiene SQL
que se llama IFNULL()
de la siguiente forma:
const result = await query('UPDATE employee SET name = IFNULL(?, name), salary = IFNULL(?, salary) WHERE id = ?', [name, salary, id])
El IFNULL(?, name)
lo que hace es que si se le pasa un valor dónde está el ?
, SQL lo va a establecer con el valor que ya tenía, es decir, lo va a dejar tal cual y no lo va a modificar.
Es como decir, si hay null
no le pasó nada y lo deja con el valor que ya tenía. Por el contrario, si el valor está vacío, utiliza el name
y es lo que va a establecer ahí. Lo mismo con salary
si el dato que se está pasando es null
utiliza el valor que ya tenía, es decir, el salary
que ya tenía. El WHERE id
no es necesario modificar porque sí o sí se lo tenemos que pasar.
Guardamos y vamos nuevamente a la extensión Thunder client y enviamos una petición PATCH
a la misma ruta. Solamente que en esta ocasión vamos a actualizar nada más que el salario, para esto creamos un objeto.
{
"salary": 10000
}
Vemos que envía un objeto de actualización manteniendo el nombre que era Laura, pero con el salario modificado y así podemos actualizar parcialmente un elemento.