Cómo usar PostgreSQL con Node.js

Fuente: PostgreSQL y NodeJS

2 años de edad, lo que probablemente significa “desactualizado” hoy, pero aquí está

Configuración del proyecto

Comience instalando el generador Express:

$ npm install -g [correo electrónico protegido]

Luego cree un nuevo proyecto e instale las dependencias:

$ express nodo-postgres-todo
$ cd node-postgres-todo && npm install

Agregar supervisor para ver los cambios de código:

$ npm install [correo electrónico protegido] -g

Actualice el script de start en el archivo package.json :

“guiones”: {
“start”: “supervisor ./bin/www”
},

Ejecuta la aplicación:

$ npm start

Luego navegue a http: // localhost: 3000 / en su navegador. Debería ver el texto “Bienvenido a Express”.

Configuración de Postgres

¿Necesita configurar Postgres? En una Mac? Echa un vistazo a Postgres.app.

Con su servidor Postgres funcionando en el puerto 5432, hacer una conexión de base de datos es fácil con la biblioteca pg:

$ npm install [correo electrónico protegido] –save

Ahora configuremos un script de creación de tabla simple:

const pg = require (‘pg’);
const connectionString = process.env.DATABASE_URL || ‘postgres: // localhost: 5432 / todo’;

const client = nuevo pg.Client (connectionString);
client.connect ();
const query = client.query (
‘CREAR elementos de TABLA (id SERIAL PRIMARY KEY, text VARCHAR (40) not null, complete BOOLEAN)’);
query.on (‘end’, () => {client.end ();});

Guarde esto como database.js en una nueva carpeta llamada “modelos”.

Aquí creamos una nueva instancia de Client para interactuar con la base de datos y luego establecer comunicación con ella a través del método connect() . Luego ejecutamos una consulta SQL a través del método query() . Finalmente, la comunicación se cierra a través del método end() . Asegúrese de consultar la documentación para obtener más información.

Asegúrese de tener una base de datos llamada “todo” creada, y luego ejecute el script para configurar la tabla y los campos siguientes:

$ node models / database.js

Verifique la creación de la tabla / esquema en psql:

$ psql
# \ c todo
Ahora está conectado a la base de datos “todo” como usuario “michaelherman”.
todo = # \ d elementos
Tabla “public.items”
Columna | Tipo | Modificadores
———- + ———————– + —————————————————-
id | entero | nextval no nulo predeterminado (‘items_id_seq’ :: regclass)
texto | carácter variable (40) | no nulo
completa | booleano |
Índices:
CLAVE PRIMARIA “items_pkey”, btree (id)

todo = #

Con la configuración de la conexión de la base de datos junto con la tabla de “elementos”, ahora podemos configurar la parte CRUD de nuestra aplicación.

Lado del servidor: rutas

Para simplificarlo, agreguemos todos los puntos finales al archivo index.js dentro de la carpeta “rutas”. Asegúrese de actualizar las importaciones:

const express = require (‘express’);
const router = express.Router ();
const pg = require (‘pg’);
const path = require (‘ruta’);
const connectionString = process.env.DATABASE_URL || ‘postgres: // localhost: 5432 / todo’;

Ahora, agreguemos cada punto final.

Función

URL

Acción

CREAR

/ api / v1 / todos

Crea un solo todo

LEER

/ api / v1 / todos

Consigue todos

ACTUALIZAR

/ api / v1 / todos /: todo_id

Actualizar un solo todo

BORRAR

/ api / v1 / todos /: todo_id

Eliminar un solo todo

Siga los comentarios en línea a continuación para obtener una explicación de lo que sucede. Además, asegúrese de consultar la documentación de la página para obtener información sobre la agrupación de conexiones. ¿Cómo difiere eso de la pg.Client .

Crear

router.post (‘/ api / v1 / todos’, (req, res, next) => {
resultados const = [];
// Toma datos de la solicitud http
const data = {text: req.body.text, complete: false};
// Obtenga un cliente de Postgres del grupo de conexiones
pg.connect (connectionString, (err, client, done) => {
// Manejar errores de conexión
si (err) {
hecho();
console.log (err);
return res.status (500) .json ({éxito: falso, datos: err});
}
// Consulta SQL> Insertar datos
client.query (‘INSERTAR EN elementos (texto, completo) valores ($ 1, $ 2)’,
[data.text, data.complete]);
// Consulta SQL> Seleccionar datos
const query = client.query (‘SELECCIONAR * DE los elementos ORDER BY id ASC’);
// Transmitir resultados una fila a la vez
query.on (‘row’, (row) => {
resultados.push (fila);
});
// Después de que se devuelvan todos los datos, cierre la conexión y devuelva los resultados
query.on (‘end’, () => {
hecho();
return res.json (resultados);
});
});
});

Pruebe esto a través de Curl en una nueva ventana de terminal:

$ curl –data “text = test & complete = false” http://127.0.0.1:3000/api/v1/todos

Luego confirme que los datos se INSERTARON correctamente en la base de datos a través de psql:

todo = # SELECT * FROM items ORDER BY id ASC;
id | texto | completar
—- + ——- + ———-
1 | prueba | F
(1 fila)

Leer

router.get (‘/ api / v1 / todos’, (req, res, next) => {
resultados const = [];
// Obtenga un cliente de Postgres del grupo de conexiones
pg.connect (connectionString, (err, client, done) => {
// Manejar errores de conexión
si (err) {
hecho();
console.log (err);
return res.status (500) .json ({éxito: falso, datos: err});
}
// Consulta SQL> Seleccionar datos
const query = client.query (‘SELECCIONAR * DE los elementos ORDER BY id ASC;’);
// Transmitir resultados una fila a la vez
query.on (‘row’, (row) => {
resultados.push (fila);
});
// Después de que se devuelvan todos los datos, cierre la conexión y devuelva los resultados
query.on (‘end’, () => {
hecho();
return res.json (resultados);
});
});
});

Agregue algunas filas más de datos a través de Curl y luego pruebe el punto final en su navegador en http: // localhost: 3000 / api / v1 / todos. Debería ver una matriz de objetos JSON:

[
{
id: 1
texto: “prueba”,
completo: falso
},
{
id: 2
texto: “test2”,
completo: falso
},
{
id: 3
texto: “test3”,
completo: falso
}
]

Actualizar

router.put (‘/ api / v1 / todos /: todo_id’, (req, res, next) => {
resultados const = [];
// Captura datos de los parámetros de URL
const id = req.params.todo_id;
// Toma datos de la solicitud http
const data = {text: req.body.text, complete: req.body.complete};
// Obtenga un cliente de Postgres del grupo de conexiones
pg.connect (connectionString, (err, client, done) => {
// Manejar errores de conexión
si (err) {
hecho();
console.log (err);
return res.status (500) .json ({éxito: falso, datos: err});
}
// Consulta SQL> Actualizar datos
client.query (‘ACTUALIZAR elementos SET text = ($ 1), complete = ($ 2) WHERE id = ($ 3)’,
[data.text, data.complete, id]);
// Consulta SQL> Seleccionar datos
const query = client.query (“SELECCIONAR * DE los elementos ORDER BY id ASC”);
// Transmitir resultados una fila a la vez
query.on (‘row’, (row) => {
resultados.push (fila);
});
// Después de que se devuelvan todos los datos, cierre la conexión y devuelva los resultados
query.on (‘end’, function () {
hecho();
return res.json (resultados);
});
});
});

Nuevamente, pruebe a través de Curl:

1

$ curl -X PUT –data “text = test & complete = true” http://127.0.0.1:3000/api/v1/todos/1

Navegue a http: // localhost: 3000 / api / v1 / todos para asegurarse de que los datos se hayan actualizado correctamente.

[
{
id: 1
texto: “prueba”,
completo: verdadero
},
{
id: 2
texto: “test2”,
completo: falso
},
{
id: 3
texto: “test3”,
completo: falso
}
]

Borrar

router.delete (‘/ api / v1 / todos /: todo_id’, (req, res, next) => {
resultados const = [];
// Captura datos de los parámetros de URL
const id = req.params.todo_id;
// Obtenga un cliente de Postgres del grupo de conexiones
pg.connect (connectionString, (err, client, done) => {
// Manejar errores de conexión
si (err) {
hecho();
console.log (err);
return res.status (500) .json ({éxito: falso, datos: err});
}
// Consulta SQL> Eliminar datos
client.query (‘DELETE FROM items WHERE id = ($ 1)’, [id]);
// Consulta SQL> Seleccionar datos
var query = client.query (‘SELECCIONAR * DESDE elementos ORDER BY id ASC’);
// Transmitir resultados una fila a la vez
query.on (‘row’, (row) => {
resultados.push (fila);
});
// Después de que se devuelvan todos los datos, cierre la conexión y devuelva los resultados
query.on (‘end’, () => {
hecho();
return res.json (resultados);
});
});
});

Prueba final de rizo:

$ curl -X DELETE http://127.0.0.1:3000/api/v1/todos/3

Y ahora deberías tener:

[
{
id: 1
texto: “prueba”,
completo: verdadero
},
{
id: 2
texto: “test2”,
completo: falso
}
]

Refactorización

Antes de saltar al lado del cliente para agregar Angular, tenga en cuenta que nuestro código debe ser refactorizado para abordar algunos problemas. Nos ocuparemos de esto más adelante en este tutorial, pero esta es una excelente oportunidad para refactorizar el código por su cuenta. ¡Buena suerte!

Lado del cliente: angular

Vamos a sumergirnos en Angular.

Tenga en cuenta que esto no pretende ser un tutorial exhaustivo. Si eres nuevo en Angular, te sugiero que sigas mi tutorial “AngularJS por ejemplo”: creación de una calculadora de inversión de Bitcoin.

Módulo

Cree un archivo llamado app.js en la carpeta “public / javascripts”. Este archivo albergará nuestro módulo angular y controlador:

angular.module (‘nodeTodo’, [])
.controller (‘mainController’, ($ scope, $ http) => {
$ scope.formData = {};
$ scope.todoData = {};
// Obtén todos los todos
$ http.get (‘/ api / v1 / todos’)
.success ((datos) => {
$ scope.todoData = datos;
console.log (datos);
})
.error ((error) => {
console.log (‘Error:’ + error);
});
});

Aquí definimos nuestro módulo y el controlador. Dentro del controlador, estamos utilizando el servicio $ http para realizar una solicitud AJAX al punto final '/api/v1/todos' y luego actualizar el alcance en consecuencia.

¿Qué más está pasando?

Bueno, estamos inyectando los servicios $scope y $http . Además, estamos definiendo y actualizando $scope para manejar el enlace.

Actualizar ruta principal

Actualicemos la ruta principal en index.js dentro de la carpeta “rutas”:

router.get (‘/’, (req, res, next) => {
res.sendFile (‘index.html’);
});

Entonces, cuando el usuario llega al punto final principal, enviamos el archivo index.html . Este archivo contendrá nuestras plantillas HTML y Angular.

Asegúrese de agregar también la siguiente dependencia:

const path = require (‘ruta’);

Ver

Ahora, agreguemos nuestra vista angular básica en index.html :

Aplicación Todo – con Node + Express + Angular + PostgreSQL

  • {{todo.text}}

Agregue esto a la carpeta “pública”.

Todo esto debería ser sencillo. Iniciamos Angular – ng-app="nodeTodo" , definimos el alcance del controlador – ng-controller="mainController" – y luego usamos ng-repeat para recorrer el objeto todoData , agregando cada todoData individual a la página.

Módulo (ronda dos)

A continuación, actualice el módulo para manejar las funciones de creación y eliminación:

// Crea un nuevo todo
$ scope.createTodo = () => {
$ http.post (‘/ api / v1 / todos’, $ scope.formData)
.success ((datos) => {
$ scope.formData = {};
$ scope.todoData = datos;
console.log (datos);
})
.error ((error) => {
console.log (‘Error:’ + error);
});
};
// Eliminar un todo
$ scope.deleteTodo = (todoID) => {
$ http.delete (‘/ api / v1 / todos /’ + todoID)
.success ((datos) => {
$ scope.todoData = datos;
console.log (datos);
})
.error ((datos) => {
console.log (‘Error:’ + datos);
});
};

Ahora, vamos a actualizar nuestra vista …

Vista (segunda ronda)

Simplemente actualice el elemento de la lista de esta manera:

  • & nbsp; {{todo.text}}
  • Esto usa la directiva ng-click para llamar a la función deleteTodo() que toma una id única asociada con cada tarea como argumento.

    Prueba esto. Asegúrese de que cuando hace clic en una casilla de verificación, todo se elimina

    Vista (ronda tres)

    Para manejar la creación de un nuevo todo, necesitamos agregar un formulario HTML:

    • & nbsp; {{todo.text}}

    Nuevamente, usamos ng-click para llamar a la función createTodo() en el controlador. ¡Prueba esto!

    Vista (cuarta ronda)

    Con la funcionalidad principal hecha, actualicemos el front-end para que se vea un poco más presentable.

    HTML :

    Aplicación Todo – con Node + Express + Angular + PostgreSQL

    Aplicación Todo


    Nodo + Express + Angular + PostgreSQL

    • & nbsp; {{todo.text}}


    CSS :

    cuerpo {
    acolchado: 50px;
    fuente: 14px “Lucida Grande”, Helvetica, Arial, sans-serif;
    }

    una {
    color: # 00B7FF;
    }

    ul {
    tipo-estilo-lista: ninguno;
    relleno-izquierda: 10px;
    }

    .envase {
    ancho máximo: 400 px;
    color de fondo: #eeeeee;
    borde: 1px negro sólido;
    }

    .header {
    alinear texto: centro;
    }

    ¿Como es que? ¿No está a la altura? Continúa trabajando en ello por tu parte.

    Refactorización (de verdad)

    Ahora que agregamos la funcionalidad de front-end, vamos a actualizar la estructura de la aplicación y refactorizar partes del código.

    Estructura

    Dado que nuestra aplicación se divide lógicamente entre el cliente y el servidor, hagamos lo mismo para nuestra estructura de proyecto. Por lo tanto, realice los siguientes cambios en la estructura de su carpeta:

    ├── app.js
    ├── bin
    │ └── www
    ├── cliente
    Vas ├── javascripts
    │ │ └── app.js
    │ ├── hojas de estilo
    │ │ └── style.css
    └── └── vistas
    │ └── index.html
    ├── package.json
    └── servidor
    ├── modelos
    │ └── database.js
    └── rutas
    └── index.js

    Ahora necesitamos hacer algunas actualizaciones al código:

    servidor / rutas / index.js :

    router.get (‘/’, (req, res, next) => {
    res.sendFile (path.join (
    __dirname, ‘..’, ‘..’, ‘cliente’, ‘vistas’, ‘index.html’));
    });

    app.js :

    const express = require (‘express’);
    const path = require (‘ruta’);
    const favicon = require (‘serve-favicon’);
    const logger = require (‘morgan’);
    const cookieParser = require (‘cookie-parser’);
    const bodyParser = require (‘body-parser’);

    rutas const = require (‘./ server / routes / index’);
    // var users = require (‘./ routes / users’);

    aplicación const = express ();

    // ver configuración del motor
    // app.set (‘views’, path.join (__ dirname, ‘views’));
    // app.set (‘view engine’, ‘html’);

    // descomenta después de colocar tu favicon en / public
    //app.use(favicon(path.join(__dirname, ‘public’, ‘favicon.ico’)));
    app.use (logger (‘dev’));
    app.use (bodyParser.json ());
    app.use (bodyParser.urlencoded ({extended: false}));
    app.use (cookieParser ());
    app.use (express.static (path.join (__ dirname, ‘cliente’)));

    app.use (‘/’, rutas);
    // app.use (‘/ users’, usuarios);

    // captura 404 y reenvía al controlador de errores
    app.use ((req, res, next) => {
    var err = nuevo error (‘No encontrado’);
    err.status = 404;
    siguiente (err);
    });

    // manejadores de errores

    // controlador de errores de desarrollo
    // imprimirá stacktrace
    if (app.get (‘env’) === ‘desarrollo’) {
    app.use ((err, req, res, next) => {
    res.status (err.status || 500);
    res.json ({
    mensaje: err.message,
    error: err
    });
    });
    }

    // controlador de errores de producción
    // no se filtraron stacktraces al usuario
    app.use ((err, req, res, next) => {
    res.status (err.status || 500);
    res.json ({
    mensaje: err.message,
    error: {}
    });
    });

    module.exports = app;

    Función de utilidad

    ¿Notó en nuestras rutas que estamos reutilizando gran parte del mismo código en cada una de las funciones CRUD:

    pg.connect (connectionString, (err, client, done) => {
    // Manejar errores de conexión
    si (err) {
    hecho();
    console.log (err);
    return res.status (500) .json ({éxito: falso, datos: err});
    }
    // Consulta SQL> Seleccionar datos
    const query = client.query (‘SELECCIONAR * DE los elementos ORDER BY id ASC;’);
    // Transmitir resultados una fila a la vez
    query.on (‘row’, (row) => {
    resultados.push (fila);
    });
    // Después de que se devuelvan todos los datos, cierre la conexión y devuelva los resultados
    query.on (‘end’, () => {
    hecho();
    return res.json (resultados);
    });
    });

    Deberíamos resumirlo en una función de utilidad para no duplicar el código. Haga esto por su cuenta y luego publique un enlace a su código en los comentarios para su revisión.

    Conclusión y próximos pasos.

    ¡Eso es! Dado que aquí hay varias piezas móviles, revise cómo encaja cada pieza en el proceso general y si cada una es parte del cliente o servidor. Comenta abajo con preguntas. Agarra el código del repositorio.


    Esta aplicación está lejos de estar terminada. ¿Qué más necesitamos hacer?

    1. Manejar permisos a través de passport.js
    2. Agregue un corredor de tareas, como Gulp
    3. Prueba con Mocha y Chai
    4. Verifique la cobertura de prueba con Estambul
    5. Añadir promesas
    6. Use Bower para administrar dependencias del lado del cliente
    7. Utilice enrutamiento angular, validación de formularios, servicios y plantillas
    8. Manejar actualizaciones (solicitudes PUT)
    9. Administre mejor la capa de la base de datos agregando un ORM, como Sequelize ( consulte mi publicación de seguimiento en Node, Postgres y Sequelize ), y un medio para administrar las migraciones

    Sequelize.

    El nodo.js ORM para PostgreSQL, MySQL, SQLite y MSSQL

    Impresionante ORM para Node.js. Lleva un poco de tiempo entender todo, pero su soporte es increíble. ¡También está basado en promesas!

    Use la biblioteca ‘pg’ de Node.js para conectar PostgreSQL a Node.js

    Tiene una sintaxis muy fácil y mínima.