fcbk
Conéctate con TwitterConéctate con Facebook
0

Cómo crear un API JSON en PostgreSQL con NodeJS

0Puntos

hace 2 años

Una de las principales características que nos ofrece PostgreSQL es aprovechar la flexibilidad de datos no relacionales (NoSQL) almacenados en JSON. Y, al mismo tiempo, las capacidades de indexado y búsqueda de datos relacionales (SQL). Parece imposible, pero no lo es. Usar JSON y datos relacionales te permite aprovechar lo mejor de cada una, de acuerdo a tus necesidades. Para lograrlo, PostgreSQL soporta datos almacenados en JSON o en JSONB. La diferencia es que el primero guarda una copia exacta de todo el texto y JSONB lo hace en segmentos y, además, soporta indexado. JSONB es ligeramente más lento en escritura, pero mucho más rápido en búsqueda y lectura. Otra de sus ventajas es que ahorra espacio eliminando espacios en blanco y llaves duplicadas. Para conectar NodeJS con PostgreSQL usaremos una librería llamada MassiveJS (mi favorita). Para mostrar los datos usaremos la aplicación de ReactJS que hemos usado en otros ejemplos, aprovechando que ReactJS es fácil de reciclar.

En el backend

Antes de empezar a programar vamos a crear un archivo para guardar las configuraciones generales. Lo llamaremos config.json y ahí guardaremos el puerto que deseamos para Express y para PostgreSQL. Es necesario que ingresemos el nombre de la base de datos, el nombre de usuario, la contraseña y la IP de nuestro servidor. config.json [js] { "express" : { "port" : 80 }, "postgres": { "db" : "postgres", "user" : "postgres", "password" : "", "host" : "localhost" } } [/js] En Node.js empezaremos invocando las dependencias e importando las configuraciones que usaremos en este proyecto. server.js [js] // Import configuration var config = require('./config.json'); // Express var express = require('express'); // Serve-Static var serveStatic = require('serve-static'); // Body-Parser var bodyParser = require('body-parser'); // Multer var multer = require('multer'); // PostgreSQL var massive = require("massive"); [/js] Ahora vamos hacer una conexión a PostgreSQL y la almacenaremos en un objeto al que llamaremos massiveInstance. También vamos a declarar una variable vacía para almacenar los datos de nuestro modelo una vez realizada la conexión.
server.js
[js] var connectionString = "postgres://"+config.postgres.user+":"+config.postgres.password+"@"+config.postgres.host+"/"+config.postgres.db; var massiveInstance = massive.connectSync({connectionString : connectionString}); var db; [/js] Lo siguiente que haremos es crear los métodos que llamaremos para iniciar Express. Adicionalmente, guardaremos una instancia de este en un objeto llamado app que usaremos más adelante para configurar nuestro servidor de Express. Justo después de iniciar nuestro servidor vamos a guardar una instancia de la base de datos en el objeto db. Al final de nuestro código la vamos a configurar, cuando definamos la configuración de Express.
server.js
[js] // Initialize var app = express(); var startExpress = function() { app.listen(config.express.port); db = app.get('db'); } var initialize = function() { startExpress() } [/js] Es un buen momento para crear un método que llamaremos cuando tengamos un error en el servidor. Este enviará una respuesta al usuario con un error 500 y el mensaje de error completo.
server.js
[js] // Send back a 500 error var handleError = function(res) { return function(err){ console.log(err) res.send(500,{error: err.message}); } } [/js] Para nuestra API vamos a crear dos métodos, el primero será para enviar el contenido de la tabla que vamos a ocupar. Para este ejemplo usaremos una tabla llamada steps y vamos a buscar el objeto con el ID 1 con un método de Massive llamado findDoc, que nos facilita el filtrar el contenido de nuestra tabla. Es posible pasar un ID o cualquier parámetro, este sería equivalente a la cláusula de SQL SELECT * FROM steps WHERE (). Cuando guardemos un objeto JSONB en esta tabla, se le asignará un ID de manera automática. Para nuestro ejemplo, todo lo haremos con el objeto con el ID 1. Si estuvieras armando una base de usuarios, podrías crear un objeto por cada usuario con todos sus datos; y consultarlos por indice o filtrar con findDoc.
server.js
[js] // Retrieve table content var list = function(request, res, next) { db.steps.findDoc(1, function(err,doc){ if (err) { handleError(res) }; console.log(doc.data); res.json({ data: doc.data }); }); } [/js] Nuestro siguiente paso es crear el método que vamos a usar para actualizar el objeto en el que estamos guardando los datos. Para lograrlo, usaremos un método de Massive llamado saveDoc al que debemos pasar el objeto completo y el ID del objeto. De no dar un ID, se creará un objeto nuevo y se le asignará un ID automáticamente.
server.js
[js] // Insert an element on an existing object var update = function(request, res, next) { var newDoc = request.body.data; db.steps.saveDoc({id:1,data:newDoc}, function(err,response){ if (err) { handleError(res) }; console.log(response) res.json({ data: response }); }); } [/js] Finalmente, vamos a configurar Express. En este paso es necesario relacionar las rutas para nuestra API con los métodos que acabamos de crear. En este punto relacionaremos nuestro servidor  y la instancia de Massive, para finalmente llamar el método responsable de inicializar nuestra aplicación.
server.js
[js] // Express Setup // Data parsing app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use(multer()); // Define API routes app.route('/api/list').get(list); app.route('/api/update').post(update); // Static files server app.use(serveStatic('./public')); // Set a reference to the massive instance on Express' app: app.set('db', massiveInstance); initialize() [/js]

En el Frontend

Si es tu primera vez con ReactJS, te recomiendo este artículo con los conceptos básicos de una aplicación de ReactJS. Componentes Como podrás notar, nuestra aplicación está dividida en tres componentes. SimpleFilterableList es el de mayor jerarquía; y por lo tanto, en el vamos a definir los states que se pasarán como props a SimpleList. Este, a su vez, los pasará a SimpleListRow. Empezaremos por crear tres objetos, instance que usaremos para guardar el contexto de una instancia de ReactJS, dataArray en el que guardaremos una copia local de los datos y downloadData, el método que vamos a llamar cuando deseemos actualizar la copia local de datos y los states de nuestra aplicación. Recordemos que al cambiar el valor de un state ReactJS hará una actualización en cascada de sus componentes; a los que les pasará la información en forma de props. Para comunicarnos con nuestra API usaremos AJAX de jQuery.
reactApp.jsx
[js] var instance; var dataArray; var downloadData = function(){ $.ajax({ url: '/api/list', dataType: 'json', success: function(res) { dataArray = res.data; console.log(dataArray); instance.setState({simpleList: dataArray}); }.bind(instance), error: function(xhr, status, err) { console.error(instance.props.url, status, err.toString()) }.bind(instance) }); }; [/js] SimpleFilterableListposee dos inputs. Además del elemento SimpleList, el primer input lo usaremos para filtrar los datos que se muestran usando un método al que llamaremos updateUserInput. Si deseas aprender más sobre cómo filtrar vistas de ReactJS, te recomiendo visitar este artículo. El segundo input lo usaremos para enviar datos a nuestra API usando un método que llamaremos sendNewElement. Este se activará cuando el usuario presione Enter; y enviará el contenido del input por AJAX a nuestra API. Además, bloquea el input hasta que los datos se hayan enviado y se encarga de borrar el contenido una vez que este fue enviado. Después, pasaremos el contenido a mostrar y los valores del filtro al objeto SimpleListpor medio de sus props.
reactApp.jsx
[js] var SimpleFilterableList = React.createClass({ componentDidMount: function() { instance = this; downloadData(); }, getInitialState: function() { return { userInput: "", simpleList: [ { row: 'cargando ...' } ] }; }, updateUserInput: function(input){ this.setState({userInput: input.target.value}); }, sendNewElement: function(key){ if (key.key == "Enter"){ document.getElementById('newElement').disabled = true; var newObject = {row:document.getElementById('newElement').value}; dataArray.push(newObject); $.ajax({ url: "/api/update", type: "post", data: {"data":dataArray} }).done(function(response){ downloadData(); document.getElementById('newElement').value = ''; document.getElementById('newElement').disabled = false; document.getElementById("userInput").focus(); }); }; }, render: function(){ return ( <input id ='userInput' type ='text' placeholder ='Filtrar...' onChange ={this.updateUserInput}> </input> simpleList ={this.state.simpleList} userInput ={this.state.userInput}> <input id ='newElement' type ='text' placeholder ='+' onKeyPress ={this.sendNewElement} onClick ={this.favToInput} className ='fav'> </input> </div> ); } }); [/js] SimpleList actúa como un contenedor para SimpleListRow y le envía los datos de los props directamente. Podríamos encapsular la lógica que genera un renglón por cada elemento en this.props.simpleList pero resulta mas eficiente que SimpleListRow se encargue de eso. reactApp.jsx [js] var SimpleList = React.createClass({ render: function() { return ( <span> <p><strong>Pasos para dominar un nuevo lenguaje de programación:</strong></p> <SimpleListRow simpleList={this.props.simpleList} userInput={this.props.userInput}/> </span> ); } }); [/js] SimpleListRowse encarga de crear una fila por cada elemento de this.props.simpleList y a la vez muestra elementos cuyo contenido contenga coincidencias con el de this.props.userInput.reactApp.jsx [js] var SimpleListRow = React.createClass({ render: function() { var rows = this.props.simpleList; var userInput = this.props.userInput; return ( <ol> {rows.map(function(element){ if (element.row){ if (element.row.toLowerCase().search(userInput.toLowerCase()) > -1){ return ( <li>{element.row}</li> ) } } })} </ol> ); } }); [/js] Finalmente, sólo nos falta asignar el lugar en el DOM en el que deseamos que ReactJS inyecte a SimpleFilterableList.
reactApp.jsx
[js] React.render( <SimpleFilterableList />, document.getElementById('list') ) [/js] ¡Y listo! Ahora conoces un full-stack que incluye PostgreSQL como Modelo, Node.js como Controlador y ReactJS como Visualización. Te invito a que hagas fork al repositorio en Github y nos compartas tus ideas, mejoras y creaciones con lo que hoy aprendimos. Si deseas aprender más sobre este y otros secretos tenemos cursos de PostgreSQL, ReactJS y de Node.js en Platzi. Si te interesa algún tema en específico, no dudes en dejarlo en los comentarios.
Cesar
Cesar
@reicek

0Puntos

hace 2 años