Solución:
Siempre puedes implementar una función como esta:
function updateProductByID (id, cols) {
// Setup static beginning of query
var query = ['UPDATE products'];
query.push('SET');
// Create another array storing each set command
// and assigning a number value for parameterized query
var set = [];
Object.keys(cols).forEach(function (key, i) {
set.push(key + ' = ($' + (i + 1) + ')');
});
query.push(set.join(', '));
// Add the WHERE statement to look up by id
query.push('WHERE pr_id = ' + id );
// Return a complete query string
return query.join(' ');
}
Y luego úselo como tal:
/*
Post /api/project/products/:pr_id HTTP/1.1
*/
exports.updateProduct = function(req, res){
pg.connect(cs, function(err, client, done) {
// Setup the query
var query = updateProductByID(req.params.pr_id, req.body);
// Turn req.body into an array of values
var colValues = Object.keys(req.body).map(function (key) {
return req.body[key];
});
client.query(query, colValues, function(err, result) {
if (handleErr(err, done)) return;
done();
sendResponse(res, result.rows[0]);
});
});
};
O, si un ORM es algo que necesita porque estará haciendo mucho como lo anterior, debería revisar módulos como Knex.js
Ya se han dado buenas respuestas, pero en mi humilde opinión no lo suficientemente buenas en un aspecto, todas carecen de una buena abstracción. Intentaré proporcionar una forma más abstracta de actualizar sus datos en postgres
utilizando node-postgres
.
Siempre es una buena práctica seguir la documentación oficial, la siguiente estructura de código se tomó de node-postgres, puede extenderla como desee:
aquí está el mío, aquí es donde interactúas con tu base de datos
const { Pool } = require("pg");
const connection = require("./connection.json");
const pool = new Pool(connection);
const { insert, select, remove, update } = require("./helpers");
/**
* The main mechanism to avoid SQL Injection is by escaping the input parameters.
* Any good SQL library should have a way to achieve this.
* PG library allows you to do this by placeholders `($1, $2)`
*/
module.exports = {
query: (text, params, callback) => {
const start = Date.now();
return pool.query(text, params, (err, res) => {
const duration = Date.now() - start;
console.log("executed query", { text, duration, rows: res.rowCount });
callback(err, res);
});
},
getClient: callback => {
pool.connect((err, client, done) => {
const query = client.query;
// monkey patch the query method to keep track of the last query executed
client.query = (...args) => {
client.lastQuery = args;
return query.apply(client, args);
};
// set a timeout of 5 seconds, after which we will log this client's last query
const timeout = setTimeout(() => {
console.error("A client has been checked out for more than 5 seconds!");
console.error(
`The last executed query on this client was: ${client.lastQuery}`
);
}, 5000);
const release = err => {
// call the actual 'done' method, returning this client to the pool
done(err);
// clear our timeout
clearTimeout(timeout);
// set the query method back to its old un-monkey-patched version
client.query = query;
};
callback(err, client, release);
});
},
/**
* Updates data
*
* entity: table name, e.g, users
* conditions: { id: "some-unique-user-id", ... }
* fields: list of desired columns to update { username: "Joe", ... }
*/
updateOne: async (entity, conditions, fields) => {
if (!entity) throw new Error("no entity table specified");
if (Utils.isObjEmpty(conditions))
throw new Error("no conditions specified");
let resp;
const { text, values } = update(entity, conditions, fields);
try {
rs = await pool.query(text, values);
resp = rs.rows[0];
} catch (err) {
console.error(err);
throw err;
}
return resp;
},
createOne: async (entity, data) => {
},
deleteOne: async (entity, conditions, data) => {
},
findAll: async (entity, conditions, fields) => {
},
// ... other methods
};
aquí hay métodos auxiliares para las operaciones CRUD, ellos prepararán el texto de la consulta con los valores preparados:
/**
* tableName: `users`
* conditions: { id: 'joe-unique-id', ... }
* data: { username: 'Joe', age: 28, status: 'active', ... }
*
* "UPDATE users SET field_1 = $1, field_2 = $2, field_3 = $3, ... ( WHERE ...) RETURNING *";
*/
exports.update = (tableName, conditions = {}, data = {}) => {
const dKeys = Object.keys(data);
const dataTuples = dKeys.map((k, index) => `${k} = $${index + 1}`);
const updates = dataTuples.join(", ");
const len = Object.keys(data).length;
let text = `UPDATE ${tableName} SET ${updates} `;
if (!Utils.isObjEmpty(conditions)) {
const keys = Object.keys(conditions);
const condTuples = keys.map((k, index) => `${k} = $${index + 1 + len} `);
const condPlaceholders = condTuples.join(" AND ");
text += ` WHERE ${condPlaceholders} RETURNING *`;
}
const values = [];
Object.keys(data).forEach(key => {
values.push(data[key]);
});
Object.keys(conditions).forEach(key => {
values.push(conditions[key]);
});
return { text, values };
};
exports.select = (tableName, conditions = {}, data = ["*"]) => {...}
exports.insert = (tableName, conditions = {}) => {...}
exports.remove = (tableName, conditions = {}, data = []) => {...}
Y finalmente puede usar esto en sus controladores de ruta sin saturar su base de código:
const db = require("../db");
/**
*
*/
exports.updateUser = async (req, res) => {
try {
console.log("[PUT] {api/v1/users}");
const fields = {
name: req.body.name,
description: req.body.description,
info: req.body.info
};
const userId = req.params.id;
const conditions = { id: userId };
const updatedUser = await db.updateOne("users", conditions, fields);
if (updatedUser) {
console.log(`team ${updatedUser.name} updated successfully`);
return res.json(updatedUser);
}
res.status(404).json({ msg: "Bad request" });
} catch (err) {
console.error(err);
res.status(500).send({ msg: "Server error" });
}
};
Utilidades convenientes:
const Utils = {};
Utils.isObject = x => x !== null && typeof x === "object";
Utils.isObjEmpty = obj => Utils.isObject(obj) && Object.keys(obj).length === 0;
Me gusta usar knexjs, que funciona con postgre. También es una forma divertida de escribir consultas en JavaScript (sin toda esa desagradable manipulación de cadenas SQL).
Tomemos, por ejemplo, este método, que almacena cierta información de contacto. El esquema JSON de esa información de contacto se define en otro lugar (también es útil cuando valido). El resultado es una consulta generada por código, que contiene solo columnas pasadas.
function saveContactInfo( inputs, callback ) {
var setObj = {};
for( var property in inputs.contact )
{
//assumes properties are same as DB columns, otherwise need to use some string-mapping lookup.
setObj[ property ] = inputs.contact[property];
}
setObj[ "LastModified" ] = new Date();
var query = knex( "tblContact" ).update( setObj ).where( "contactId", inputs.contact.contactId );
//log.debug("contactDao.saveContactInfo: " + query.toString());
query.exec( function(err, results ){
if(err) return callback(err);
//Return from DB is usually an array, so return the object, not the array.
callback( null, results[0] );
});
}
Knexjs también tiene algunas opciones ingeniosas solo de postgre (que me hubieran sido útiles si no hubiera estado usando MySQL)