Post Top Ad

Your Ad Spot

martes, 9 de julio de 2019

Uso de Sequelize.js y SQLite en una aplicación Express.js

En este tutorial, demostraré cómo construir una aplicación web de administración de contactos simple utilizando Node.js , Express.js , Vue.js junto con el mapeador relacional de objetos (ORM) sequelize.jsrespaldado por una base de datos SQLite .
Sin embargo, el objetivo principal de este artículo será cómo utilizar la biblioteca sequelize.js en conjunto con SQLite, que se expande en mi artículo previo Un SQLite Tutorial con Node.js . Puede encontrar el código completado para este tutorial en mi cuenta de GitHub .

Instalación e instalación

Para comenzar, inicializaré un nuevo proyecto usando Good Ole npm inity presionando Intro para aceptar todos los valores predeterminados, excepto para usar el punto de entrada de server.js en lugar de index.js. Como nota al margen, estoy usando la versión 8.10 de Node.js.
$ mkdir node-vue-sqlite-sequelize && cd node-vue-sqlite-sequelize
$ npm init
A continuación, instalaré las dependencias que necesitaré, que son Express, parser, sequelize, sequelize-cli, sqlite3 y opcionalmente nodemon para evitar que tenga que reiniciar Node.js todo el tiempo.
$ npm install --save express body-parser sequelize sequelize-cli sqlite3 nodemon

Creación de la interfaz de usuario de gestión de contactos

Primero hago un directorio estático en el mismo directorio que el archivo package.json:
$ mkdir static
Dentro del nuevo directorio estático, haré un archivo HTML llamado index.html. Para crear UI web ricas e interactivas (o incluso aplicaciones de escritorio con Electron ) confío en Vue.js por su elegancia y simplicidad, así que nuevamente en este tutorial usaré Vue.js en el archivo index.html.
Dentro de index.html comenzaré con una pieza simple de HTML5 boiler-plate y fuente Vue.js junto con Bulma y Font-Awesome para hacer las cosas un poco más bonitas, junto con axios.js, que usaré para las llamadas AJAX más adelante .
<!-- index.html -->  
<!DOCTYPE html>  
<html lang="en">  
<head>  
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Contact</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17-beta.0/vue.js"></script>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
  <style>
    .section-container {
      max-width: 800px;
      margin: auto;
    }
  </style>
</head>  
<body>

</body>  
</html>  
Bien, antes de poder profundizar en la construcción de la interfaz de usuario, necesito hablar sobre la funcionalidad y pensar en las diferentes formas en que necesitaré pasar los datos de un lado a otro con esta aplicación basada en datos.
Dado que la aplicación gestiona los contactos, obviamente necesitaré cierta representación de una persona, a la que me referiré como un objeto de contacto. En cuanto a los comportamientos probablemente necesitaré lo siguiente:
  • Ver lista de todos los contactos
  • Añadir contactos
  • Ver detalles de un contacto
  • Actualizar detalles de un contacto
  • Eliminar un contacto
Funcionalidad CRUD bastante simple, ¿verdad?
Además, como mencioné anteriormente, estaré interactuando con el backend a través de una API REST de AJAX, así que permítame también diseñar los puntos finales de la API que creo que necesitaré.
RutaMétodoFuncionalidad
/api/contactsOBTENERRecuperar todos los contactos
/api/contactsENVIARCrear contacto
/api/contacts/:idPONERActualizar los detalles de un contacto.
/api/contacts/:idBORRAREliminar un contacto
Ahora que he establecido cuáles son las funcionalidades requeridas, puedo comenzar a juntar los componentes de la interfaz de usuario e implementar los comportamientos deseados de JavaScript.
Para comenzar, crearé un componente llamado AddUpdateContactque puedo usar para agregar nuevos contactos a la aplicación o actualizar los existentes. Entonces, en la parte inferior de la etiqueta de HTML bodyagrego una scriptetiqueta y el siguiente código de JavaScript basado en Vue.js:
<script>  
  const AddUpdateContact = {
    props: ['contact', 'title'],
    data () {
      return {
        id: this.contact ? this.contact.id : null,
        firstName: this.contact ? this.contact.firstName : '',
        lastName: this.contact ? this.contact.lastName : '',
        phone: this.contact ? this.contact.phone : ''
      }
    },
    methods: {
      save() {
        this.$emit('save-contact', { id: this.id, firstName: this.firstName, lastName: this.lastName, phone: this.phone })
        if (!this.id) {
          this.firstName = ''
          this.lastName = ''
          this.phone = ''
        }
      }
    },
    template: `
      <form class="form" @submit.prevent="save">
        <h3 class='subtitle'>{{ title }}</h3>
        <div class="field">
            <label>First Name</label>
            <div class="control">
              <input class="input" type="text" v-model="firstName">
            </div> 
        </div>
        <div class="field">
            <label>Last Name</label>
            <div class="control">
              <input class="input" type="text" v-model="lastName">
            </div> 
        </div>
        <div class="field">
            <label>Phone</label>
            <div class="control">
              <input class="input" type="text" v-model="phone">
            </div> 
        </div>
        <div class="field">
            <div class="control">
              <button class="button is-success">Save</button>
            </div> 
        </div>
      </form>
    `
  }
</script>  
Se le AddUpdateContactpueden dar dos elementos al componente: (1) un título y, (2) en el caso opcional donde se usará para actualizar un contacto existente o un objeto de contacto. Los datamiembros se inicializan en función de si el contactobjeto se pasa o no como prop. La templatesección contiene un formulario y campos de entrada para agregar nueva información de contacto o modificar una existente. A continuación, la sección de métodos contiene un único savemétodo que emite la información del contacto hasta un componente de los padres, lo cual es consistente con los de un solo sentido filosofía de Vue.js enlace de datos .
A continuación, crearé un Contactcomponente dentro de la scriptetiqueta, que se verá así:
<script>  
  // omitting the AddUpdateContact component ...
  const Contact = {
    props: ['contact'],
    components: { 'add-update-contact': AddUpdateContact },
    data () {
      return {
        showDetail: false
      }
    },
    methods: {
      onAddOrUpdateContact(contact) {
        this.$emit('save-contact', contact)
      },
      deleteContact (contact) {
        this.$emit('delete-contact', contact)
      }
    },
    template: `
      <div class="card">
        <header class="card-header">
          <p @click="showDetail = !showDetail" class="card-header-title">
            {{ contact.firstName }} {{ contact.lastName }}
          </p>
          <a class="card-header-icon" @click.stop="deleteContact(contact)">
            <span class="icon">
              <i class="fa fa-trash"></i>
            </span>
          </a>
        </header>
        <div v-show="showDetail" class="card-content">
            <add-update-contact title="Details" :contact="contact" @save-contact="onAddOrUpdateContact" />
        </div>
      </div>
    `
  }
</script>  
El Contactcomponente recibirá un contactobjeto como prop que contiene los datos que se mostrarán en su plantilla. El Contactcomponente también utilizará el AddUpdateContactcomponente descrito anteriormente para mostrar los detalles del contacto, así como también le dará al usuario la posibilidad de actualizarlo.
La funcionalidad de actualización es posible mediante el uso del método del controlador de eventos onAddOrUpdateContactque está escuchando el save-contactevento del AddUpdateContactcomponente, que luego propaga el evento por la cadena a la instancia principal de Vue.js, que se tratará en breve. Además, también hay un deleteContactmétodo del componente de contacto que también propaga el mensaje de eliminación hasta la instancia Vue.js principal.
Bien, para finalizar el código de JavaScript de frontend, necesito crear una instancia del objeto Vue.js raíz y decirle que se enlace a un divelemento con id"app" como:
<script>  
  // omitting AddUpdateContant and Contact components ...
  new Vue({
    el: '#app',
    components: { contact: Contact, 'add-update-contact': AddUpdateContact },
    data: {
      contacts: [],
      apiURL: 'http://localhost:3000/api/contacts'
    },
    methods: {
      onAddOrUpdateContact (contact) {
        if (contact.id) {
          this.updateContact(contact)
        } else {
          this.addContact(contact)
        }
      },
      addContact (contact) {
        return axios.post(this.apiURL, contact)
          .then((response) => {
            const copy = this.contacts.slice()
            copy.push(response.data)
            this.contacts = copy
          })
      },
      updateContact (contact) {
        return axios.put(`${this.apiURL}/${contact.id}`, contact)
          .then((response) => {
            const copy = this.contacts.slice()
            const idx = copy.findIndex((c) => c.id === response.data.id)
            copy[idx] = response.data
            this.contacts = copy
          })
      },
      deleteContact (contact) {
        console.log('deleting', contact)
        return axios.delete(`${this.apiURL}/${contact.id}`)
          .then((response) => {
            let copy = this.contacts.slice()
            const idx = copy.findIndex((c) => c.id === response.data.id)
            copy.splice(idx, 1)
            this.contacts = copy
          })
      }
    },
    beforeMount () {
      axios.get(this.apiURL)
        .then((response) => {
          this.contacts = response.data
        })
    }
  })
</script>  
El objeto raíz Vue.js registra los componentes descritos anteriormente y define un conjunto de métodos que interactuarán con la API REST a través de llamadas AJAX realizadas al servidor de aplicaciones Node.js / Express.js que se construirá próximamente.
La última parte de la interfaz de usuario a cuidar es el HTML necesario para representar los componentes de Vue.js, que se muestra a continuación en la totalidad del archivo index.html.
<!-- index.html -->  
<!DOCTYPE html>  
<html lang="en">  
<head>  
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Contacts</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17-beta.0/vue.js"></script>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
  <style>
    .section-container {
      max-width: 800px;
      margin-right: auto;
      margin-left: auto;
    }
  </style>
</head>  
<body>  
  <div id="app" class="container">
    <section class="section section-container" style="padding-top: 24px; padding-bottom: 5px;">
        <h2 class="title">Contacts</h2>
        <contact v-for="contact in contacts"
            :key="contact.name"
            :contact="contact"
            @save-contact="onAddOrUpdateContact" 
            @delete-contact="deleteContact" />
    </section>
    <section class="section section-container" style="padding-bottom: 10px;">
      <div class="box">
        <add-update-contact title="Add Contact" @save-contact="onAddOrUpdateContact" />
      </div>
    </section>
  </div>
  <script>
  const AddUpdateContact = {
    props: ['contact', 'title'],
    data () {
      return {
        id: this.contact ? this.contact.id : null,
        firstName: this.contact ? this.contact.firstName : '',
        lastName: this.contact ? this.contact.lastName : '',
        phone: this.contact ? this.contact.phone : ''
      }
    },
    methods: {
      save() {
        this.$emit('save-contact', { id: this.id, firstName: this.firstName, lastName: this.lastName, phone: this.phone })
        if (!this.id) {
          this.firstName = ''
          this.lastName = ''
          this.phone = ''
        }
      }
    },
    template: `
      <form class="form" @submit.prevent="save">
        <h3 class='subtitle'>{{ title }}</h3>
        <div class="field">
            <label>First Name</label>
            <div class="control">
              <input class="input" type="text" v-model="firstName">
            </div> 
        </div>
        <div class="field">
            <label>Last Name</label>
            <div class="control">
              <input class="input" type="text" v-model="lastName">
            </div> 
        </div>
        <div class="field">
            <label>Phone</label>
            <div class="control">
              <input class="input" type="text" v-model="phone">
            </div> 
        </div>
        <div class="field">
            <div class="control">
              <button class="button is-success">Save</button>
            </div> 
        </div>
      </form>
    `
  }

  const Contact = {
    props: ['contact'],
    components: { 'add-update-contact': AddUpdateContact },
    data () {
      return {
        showDetail: false
      }
    },
    methods: {
      onAddOrUpdateContact(contact) {
        this.$emit('save-contact', contact)
      },
      deleteContact (contact) {
        this.$emit('delete-contact', contact)
      }
    },
    template: `
      <div class="card">
        <header class="card-header">
          <p @click="showDetail = !showDetail" class="card-header-title">
            {{ contact.firstName }} {{ contact.lastName }}
          </p>
          <a class="card-header-icon" @click.stop="deleteContact(contact)">
            <span class="icon">
              <i class="fa fa-trash"></i>
            </span>
          </a>
        </header>
        <div v-show="showDetail" class="card-content">
            <add-update-contact title="Details" :contact="contact" @save-contact="onAddOrUpdateContact" />
        </div>
      </div>
    `
  }

  new Vue({
    el: '#app',
    components: { contact: Contact, 'add-update-contact': AddUpdateContact },
    data: {
      contacts: [],
      apiURL: 'http://localhost:3000/api/contacts'
    },
    methods: {
      onAddOrUpdateContact (contact) {
        if (contact.id) {
          this.updateContact(contact)
        } else {
          this.addContact(contact)
        }
      },
      addContact (contact) {
        return axios.post(this.apiURL, contact)
          .then((response) => {
            const copy = this.contacts.slice()
            copy.push(response.data)
            this.contacts = copy
          })
      },
      updateContact (contact) {
        return axios.put(`${this.apiURL}/${contact.id}`, contact)
          .then((response) => {
            const copy = this.contacts.slice()
            const idx = copy.findIndex((c) => c.id === response.data.id)
            copy[idx] = response.data
            this.contacts = copy
          })
      },
      deleteContact (contact) {
        console.log('deleting', contact)
        return axios.delete(`${this.apiURL}/${contact.id}`)
          .then((response) => {
            let copy = this.contacts.slice()
            const idx = copy.findIndex((c) => c.id === response.data.id)
            copy.splice(idx, 1)
            this.contacts = copy
          })
      }
    },
    beforeMount () {
      axios.get(this.apiURL)
        .then((response) => {
          this.contacts = response.data
        })
    }
  })

  </script>
</body>  
</html>  

Andamios la API REST

Dado que estoy utilizando Express.js para esta aplicación, tendré que crear un archivo JavaScript llamado server.js que sirva la aplicación en el mismo directorio que el archivo package.json y luego abrirlo en mi editor de texto de desarrollo favorito (VS Código).
En la parte superior del archivo, extraigo el marco Express.js requirey luego instalo una instancia de la aplicación. A continuación le digo a la aplicación que use el middleware estático de Express para ofrecer contenido estático (HTML, JS, CSS, etc.) desde el directorio "estático". En la parte inferior de la secuencia de comandos server.js, le digo al servidor de aplicaciones que vincule (escuche) el puerto 3000 y las solicitudes del servidor.
Con el fin de descifrar los puntos finales de la API REST previamente definidos en la tabla de la sección anterior, necesito un analizador del cuerpo e indico a la aplicación Express que se utilizará para analizar el contenido del cuerpo HTTP. Luego apago los puntos finales de la API que sirven el contenido solicitado.
// server.js

const express = require('express');  
const bodyParser = require('body-parser');

const app = express();

app.use(bodyParser.json());  
app.use(express.static(__dirname + '/static'));

app.get('/api/contacts', (req, res) => {  
  // TODO: retreive contacts and send to requester
});

app.post('/api/contacts', (req, res) => {  
  const { firstName, lastName, phone } = req.body
  // TODO: create contact
});

app.delete('/api/contacts/:id', (req, res) => {  
  const id = parseInt(req.params.id)
  // TODO: find and delete contact by id
});

app.put('/api/contacts/:id', (req, res) => {  
  const id = parseInt(req.params.id)
  const { firstName, lastName, phone } = req.body
  // TODO: find and update contact by id
});

app.listen(3000, () => {  
  console.log('Server is up on port 3000');
});
Una vez que se definan los modelos de datos y la posterior estructura de la base de datos SQLite, volveré y proporcionaré la funcionalidad para las devoluciones de llamada de la API.

The Sequelize ORM y Sequelize CLI

Sequelize.js es un ORM popular para Node.js versión 4 y superior que se puede usar para muchos sistemas de administración de bases de datos (DBMS) diferentes, como MySQL, Postgres, SQLite y otros. Existe una biblioteca de utilidades complementaria llamada sequelize-cli que ayuda a automatizar algunas de las partes mundanas y algo no triviales de la programación de bases de datos.
En este tutorial usaré sequelize-cli para ocuparme de generar el lenguaje de definición de datos (DDL) para crear tablas de base de datos, así como generar modelos de datos que creen y ejecuten lenguaje de manipulación de datos (DML) para consultar la base de datos, e incluso un sistema de migración para ayudar con la versión que controla el esquema de la base de datos.
Para comenzar, usaré sequelize-cli para inicializar la capa de acceso a datos del proyecto de la siguiente manera:
$ node_modules/.bin/sequelize init
Este comando creará las configuraciones de directorios, migraciones, modelos y sembradoras que resultarán en la estructura de mi proyecto con el siguiente aspecto:
.
├── config
│   └── config.json
├── migrations
├── models
│   └── index.js
├── package-lock.json
├── package.json
├── seeders
├── server.js
└── static
    └── index.html
El propósito de los directorios es el siguiente:
  • config / index.js - esto define los parámetros de conexión y dialet sql
  • migraciones: contiene scripts de migración para administrar el control de versiones del esquema
  • modelos: contiene los modelos de datos que utiliza para interactuar con la base de datos dentro del código de su aplicación
  • sembradoras: contiene scripts para completar su base de datos con datos iniciales
En primer lugar, necesito editar el archivo config / config.json para que la secuencia de tareas sepa que voy a trabajar con una base de datos SQLite. Así que voy a cambiar el archivo de este ...
{
  "development": {
    "username": "root",
    "password": null,
    "database": "database_development",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "test": {
    "username": "root",
    "password": null,
    "database": "database_test",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "production": {
    "username": "root",
    "password": null,
    "database": "database_production",
    "host": "127.0.0.1",
    "dialect": "mysql"
  }
}
a esto ...
{
  "development": {
    "dialect": "sqlite",
    "storage": "./database.sqlite3"
  },
  "test": {
    "dialect": "sqlite",
    "storage": ":memory"
  },
  "production": {
    "dialect": "sqlite",
    "storage": "./database.sqlite3"
  }
}
que creará y usará un archivo de base de datos SQLite llamado database.sqlite3 en la raíz del proyecto.
Ahora seguiré ese comando con otro pero, esta vez, usaré el model:generateargumento para definir mi modelo de contacto y sus atributos, de la siguiente manera:
$ node_modules/.bin/sequelize model:generate --name Contact --attributes firstName:string,lastName:string,phone:string,email:string
El --nameparámetro es obviamente el nombre del modelo que se generará y el --attibutesparámetro va seguido de los campos de objeto que lo definen junto con sus tipos de datos. Las salidas de este comando son dos nuevos archivos:
  1. models / contact.js: un modelo de datos que se utilizará en el código lógico de la aplicación Node.js
  2. migrations / aaaammddHHMMSS-create-contact.js: un script de migración que emitirá DDL SQL para crear la tabla de contactos en la base de datos
Además de los atributos especificados en el model:generatecomando sequelize-cli, también se generará un idcampo de entero auto incrementado , así como campos de enlace de fecha createdAtcomo updatedAt.
Lo siguiente es ejecutar la migración para que la base de datos SQLite contenga la tabla de contactos de la siguiente manera:
$ node_modules/.bin/sequelize db:migrate
Este comando indicará que la migración se ha ejecutado correctamente. Ahora puedo abrir mi archivo database.sqlite3 recién generado y ver el esquema de esta manera:
$ sqlite3 database.sqlite3
SQLite version 3.20.1 2017-08-24 16:21:36  
Enter ".help" for usage hints.  
sqlite> .schema  
CREATE TABLE `SequelizeMeta` (`name` VARCHAR(255) NOT NULL UNIQUE PRIMARY KEY);  
CREATE TABLE `Contacts` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `firstName` VARCHAR(255), `lastName` VARCHAR(255), `phone` VARCHAR(255), `email` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL);  
CREATE TABLE sqlite_sequence(name,seq);  
Tenga en cuenta que también hay otra tabla allí llamada SequelizeMeta. Esta es la tabla que utiliza Sequelize.js para mantener el orden en que se ejecutan las migraciones. Por ejemplo, al ejecutar este comando se mostrará el nombre del script de migración que acabo de ejecutar.
sqlite> .headers ON  
sqlite> select * from SequelizeMeta;  
name  
20180726180039-create-contact.js  
Ahora que tengo el modelo de contacto asignado a una tabla de base de datos, me gustaría generar una secuencia de comandos de sembradora para rellenar previamente mi base de datos con algunos datos para demostrar cómo interactuar con el ORM en mi código de aplicación. La generación de un script de sembradora es muy similar a los comandos anteriores.
$ node_modules/.bin/sequelize seed:generate --name seed-contact
La salida es una nueva secuencia de comandos en el directorio de sembradoras de la convención de nombres aaaammddHHMMSS-seed-contact.js. Inicialmente es solo un andamio de una interfaz de sembradora como esta:
'use strict';

module.exports = {  
  up: (queryInterface, Sequelize) => {
    /*
      Add altering commands here.
      Return a promise to correctly handle asynchronicity.

      Example:
      return queryInterface.bulkInsert('Person', [{
        name: 'John Doe',
        isBetaMember: false
      }], {});
    */
  },

  down: (queryInterface, Sequelize) => {
    /*
      Add reverting commands here.
      Return a promise to correctly handle asynchronicity.

      Example:
      return queryInterface.bulkDelete('Person', null, {});
    */
  }
};
Lo editaré de la siguiente manera para agregar algunos contactos.
'use strict';

module.exports = {  
  up: (queryInterface, Sequelize) => {
    /*
      Add altering commands here.
      Return a promise to correctly handle asynchronicity.

      Example:
      return queryInterface.bulkInsert('Person', [{
        name: 'John Doe',
        isBetaMember: false
      }], {});
    */
   return queryInterface.bulkInsert('Contacts', [{
      firstName: 'Snoop',
      lastName: 'Dog',
      phone: '111-222-3333',
      email: 'snoopydog@dogpound.com',
      createdAt: new Date().toDateString(),
      updatedAt: new Date().toDateString()
    }, {
      firstName: 'Scooby',
      lastName: 'Doo',
      phone: '444-555-6666',
      email: 'scooby.doo@misterymachine.com',
      createdAt: new Date().toDateString(),
      updatedAt: new Date().toDateString()
    }, {
      firstName: 'Herbie',
      lastName: 'Husker',
      phone: '402-437-0001',
      email: 'herbie.husker@unl.edu',
      createdAt: new Date().toDateString(),
      updatedAt: new Date().toDateString()
    }], {});
  },

  down: (queryInterface, Sequelize) => {
    /*
      Add reverting commands here.
      Return a promise to correctly handle asynchronicity.

      Example:
      return queryInterface.bulkDelete('Person', null, {});
    */
   return queryInterface.bulkDelete('Contacts', null, {});
  }
};
Por último, necesito ejecutar la sembradora para llenar la base de datos con estos contactos iniciales.
$ node_modules/.bin/sequelize db:seed:all
Lo que me da un resultado en la consola que me permite saber que la tabla de la base de datos se sembró correctamente con datos.
Ok, la configuración finalmente se realiza. Ahora puedo pasar a la parte más divertida de interactuar realmente con mi modelo de contacto respaldado por la base de datos y darles a los puntos finales API aplastados la funcionalidad requerida.
Sin embargo, antes de que pueda usar mi modelo de contacto, debo informarle a la aplicación Express.js que existe. Hago esto agregando un requirein server.js y asignándolo a una constvariable llamada dbasí:
// server.js
const express = require('express');  
const bodyParser = require('body-parser');  
const db = require('./models'); // new require for db object  
Esta dbvariable contendrá mi modelo de contacto, que es accesible a través de db.Contact.
Comienzo con el más simple de los puntos finales de la API, el GET /api/contactspunto final. Este punto final simplemente necesita que todos los contactos sean devueltos desde la base de datos y serializados en una respuesta al solicitante que realiza la llamada. Puedo llamar al findAllmétodo del Contactobjeto y, una vez que se haya devuelto la promesa, puedo enviar los contactos al cliente utilizando el familiar res.send(...)proporcionado por el marco Express.js.
// server.js
// ommitting everything above ...

app.get('/api/contacts', (req, res) => {  
  return db.Contact.findAll()
    .then((contacts) => res.send(contacts))
    .catch((err) => {
      console.log('There was an error querying contacts', JSON.stringify(err))
      return res.send(err)
    });
});

// omitting everything below...
Ahora puedo iniciar mi servidor Express, apuntar mi navegador a localhost: 3000 / index.html, y seré recibido con la interfaz de usuario de mis contactos.
$ nodemon
... o si no está usando nodemon:
$ npm start
Captura de pantalla de la aplicación de contacto
A partir de ahora, implementaré la funcionalidad crear para el POST /api/contactspunto final. Crear nuevas instancias de contacto es super simple. Simplemente llame al create(...)método del db.Contactobjeto, espere a que la promesa se resuelva encadenando a then(...)y luego devolveré el contacto recién creado al cliente, de esta manera:
// server.js
// ommitting everything above ...

app.post('/api/contacts', (req, res) => {  
  const { firstName, lastName, phone } = req.body
  return db.Contact.create({ firstName, lastName, phone })
    .then((contact) => res.send(contact))
    .catch((err) => {
      console.log('***There was an error creating a contact', JSON.stringify(contact))
      return res.status(400).send(err)
    })
});

// omitting everything below ...
Ahora, si ingreso otro contacto, por ejemplo, Mujer Maravilla, pero deje el número de teléfono en blanco (dijo que me llamaría en lugar de dar su número), ingrese en el formulario "Agregar contacto" de la interfaz de usuario y presione "Guardar". ahora tiene cuatro miembros: Snoop Dog, Scooby Doo, Herbie Husker y Wonder Woman.
A partir de ahora, puedo agregar la funcionalidad para implementar el comportamiento de actualización para el PUT /api/contacts/:idpunto final. Esta es una interacción de dos partes desde la perspectiva de la programación de la base de datos.
Primero empiezo por encontrar el contacto que coincida con el ID proporcionado en la URL de la API utilizando el db.Contact.findById(...)método, luego modifico los campos con datos nuevos y finalizo utilizando el update(...)método del contactobjeto que envía el modelo actualizado a la base de datos donde se realizarán esos cambios. ser persistido
// server.js
// ommitting everything above ...

app.put('/api/contacts/:id', (req, res) => {  
  const id = parseInt(req.params.id)
  return db.Contact.findById(id)
  .then((contact) => {
    const { firstName, lastName, phone } = req.body
    return contact.update({ firstName, lastName, phone })
      .then(() => res.send(contact))
      .catch((err) => {
        console.log('***Error updating contact', JSON.stringify(err))
        res.status(400).send(err)
      })
  })
});

// omitting everything below...
Con esto en su lugar, ahora puedo actualizar el número de teléfono de Wonder Woman porque mi buen amigo Snoop Dog me lo dio amablemente. Para hacer esto, hago clic en el nombre de Wonder Woman en la lista de contactos de la interfaz de usuario para expandir el formulario de detalles / actualización. Después de completar su número 111-888-1213 y hacer clic en Guardar mi contacto, la mujer maravilla se actualizará.
Lo último que debe hacer es agregar la capacidad de eliminar un contacto a través del DELETE /api/contacts/:idpunto final. La eliminación es muy similar a la funcionalidad de actualización, ya que la instancia del modelo que desea eliminar primero debe recuperarse de la base de datos, y luego simplemente llama al destroy()método de la instancia del modelo.
// server.js
// ommitting everything above ...

app.delete('/api/contacts/:id', (req, res) => {  
  const id = parseInt(req.params.id)
  return db.Contact.findById(id)
    .then((contact) => contact.destroy())
    .then(() => res.send({ id }))
    .catch((err) => {
      console.log('***Error deleting contact', JSON.stringify(err))
      res.status(400).send(err)
    })
});

// omitting everything below ...
Resulta que es bueno que los contactos de mi lista de contactos se puedan eliminar porque Wonder Woman resultó ser una verdadera reina del drama y estaba peleando con mi esposa, no con la Wonder Woman. Estás a punto de ser eliminado.
Desde la interfaz de usuario puedo eliminar Wonder Woman haciendo clic en la papelera a la derecha de su nombre.
La totalidad de la secuencia de comandos server.js se muestra a continuación para completarla.
// server.js

// server.js

const express = require('express');  
const bodyParser = require('body-parser');  
const db = require('./models');

const app = express();

app.use(bodyParser.json());  
app.use(express.static(__dirname + '/static'));

app.get('/api/contacts', (req, res) => {  
  return db.Contact.findAll()
    .then((contacts) => res.send(contacts))
    .catch((err) => {
      console.log('There was an error querying contacts', JSON.stringify(err))
      return res.send(err)
    });
});

app.post('/api/contacts', (req, res) => {  
  const { firstName, lastName, phone } = req.body
  return db.Contact.create({ firstName, lastName, phone })
    .then((contact) => res.send(contact))
    .catch((err) => {
      console.log('***There was an error creating a contact', JSON.stringify(contact))
      return res.status(400).send(err)
    })
});

app.delete('/api/contacts/:id', (req, res) => {  
  const id = parseInt(req.params.id)
  return db.Contact.findById(id)
    .then((contact) => contact.destroy({ force: true }))
    .then(() => res.send({ id }))
    .catch((err) => {
      console.log('***Error deleting contact', JSON.stringify(err))
      res.status(400).send(err)
    })
});

app.put('/api/contacts/:id', (req, res) => {  
  const id = parseInt(req.params.id)
  return db.Contact.findById(id)
  .then((contact) => {
    const { firstName, lastName, phone } = req.body
    return contact.update({ firstName, lastName, phone })
      .then(() => res.send(contact))
      .catch((err) => {
        console.log('***Error updating contact', JSON.stringify(err))
        res.status(400).send(err)
      })
  })
});

app.listen(3000, () => {  
  console.log('Server is up on port 3000');
});

Conclusión

En este tutorial, he demostrado cómo utilizar el ORM de Sequelize.js junto con su CLI de Sequelize para manejar la programación de la base de datos junto con SQLite. Al hacerlo, proporcioné una aplicación de lista de contactos que utiliza Node.js / Express.js para un servidor de aplicaciones junto con una interfaz de usuario basada en Vue.js. Sequelize.js es un ORM bastante útil que elimina muchos de los detalles necesarios para la programación de bases de datos en aplicaciones basadas en Node.js y es bastante popular entre los desarrolladores de Node.js.
Como siempre, les agradezco la lectura y agradezco los comentarios y críticas a continuación.

No hay comentarios.:

Publicar un comentario

Dejanos tu comentario para seguir mejorando!

outbrain

Páginas