Cómo Compilar E Implementar Una Aplicación De Back-end

En este tutorial, aprenderemos cómo crear e implementar un backend de aplicación de administración de imágenes.

Podrá almacenar un registro de una imagen en la base de datos, recuperar el registro de la imagen de la base de datos, actualizar el registro e incluso eliminar el registro por completo, según sea el caso.

Para lograr todo esto, usaremos Express (un marco Node.js), Postgres (una base de datos), Cloudinary (un almacenamiento de imágenes basado en la nube), GitHub (para el control/almacenamiento de versiones) y Heroku (una plataforma de alojamiento).

Todas estas herramientas son gratuitas. Así que no tienes que preocuparte por cómo pagarlos. Gracias a estos grandes innovadores.

requisitos previos

Si es nuevo en la mayoría de estas tecnologías, le aconsejo que siga mi otro tutorial sobre cómo crear un servidor y cargar imágenes en Cloudinary .

Si está totalmente interesado en Postgres, consulte este tutorial .

Cuando estés listo, ¡manos a la obra!

Cómo almacenar y recuperar un registro de imagen

Crear base de datos y tabla

Por lo tanto, querrá comenzar clonando este proyecto si aún no lo tiene.

En tu pgAdmin :

  • Crea una base de datos y nómbralatutorial
  • Crea una tabla y nómbralatutorial
  • Cree un rol de inicio de sesión/grupo y asígnele el nombre tutorial. (No olvides darle todos los privilegios.)

De vuelta en el directorio de su proyecto, instale los paquetes node-postgres ( npm i pg) y make-runnable ( npm i make-runnable).

En su package.jsonarchivo, reemplace el contenido de "scripts"con "create": "node ./services/dbConnect createTables". Usaremos esto para ejecutar el dbConnectarchivo que estamos a punto de crear.

Cree un services/dbConnectarchivo que contenga el siguiente código:


const pg = require("pg");

const config = {
  user: "tutorial",
  database: "tutorial",
  password: "tutorial",
  port: 5432,
  max: 10, // max number of clients in the pool
  idleTimeoutMillis: 30000,
};

const pool = new pg.Pool(config);

pool.on("connect", () => {
  console.log("connected to the Database");
});

const createTables = () => {
  const imageTable = `CREATE TABLE IF NOT EXISTS
    images(
      id SERIAL PRIMARY KEY,
      title VARCHAR(128) NOT NULL,
      cloudinary_id VARCHAR(128) NOT NULL,
      image_url VARCHAR(128) NOT NULL
    )`;
  pool
    .query(imageTable)
    .then((res) => {
      console.log(res);
      pool.end();
    })
    .catch((err) => {
      console.log(err);
      pool.end();
    });
};

pool.on("remove", () => {
  console.log("client removed");
  process.exit(0);
});

//export pool and createTables to be accessible from anywhere within the application
module.exports = {
  createTables,
  pool,
};

require("make-runnable");

Ahora estamos listos para crear la tabla en nuestra base de datos. Si estás listo, ¡vamos al rock and roll!

Ejecuta el siguiente código en tu terminal:


  npm run create

Si la imagen a continuación es su resultado, entonces está listo para comenzar:

Texto alternativo

Verifique su pgAdmin , y debería tener su mesa asentada correctamente en su base de datos como en la imagen a continuación:

Texto alternativo

Muy bien, ha sido un largo camino. Es hora de unir Node, Postgres y Cloudinary.

Cómo crear puntos finales para almacenar y recuperar registros de imágenes

Extremo 1: imagen persistente

Primero, requiere el dbConnect.jsarchivo en la parte superior del app.jsarchivo así:

  const db = require('services/dbConnect.js');

Luego, en el app.jsarchivo, cree un nuevo punto final (imagen persistente) con el siguiente código:


// persist image
app.post("/persist-image", (request, response) => {
  // collected image from a user
  const data = {
    title: request.body.title,
    image: request.body.image,
  }

  // upload image here
  cloudinary.uploader.upload(data.image)
  .then().catch((error) => {
    response.status(500).send({
      message: "failure",
      error,
    });
  });
})

Reemplace el thenbloque con el siguiente código:


.then((image) => {
    db.pool.connect((err, client) => {
      // inset query to run if the upload to cloudinary is successful
      const insertQuery = 'INSERT INTO images (title, cloudinary_id, image_url) 
         VALUES($1,$2,$3) RETURNING *';
      const values = [data.title, image.public_id, image.secure_url];
    })
  })

Los image.public_idy image.secure_urlse obtienen como parte de los detalles devueltos para una imagen después de que la imagen se haya cargado correctamente en Cloudinary.

Ahora mantenemos un registro de image.public_idy image.secure_url(como puede ver en el código anterior) para usarlo para recuperar, actualizar o eliminar el registro de la imagen cuando lo creamos conveniente.

Muy bien, ¡sigamos adelante!

Aún en el thenbloque, agregue el siguiente código debajo de la consulta que creamos:


// execute query
client.query(insertQuery, values)
      .then((result) => {
        result = result.rows[0];

        // send success response
        response.status(201).send({
          status: "success",
          data: {
            message: "Image Uploaded Successfully",
            title: result.title,
            cloudinary_id: result.cloudinary_id,
            image_url: result.image_url,
          },
        })
      }).catch((e) => {
        response.status(500).send({
          message: "failure",
          e,
        });
      })

Así que nuestro persist-imagepunto final ahora se ve así:


// persist image
app.post("/persist-image", (request, response) => {
  // collected image from a user
  const data = {
    title: request.body.title,
    image: request.body.image
  }

  // upload image here
  cloudinary.uploader.upload(data.image)
  .then((image) => {
    db.pool.connect((err, client) => {
      // inset query to run if the upload to cloudinary is successful
      const insertQuery = 'INSERT INTO images (title, cloudinary_id, image_url) 
         VALUES($1,$2,$3) RETURNING *';
      const values = [data.title, image.public_id, image.secure_url];

      // execute query
      client.query(insertQuery, values)
      .then((result) => {
        result = result.rows[0];

        // send success response
        response.status(201).send({
          status: "success",
          data: {
            message: "Image Uploaded Successfully",
            title: result.title,
            cloudinary_id: result.cloudinary_id,
            image_url: result.image_url,
          },
        })
      }).catch((e) => {
        response.status(500).send({
          message: "failure",
          e,
        });
      })
    })  
  }).catch((error) => {
    response.status(500).send({
      message: "failure",
      error,
    });
  });
});

Ahora vamos a probar todo nuestro arduo trabajo:

Abra su cartero y pruebe su punto final como se muestra en la imagen a continuación. El mío fue un éxito. ¿Espero que el tuyo tampoco haya tenido errores?

Texto alternativo

Abra su consola/panel de Cloudinary y verifique su media Library. Su nueva imagen debería estar sentada allí cómodamente como la mía a continuación:

Texto alternativo

Y ahora a la razón principal por la que estamos aquí: verifique la imagestabla en su pgAdmin . La mía es la que ves a continuación:

Texto alternativo

¡Oh, la la! Llegamos hasta aquí. Por favor, tómese un descanso si lo necesita. Estaré aquí esperándote cuando regreses. :)

Si está listo, recuperemos la imagen que conservamos hace un momento.

Punto final 2: Recuperar imagen

Comience con este código:


app.get("/retrieve-image/:cloudinary_id", (request, response) => {

});

A continuación, necesitaremos recopilar una identificación única del usuario para recuperar una imagen en particular. Así que agregue const { id } = request.params;al código anterior así:


app.get("/retrieve-image/:cloudinary_id", (request, response) => {
  // data from user
  const { cloudinary_id } = request.params;

});

Agregue el siguiente código justo debajo del código anterior:


db.pool.connect((err, client) => {
      // query to find image
    const query = "SELECT * FROM images WHERE cloudinary_id = $1";
    const value = [cloudinary_id];
    });

Debajo de la consulta, ejecute la consulta con el siguiente código:


// execute query
    client
      .query(query, value)
      .then((output) => {
        response.status(200).send({
          status: "success",
          data: {
            id: output.rows[0].cloudinary_id,
            title: output.rows[0].title,
            url: output.rows[0].image_url,
          },
        });
      })
      .catch((error) => {
        response.status(401).send({
          status: "failure",
          data: {
            message: "could not retrieve record!",
            error,
          },
        });
      });

Ahora nuestra retrieve-imageAPI se ve así:


app.get("/retrieve-image/:cloudinary_id", (request, response) => {
  // data from user
  const { cloudinary_id } = request.params;
  
  db.pool.connect((err, client) => {
    // query to find image
    const query = "SELECT * FROM images WHERE cloudinary_id = $1";
    const value = [cloudinary_id];

    // execute query
    client
      .query(query, value)
      .then((output) => {
        response.status(200).send({
          status: "success",
          data: {
            id: output.rows[0].cloudinary_id,
            title: output.rows[0].title,
            url: output.rows[0].image_url,
          },
        });
      })
      .catch((error) => {
        response.status(401).send({
          status: "failure",
          data: {
            message: "could not retrieve record!",
            error,
          },
        });
      });
  });
});

A ver qué tan bien lo hicimos:

En su cartero, copie cloudinary_idy agréguelo a la URL como en la imagen a continuación:

Texto alternativo

¡SÍ SÍ! También podemos recuperar nuestra imagen.

Si estás aquí, te mereces un aplauso y una ovación de pie por tu laboriosidad.

¡Felicidades! Acabas de alcanzar un gran hito.

El código para almacenar y recuperar registros de imágenes está aquí .

Cómo actualizar y eliminar un registro de imagen

Ahora veremos cómo eliminar y actualizar un registro de imagen según sea el caso. Comencemos con el punto final de eliminación.

Eliminar punto final

En el archivo app.js, comience con el siguiente código:


// delete image
app.delete("delete-image/:cloudinary_id", (request, response) => {

});

A continuación, queremos obtener el ID único de la imagen que queremos eliminar de la URL, es decir, cloudinary_id. Entonces, dentro del código anterior, agregue:


const { cloudinary_id } = request.params;

Ahora comenzamos el proceso de eliminación.

Primero, eliminamos de Cloudinary. Agregue el siguiente código para eliminar la imagen de Cloudinary:


cloudinary.uploader
    .destroy(cloudinary_id)
    .then((result) => {
      response.status(200).send({
        message: "success",
        result,
      });
    })
    .catch((error) => {
      response.status(500).send({
        message: "Failure",
        error,
      });
    });

En este punto, nuestra API solo puede eliminar la imagen de Cloudinary (puede consultarla en cartero). Pero también queremos deshacernos del registro que tenemos en nuestra base de datos de Postgres.

En segundo lugar, eliminamos de nuestra base de datos de Postgres. Para hacerlo, reemplace el código en el thenbloque con lo siguiente query:


db.pool.connect((err, client) => {
     
      // delete query
      const deleteQuery = "DELETE FROM images WHERE cloudinary_id = $1";
      const deleteValue = [cloudinary_id];

})

Ejecute la consulta con el siguiente código debajo:


// execute delete query
      client.query(deleteQuery, deleteValue)
      .then((deleteResult) => {
        response.status(200).send({
          message: "Image Deleted Successfully!",
          deleteResult
        });
      }).catch((e) => {
        response.status(500).send({
          message: "Image Couldn't be Deleted!",
          e
        });
      });

Así que nuestro Endpoint debería verse así:


// delete image
app.delete("/delete-image/:cloudinary_id", (request, response) => {
  // unique ID
  const { cloudinary_id } = request.params;

  // delete image from cloudinary first
  cloudinary.uploader
    .destroy(cloudinary_id)

    // delete image record from postgres also
    .then(() => {
      db.pool.connect((err, client) => {
     
      // delete query
      const deleteQuery = "DELETE FROM images WHERE cloudinary_id = $1";
      const deleteValue = [cloudinary_id];

      // execute delete query
      client
        .query(deleteQuery, deleteValue)
        .then((deleteResult) => {
          response.status(200).send({
            message: "Image Deleted Successfully!",
            deleteResult,
          });
        })
        .catch((e) => {
          response.status(500).send({
            message: "Image Couldn't be Deleted!",
            e,
          });
        });
      })
    })
    .catch((error) => {
      response.status(500).send({
        message: "Failure",
        error,
      });
    });
});

Ha llegado el momento de que pongamos a prueba nuestro Endpoint.

El siguiente es mi Cloudinary media librarycon dos imágenes que ya subí. Tome nota de su identificación única ( public_id).

Texto alternativo

Si aún no lo tiene, utilice el extremo de imagen persistente para cargar algunas imágenes.

Ahora procedamos al cartero:

Texto alternativo

Observe, la identificación única, ya que coincide con una de las imágenes en mi biblioteca de medios de Cloudinary.

Desde la salida, ejecutamos el comando DELETE y eso eliminó una FILA de nuestra TABLA de imágenes en nuestra base de datos.

Ahora esta es mi biblioteca de medios con una de las imágenes restantes:

Texto alternativo

Walahhhh... Ahora podemos deshacernos de una imagen.

Tómese un descanso si lo necesita. ✌🏾

Si está listo, estoy listo para actualizar las imágenes.

Actualizar API de imagen

Debajo de la delete-imageAPI, comencemos a crear la update-imageAPI con el siguiente código:


// update image
app.put("/update-image/:cloudinary_id", (request, response) => {

});

All codes will live in there.

Recopile la identificación única de Cloudinary y los detalles de la nueva imagen del usuario con el siguiente código:


// unique ID
  const { cloudinary_id } = request.params;

// collected image from a user
  const data = {
    title: request.body.title,
    image: request.body.image,
  };

Elimina la imagen de Cloudinary con el siguiente código:


// delete image from cloudinary first
  cloudinary.uploader
    .destroy(cloudinary_id)
      // upload image here
    .then()
    .catch((error) => {
      response.status(500).send({
        message: "failed",
        error,
      });
    });

A continuación, cargue otra imagen en Cloudinary. Para hacer eso, ingrese el siguiente código en el thenbloque:


() => {
      cloudinary.uploader
        .upload(data.image)
        .then()
        .catch((err) => {
          response.status(500).send({
            message: "failed",
            err,
          });
        });
    }

Ahora reemplacemos nuestro registro inicial con los nuevos detalles de la imagen. Reemplace el contenido del thenbloque con lo siguiente:


(result) => {
          db.pool.connect((err, client) => {
            
            // update query
            const updateQuery =
              "UPDATE images SET title = $1, cloudinary_id = $2, image_url = $3 WHERE cloudinary_id = $4";
            const value = [
              data.title,
              result.public_id,
              result.secure_url,
              cloudinary_id,
            ];
          });
        }

Ejecutamos la consulta usando el siguiente código justo debajo de la declaración de la consulta:


// execute query
            client
              .query(updateQuery, value)
              .then(() => {

                // send success response
                response.status(201).send({
                  status: "success",
                  data: {
                    message: "Image Updated Successfully"
                  },
                });
              })
              .catch((e) => {
                response.status(500).send({
                  message: "Update Failed",
                  e,
                });
              });

En este punto, esto es lo que tengo:


// update image
app.put("/update-image/:cloudinary_id", (request, response) => {
  // unique ID
  const { cloudinary_id } = request.params;

  // collected image from a user
  const data = {
    title: request.body.title,
    image: request.body.image,
  };

    // delete image from cloudinary first
    cloudinary.uploader
      .destroy(cloudinary_id)

      // upload image here
      .then(() => {
        cloudinary.uploader
          .upload(data.image)

          // update the database here
          .then((result) => {
            db.pool.connect((err, client) => {
            // update query
            const updateQuery =
              "UPDATE images SET title = $1, cloudinary_id = $2, image_url = $3 WHERE cloudinary_id = $4";
            const value = [
              data.title,
              result.public_id,
              result.secure_url,
              cloudinary_id,
            ];

            // execute query
            client
              .query(updateQuery, value)
              .then(() => {

                // send success response
                response.status(201).send({
                  status: "success",
                  data: {
                    message: "Image Updated Successfully"
                  },
                });
              })
              .catch((e) => {
                response.status(500).send({
                  message: "Update Failed",
                  e,
                });
              });
            });
          })
          .catch((err) => {
            response.status(500).send({
              message: "failed",
              err,
            });
          });
      })
      .catch((error) => {
        response.status(500).send({
          message: "failed",
          error,
        });
      });
  
});

¡Es tiempo de prueba!

Este es mi cartero en la imagen de abajo:

Texto alternativo

Tome nota de la ID única de Cloudinary que coincide con la imagen que queda en mi biblioteca de medios de Cloudinary.

Ahora eche un vistazo a mi biblioteca de medios Cloudinary en la imagen que sigue:

Texto alternativo

Tome nota de la nueva imagen que reemplaza a la inicial en mi biblioteca de medios arriba.

Además, vea que la ID única de Cloudinary coincida con la de mi base de datos con el nuevo título. Ver imagen a continuación:

Texto alternativo

¡Yayeh! ¡Lo hiciste increíblemente bien! 💪

Acabamos de completar una aplicación de gestión de imágenes con Node.js, Cloudinary y Postgres.

Optimización de código con enrutamiento rápido

Express Routing nos permite optimizar nuestro código Node.js o darle una estructura más modular al separar la lógica comercial de los controladores. Queremos usar eso para limpiar nuestro código hasta ahora.

Comenzaremos creando una nueva carpeta con el nombre routesen el directorio raíz:


mk dir routes

En la carpeta de rutas, crea un archivo con el nombre: routes.js.

Para ventanas:


echo . > routes.js

Para Mac:


touch routes.js

Vacíe el routes.jsarchivo si hay algo e ingrese el siguiente código:


const express = require('express');

const router = express.Router();



module.exports = router;

Agrega el siguiente código arriba de la última línea:


const cloudinary = require("cloudinary").v2;
require("dotenv").config();
const db = require("../services/dbConnect.js");

// cloudinary configuration
cloudinary.config({
  cloud_name: process.env.CLOUD_NAME,
  api_key: process.env.API_KEY,
  api_secret: process.env.API_SECRET,
});

De vuelta en el archivo App.js, elimine el siguiente código:


const cloudinary = require("cloudinary").v2;
require("dotenv").config();
const db = require("./services/dbConnect.js");

// cloudinary configuration
cloudinary.config({
  cloud_name: process.env.CLOUD_NAME,
  api_key: process.env.API_KEY,
  api_secret: process.env.API_SECRET,
});

Mueva todas las API a routes.js.

Cambie todas las apariciones de appa con routercuidado.

Mi routes.jsarchivo ahora se ve así .

De vuelta en el app.jsarchivo, importe el routes.jsarchivo así:


// import the routes file
const routes = require("./routes/routes")

Ahora registre las rutas así:


// register the routes 
app.use('/', routes);

Este es mi app.jsarchivo en este momento:


const express = require("express");
const app = express();

// import the routes file
const routes = require("./routes/routes")

// body parser configuration
const bodyParser = require("body-parser");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// register the routes 
app.use('/', routes);

module.exports = app;

Es hora de probar y ver si nuestras rutas siguen funcionando como antes.

Asegúrate de que el tuyo funcione como el mío a continuación:

imagen persistente

imagen persistente

recuperar-imagen

recuperar imagen

actualizar-imagen

actualizar imagen

Eliminar Imagen

Eliminar Imagen

¡Guau! Hemos podido separar nuestras rutas de nuestro app.jsarchivo.

El código para esto está aquí .

Aunque nuestro routes.jsarchivo todavía es largo, tenemos una buena base para separar nuestra lógica comercial de nuestros controladores. Ha llegado el momento de hacer precisamente eso.

Cómo mover cada punto final a un archivo diferente

Comience creando una nueva carpeta en la routescarpeta y asígnele el nombre controllers.

En la carpeta de controladores, cree 5 archivos y asígneles el nombre de los 5 puntos finales.

Nuestra carpeta y archivos deben estar estructurados de la siguiente manera:

estructura de carpetas y archivos

Volviendo al archivo route.js, trabajemos en la image-uploadAPI. Cortar el siguiente código:


(request, response) => {
  // collected image from a user
  const data = {
    image: request.body.image,
  };

  // upload image here
  cloudinary.uploader
    .upload(data.image)
    .then((result) => {
      response.status(200).send({
        message: "success",
        result,
      });
    })
    .catch((error) => {
      response.status(500).send({
        message: "failure",
        error,
      });
    });
}

En el imageUploadarchivo, equipare el código que ya cortó desde el image-uploadpunto final para que le exports.imageUploadguste:


exports.imageUpload = (request, response) => {
    // collected image from a user
    const data = {
      image: request.body.image,
    };
  
    // upload image here
    cloudinary.uploader
      .upload(data.image)
      .then((result) => {
        response.status(200).send({
          message: "success",
          result,
        });
      })
      .catch((error) => {
        response.status(500).send({
          message: "failure",
          error,
        });
      });
  }

Ahora importemos lo necesario para que este código funcione. Así que este es mi imageUploadarchivo en este momento:


const cloudinary = require("cloudinary").v2;
require("dotenv").config();

// cloudinary configuration
cloudinary.config({
  cloud_name: process.env.CLOUD_NAME,
  api_key: process.env.API_KEY,
  api_secret: process.env.API_SECRET,
});

exports.imageUpload = (request, response) => {
    // collected image from a user
    const data = {
      image: request.body.image,
    };
  
    // upload image here
    cloudinary.uploader
      .upload(data.image)
      .then((result) => {
        response.status(200).send({
          message: "success",
          result,
        });
      })
      .catch((error) => {
        response.status(500).send({
          message: "failure",
          error,
        });
      });
  }

Importemos y registremos la imageUploadAPI en el routes.jsarchivo así:


const imageUpload = require("./controllers/imageUpload");

// image upload API
router.post("image-upload", imageUpload.imageUpload);

Ahora tenemos esta línea de código que apunta a la imageUploadAPI en el imageUpload.jsarchivo del routes.jsarchivo.

¡Qué asombroso! Nuestro código es más legible.

Asegúrese de probar la API para asegurarse de que funciona correctamente. El mío funciona perfectamente. Ver imagen a continuación:

resultado de la prueba de carga de imagen

¡Ahora es tu turno!

Aplique lo que ha aprendido a las otras API. Veamos lo que tienes.

Te estaré esperando del otro lado...

Si estás aquí, entonces creo que has hecho el tuyo y están funcionando perfectamente, o al menos, ya diste lo mejor de ti. ¡Prestigio!

Mira el mío aquí .

Felicidades. Usted es maravilloso :)

El código de optimización de código está aquí .

Muy bien, al siguiente paso.

Cómo implementar en GitHub y Heroku

Ahora que hemos completado nuestra aplicación, implementémosla en Heroku para que podamos acceder a ella incluso sin estar en nuestra computadora portátil donde se escribió el código.

Lo guiaré a través de la carga de nuestra aplicación en GitHub y su implementación en Heroku .

Sin más preámbulos, ensuciémonos las manos.

Cómo subir el código a GitHub

Subir o subir a GitHub es tan fácil como comer tu comida favorita. Consulte este recurso para aprender a enviar su proyecto desde su máquina local a GitHub.

Cómo implementar en Heroku

Comencemos por crear una cuenta en Heroku .

Si ha creado una cuenta, es posible que se le haya pedido que cree una aplicación (es decir, una carpeta donde se alojará su aplicación). Puedes hacer eso, pero yo haré el mío usando mi terminal ya que el terminal viene con algunas funcionalidades adicionales que necesitaremos más adelante.

Abra su proyecto en una terminal si aún no lo ha hecho. Usaré el terminal integrado VS Code.

Instale la CLI de Heroku:

npm install heroku

Texto alternativo

Inicie sesión en la CLI de Heroku. Esto abrirá una ventana del navegador, que puede usar para iniciar sesión.

heroku login

Texto alternativo

Crea una aplicación. Puede tener cualquier nombre. estoy node-postgres-cloudinaryusando

heroku create node-postgres-cloudinary

Texto alternativo

Vaya a su tablero de Heroku y encontrará la aplicación recién creada.

Texto alternativo

Waalaah!

Así es como se ve el mío en la imagen de arriba. Ya tengo algunas aplicaciones allí, pero puedes ver la que acabo de crear.

Ahora agreguemos la base de datos PostgreSQL a la aplicación.

Cómo agregar Heroku Postgres

Haga clic en la aplicación que acaba de crear. Te llevará al tablero de la aplicación.

Texto alternativo

Haga clic en la Resourcespestaña/menú.

Texto alternativo

En la Add-onsSección, busque y seleccione Heroku Postgres.

Texto alternativo

Asegúrese de seleccionar el Hobby Dev - Freeplan en la ventana emergente que aparece a continuación:

Texto alternativo

Haga clic en el provisionbotón para agregarlo a la aplicación de la siguiente manera:

Texto alternativo

Haga clic en Heroku Postgrespara ir al Heroku Postgrestablero.

Texto alternativo

Haga clic en la settingspestaña:

Texto alternativo

Haga clic en View Credentials:

Texto alternativo

En las Credenciales, nos interesa la CLI de Heroku. Lo usaremos en un momento.

Texto alternativo

De vuelta a la terminal.

Confirmemos si Heroku Postgresse agregó con éxito. Introduce lo siguiente en la terminal:

heroku addons

Texto alternativo

¡Síiiiiiiiii! Se agregó con éxito.

Antes de continuar, asegúrese de que su PostgreSQL pathesté configurado correctamente si está en Windows . Siga este enlace para aprender a configurar un archivo path. La ruta debe ser así: C:\Program Files\PostgreSQL\<VERSION>\bin.

La versión dependerá de la instalada en su máquina. El mío es: C:\Program Files\PostgreSQL\12\binya que estoy usando el version 12.

La siguiente imagen podría ser útil:

Texto alternativo

Es posible que deba navegar a la carpeta donde está instalado PostgreSQL en su máquina para encontrar su propia ruta.

Texto alternativo

Inicie sesión en el Heroku Postgresuso de la CLI de Heroku desde nuestras Heroku Postgrescredenciales. Este es el mío, el tuyo será diferente:

heroku pg:psql postgresql-slippery-19135 --app node-postgres-cloudinary

Si recibió un error, lo más probable es que su ruta no esté configurada correctamente.

Cómo preparar nuestra conexión de base de datos para que coincida con la de Heroku

Por el momento, mi base de datos se ve así:


const pg = require("pg");

const config = {
  user: "tutorial",
  database: "tutorial",
  password: "tutorial",
  port: 5432,
  max: 10, // max number of clients in the pool
  idleTimeoutMillis: 30000,
};

const pool = new pg.Pool(config);

pool.on("connect", () => {
  console.log("connected to the Database");
});

Si intenta conectar Heroku a esto, obtendrá un error. Esto se debe a que Heroku ya tiene una connection stringconfiguración. Así que tenemos que configurar nuestra conexión para que Heroku pueda conectarse fácilmente.

Voy a refactorizar mi archivo de conexión a la base de datos ( dbConnect.js) y .envel archivo para que esto suceda.

  • dbConnect.js

const pg = require('pg');
require('dotenv').config();

// set production variable. This will be called when deployed to a live host
const isProduction = process.env.NODE_ENV === 'production';

// configuration details
const connectionString = `postgresql://${process.env.DB_USER}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_DATABASE}`;

// if project has been deployed, connect with the host's DATABASE_URL
// else connect with the local DATABASE_URL
const pool = new pg.Pool({
  connectionString: isProduction ? process.env.DATABASE_URL : connectionString,
  ssl: isProduction,
});

// display message on success if successful
pool.on('connect', () => {
  console.log('Teamwork Database connected successfully!');
});

  • archivo .env

DB_USER="tutorial"
DB_PASSWORD="tutorial"
DB_HOST="localhost"
DB_PORT="5432"
DB_DATABASE="tutorial"

Con la configuración del archivo dbconnecty .env, estamos listos para exportar nuestra base de datos y tablas desde nuestra máquina local a heroku postgres.

Cómo exportar bases de datos y tablas

Vaya a su pgAdminy localice la base de datos para este tutorial. El mío es un tutorial.

Texto alternativo

Haga clic derecho sobre él y seleccione Backup. Esto abrirá una nueva ventana.

Texto alternativo

Ingrese un nombre para el archivo SQL como lo hice yo. Seleccione el plainformato. Luego haga clic en Copia de seguridad. Esto guardará el archivo en su carpeta de documentos.

Localice el archivo y muévalo al directorio del proyecto. Puede estar en cualquier lugar del directorio, pero elijo mover el mío al servicesdirectorio porque contiene los archivos relacionados con la base de datos.

De vuelta en la terminal, navegue a la carpeta que contiene el archivo SQL y ejecute el siguiente código para agregar las tablas que acabamos de exportar a la base de heroku postgresdatos:

cat <your-SQL-file> | <heroku-CLI-from-the-heroku-posgres-credentials>

Así es como se ve el mío:

cat tutorial.sql | heroku pg:psql postgresql-slippery-19135 --app node-postgres-cloudinary

Texto alternativo

¿Notaste que cambié el directorio a servicios ( cd services)? Ahí es donde se encuentra mi sqlarchivo.

¡Guau! Acabamos de exportar con éxito nuestra base de datos y tablas a Heroku.

ya casi termina...

Cómo decirle a GitHub que hicimos cambios

Agregue los archivos en los que hemos realizado cambios:

$ git add .

El punto ( .) agrega todos los archivos.

Confirme sus últimos cambios:

$ git commit -m "refactored the dbConnect and .env file to fit in to heroku; Added the database SQL file"

Empuje los archivos confirmados:

$ git push origin -u master

Texto alternativo

Finalmente implementando nuestra aplicación

Ve al tablero de tu aplicación:

Texto alternativo

Seleccione el método de implementación de GitHub:

Texto alternativo

Busque y seleccione un repositorio, y haga clic en connect:

Texto alternativo

Seleccione la rama que desea implementar (en mi caso, es la masterrama):

Texto alternativo

Habilite la implementación automática haciendo clic en el Enable automatic deploymentbotón como en la imagen de arriba.

Haga clic en el Deploybotón en el despliegue manual

Texto alternativo

No tendremos que hacer todo esto para implementaciones posteriores.

Ahora tiene un botón que le indica "ver el sitio" después de completar la compilación. Pinchalo. Esto abrirá su aplicación en una nueva pestaña.

Texto alternativo

¡Oh, no! ¿Un insecto? ¿¿Error de la aplicación??

No te preocupes, es solo un pequeño problema. Algo que nunca debe olvidar hacer al realizar implementaciones. La mayoría de los servicios de alojamiento lo requerirán.

Cómo solucionar el error de la aplicación Heroku

Regrese al directorio raíz de su proyecto.

Cree un archivo y asígnele un nombre Procfile(no tiene extensión).

En el archivo, ingrese el siguiente código:

web: node index.js

Texto alternativo

Esto dirige a Heroku al archivo del servidor ( index.js), que es el punto de entrada de la aplicación. Si su servidor está en un archivo diferente, modifíquelo según sea necesario.

Guarde el archivo y envíe los nuevos cambios a GitHub.

Espere de 2 a 5 minutos para que Heroku detecte automáticamente los cambios en su repositorio de GitHub y represente los cambios en la aplicación.

Ahora puede actualizar esa página de error y ver que su arduo trabajo da sus frutos:

Texto alternativo

También puede probar la retrieve imageruta y verla funcionando.

¡Felicidades! Qué hazaña has logrado.

Otras rutas (persistir-imagen, actualizar-imagen y eliminar-imagen) no funcionarán porque no hemos aprovisionado o agregado cloudinarycomplementos. Es tan sencillo como el PostgreSQLque acabamos de hacer. Así que puedes darle una oportunidad.

Conclusión

Comenzamos este tutorial con el objetivo de aprender cómo construir una aplicación de backend usando Express, Postgres, Cloudinary, Github y Heroku.

Aprendimos cómo almacenar, recuperar, eliminar y actualizar un registro de imagen. Luego organizamos nuestro código con Express Routing, lo subimos a GitHub y lo implementamos en Heroku. Eso fue mucho.

Espero que estén de acuerdo en que valió la pena porque aprendimos mucho. Debe intentar agregar el complemento Cloudinary usted mismo para mejorar aún más su conocimiento.

¡Gracias por leer! 

Enlace: https://www.freecodecamp.org/news/how-to-build-a-backend-application/

#express  #postgres  #cloudinary  #github  #heroku 

What is GEEK

Buddha Community

Cómo Compilar E Implementar Una Aplicación De Back-end
Hermann  Frami

Hermann Frami

1651383480

A Simple Wrapper Around Amplify AppSync Simulator

This serverless plugin is a wrapper for amplify-appsync-simulator made for testing AppSync APIs built with serverless-appsync-plugin.

Install

npm install serverless-appsync-simulator
# or
yarn add serverless-appsync-simulator

Usage

This plugin relies on your serverless yml file and on the serverless-offline plugin.

plugins:
  - serverless-dynamodb-local # only if you need dynamodb resolvers and you don't have an external dynamodb
  - serverless-appsync-simulator
  - serverless-offline

Note: Order is important serverless-appsync-simulator must go before serverless-offline

To start the simulator, run the following command:

sls offline start

You should see in the logs something like:

...
Serverless: AppSync endpoint: http://localhost:20002/graphql
Serverless: GraphiQl: http://localhost:20002
...

Configuration

Put options under custom.appsync-simulator in your serverless.yml file

| option | default | description | | ------------------------ | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | | apiKey | 0123456789 | When using API_KEY as authentication type, the key to authenticate to the endpoint. | | port | 20002 | AppSync operations port; if using multiple APIs, the value of this option will be used as a starting point, and each other API will have a port of lastPort + 10 (e.g. 20002, 20012, 20022, etc.) | | wsPort | 20003 | AppSync subscriptions port; if using multiple APIs, the value of this option will be used as a starting point, and each other API will have a port of lastPort + 10 (e.g. 20003, 20013, 20023, etc.) | | location | . (base directory) | Location of the lambda functions handlers. | | refMap | {} | A mapping of resource resolutions for the Ref function | | getAttMap | {} | A mapping of resource resolutions for the GetAtt function | | importValueMap | {} | A mapping of resource resolutions for the ImportValue function | | functions | {} | A mapping of external functions for providing invoke url for external fucntions | | dynamoDb.endpoint | http://localhost:8000 | Dynamodb endpoint. Specify it if you're not using serverless-dynamodb-local. Otherwise, port is taken from dynamodb-local conf | | dynamoDb.region | localhost | Dynamodb region. Specify it if you're connecting to a remote Dynamodb intance. | | dynamoDb.accessKeyId | DEFAULT_ACCESS_KEY | AWS Access Key ID to access DynamoDB | | dynamoDb.secretAccessKey | DEFAULT_SECRET | AWS Secret Key to access DynamoDB | | dynamoDb.sessionToken | DEFAULT_ACCESS_TOKEEN | AWS Session Token to access DynamoDB, only if you have temporary security credentials configured on AWS | | dynamoDb.* | | You can add every configuration accepted by DynamoDB SDK | | rds.dbName | | Name of the database | | rds.dbHost | | Database host | | rds.dbDialect | | Database dialect. Possible values (mysql | postgres) | | rds.dbUsername | | Database username | | rds.dbPassword | | Database password | | rds.dbPort | | Database port | | watch | - *.graphql
- *.vtl | Array of glob patterns to watch for hot-reloading. |

Example:

custom:
  appsync-simulator:
    location: '.webpack/service' # use webpack build directory
    dynamoDb:
      endpoint: 'http://my-custom-dynamo:8000'

Hot-reloading

By default, the simulator will hot-relad when changes to *.graphql or *.vtl files are detected. Changes to *.yml files are not supported (yet? - this is a Serverless Framework limitation). You will need to restart the simulator each time you change yml files.

Hot-reloading relies on watchman. Make sure it is installed on your system.

You can change the files being watched with the watch option, which is then passed to watchman as the match expression.

e.g.

custom:
  appsync-simulator:
    watch:
      - ["match", "handlers/**/*.vtl", "wholename"] # => array is interpreted as the literal match expression
      - "*.graphql"                                 # => string like this is equivalent to `["match", "*.graphql"]`

Or you can opt-out by leaving an empty array or set the option to false

Note: Functions should not require hot-reloading, unless you are using a transpiler or a bundler (such as webpack, babel or typescript), un which case you should delegate hot-reloading to that instead.

Resource CloudFormation functions resolution

This plugin supports some resources resolution from the Ref, Fn::GetAtt and Fn::ImportValue functions in your yaml file. It also supports some other Cfn functions such as Fn::Join, Fb::Sub, etc.

Note: Under the hood, this features relies on the cfn-resolver-lib package. For more info on supported cfn functions, refer to the documentation

Basic usage

You can reference resources in your functions' environment variables (that will be accessible from your lambda functions) or datasource definitions. The plugin will automatically resolve them for you.

provider:
  environment:
    BUCKET_NAME:
      Ref: MyBucket # resolves to `my-bucket-name`

resources:
  Resources:
    MyDbTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: myTable
      ...
    MyBucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: my-bucket-name
    ...

# in your appsync config
dataSources:
  - type: AMAZON_DYNAMODB
    name: dynamosource
    config:
      tableName:
        Ref: MyDbTable # resolves to `myTable`

Override (or mock) values

Sometimes, some references cannot be resolved, as they come from an Output from Cloudformation; or you might want to use mocked values in your local environment.

In those cases, you can define (or override) those values using the refMap, getAttMap and importValueMap options.

  • refMap takes a mapping of resource name to value pairs
  • getAttMap takes a mapping of resource name to attribute/values pairs
  • importValueMap takes a mapping of import name to values pairs

Example:

custom:
  appsync-simulator:
    refMap:
      # Override `MyDbTable` resolution from the previous example.
      MyDbTable: 'mock-myTable'
    getAttMap:
      # define ElasticSearchInstance DomainName
      ElasticSearchInstance:
        DomainEndpoint: 'localhost:9200'
    importValueMap:
      other-service-api-url: 'https://other.api.url.com/graphql'

# in your appsync config
dataSources:
  - type: AMAZON_ELASTICSEARCH
    name: elasticsource
    config:
      # endpoint resolves as 'http://localhost:9200'
      endpoint:
        Fn::Join:
          - ''
          - - https://
            - Fn::GetAtt:
                - ElasticSearchInstance
                - DomainEndpoint

Key-value mock notation

In some special cases you will need to use key-value mock nottation. Good example can be case when you need to include serverless stage value (${self:provider.stage}) in the import name.

This notation can be used with all mocks - refMap, getAttMap and importValueMap

provider:
  environment:
    FINISH_ACTIVITY_FUNCTION_ARN:
      Fn::ImportValue: other-service-api-${self:provider.stage}-url

custom:
  serverless-appsync-simulator:
    importValueMap:
      - key: other-service-api-${self:provider.stage}-url
        value: 'https://other.api.url.com/graphql'

Limitations

This plugin only tries to resolve the following parts of the yml tree:

  • provider.environment
  • functions[*].environment
  • custom.appSync

If you have the need of resolving others, feel free to open an issue and explain your use case.

For now, the supported resources to be automatically resovled by Ref: are:

  • DynamoDb tables
  • S3 Buckets

Feel free to open a PR or an issue to extend them as well.

External functions

When a function is not defined withing the current serverless file you can still call it by providing an invoke url which should point to a REST method. Make sure you specify "get" or "post" for the method. Default is "get", but you probably want "post".

custom:
  appsync-simulator:
    functions:
      addUser:
        url: http://localhost:3016/2015-03-31/functions/addUser/invocations
        method: post
      addPost:
        url: https://jsonplaceholder.typicode.com/posts
        method: post

Supported Resolver types

This plugin supports resolvers implemented by amplify-appsync-simulator, as well as custom resolvers.

From Aws Amplify:

  • NONE
  • AWS_LAMBDA
  • AMAZON_DYNAMODB
  • PIPELINE

Implemented by this plugin

  • AMAZON_ELASTIC_SEARCH
  • HTTP
  • RELATIONAL_DATABASE

Relational Database

Sample VTL for a create mutation

#set( $cols = [] )
#set( $vals = [] )
#foreach( $entry in $ctx.args.input.keySet() )
  #set( $regex = "([a-z])([A-Z]+)")
  #set( $replacement = "$1_$2")
  #set( $toSnake = $entry.replaceAll($regex, $replacement).toLowerCase() )
  #set( $discard = $cols.add("$toSnake") )
  #if( $util.isBoolean($ctx.args.input[$entry]) )
      #if( $ctx.args.input[$entry] )
        #set( $discard = $vals.add("1") )
      #else
        #set( $discard = $vals.add("0") )
      #end
  #else
      #set( $discard = $vals.add("'$ctx.args.input[$entry]'") )
  #end
#end
#set( $valStr = $vals.toString().replace("[","(").replace("]",")") )
#set( $colStr = $cols.toString().replace("[","(").replace("]",")") )
#if ( $valStr.substring(0, 1) != '(' )
  #set( $valStr = "($valStr)" )
#end
#if ( $colStr.substring(0, 1) != '(' )
  #set( $colStr = "($colStr)" )
#end
{
  "version": "2018-05-29",
  "statements":   ["INSERT INTO <name-of-table> $colStr VALUES $valStr", "SELECT * FROM    <name-of-table> ORDER BY id DESC LIMIT 1"]
}

Sample VTL for an update mutation

#set( $update = "" )
#set( $equals = "=" )
#foreach( $entry in $ctx.args.input.keySet() )
  #set( $cur = $ctx.args.input[$entry] )
  #set( $regex = "([a-z])([A-Z]+)")
  #set( $replacement = "$1_$2")
  #set( $toSnake = $entry.replaceAll($regex, $replacement).toLowerCase() )
  #if( $util.isBoolean($cur) )
      #if( $cur )
        #set ( $cur = "1" )
      #else
        #set ( $cur = "0" )
      #end
  #end
  #if ( $util.isNullOrEmpty($update) )
      #set($update = "$toSnake$equals'$cur'" )
  #else
      #set($update = "$update,$toSnake$equals'$cur'" )
  #end
#end
{
  "version": "2018-05-29",
  "statements":   ["UPDATE <name-of-table> SET $update WHERE id=$ctx.args.input.id", "SELECT * FROM <name-of-table> WHERE id=$ctx.args.input.id"]
}

Sample resolver for delete mutation

{
  "version": "2018-05-29",
  "statements":   ["UPDATE <name-of-table> set deleted_at=NOW() WHERE id=$ctx.args.id", "SELECT * FROM <name-of-table> WHERE id=$ctx.args.id"]
}

Sample mutation response VTL with support for handling AWSDateTime

#set ( $index = -1)
#set ( $result = $util.parseJson($ctx.result) )
#set ( $meta = $result.sqlStatementResults[1].columnMetadata)
#foreach ($column in $meta)
    #set ($index = $index + 1)
    #if ( $column["typeName"] == "timestamptz" )
        #set ($time = $result["sqlStatementResults"][1]["records"][0][$index]["stringValue"] )
        #set ( $nowEpochMillis = $util.time.parseFormattedToEpochMilliSeconds("$time.substring(0,19)+0000", "yyyy-MM-dd HH:mm:ssZ") )
        #set ( $isoDateTime = $util.time.epochMilliSecondsToISO8601($nowEpochMillis) )
        $util.qr( $result["sqlStatementResults"][1]["records"][0][$index].put("stringValue", "$isoDateTime") )
    #end
#end
#set ( $res = $util.parseJson($util.rds.toJsonString($util.toJson($result)))[1][0] )
#set ( $response = {} )
#foreach($mapKey in $res.keySet())
    #set ( $s = $mapKey.split("_") )
    #set ( $camelCase="" )
    #set ( $isFirst=true )
    #foreach($entry in $s)
        #if ( $isFirst )
          #set ( $first = $entry.substring(0,1) )
        #else
          #set ( $first = $entry.substring(0,1).toUpperCase() )
        #end
        #set ( $isFirst=false )
        #set ( $stringLength = $entry.length() )
        #set ( $remaining = $entry.substring(1, $stringLength) )
        #set ( $camelCase = "$camelCase$first$remaining" )
    #end
    $util.qr( $response.put("$camelCase", $res[$mapKey]) )
#end
$utils.toJson($response)

Using Variable Map

Variable map support is limited and does not differentiate numbers and strings data types, please inject them directly if needed.

Will be escaped properly: null, true, and false values.

{
  "version": "2018-05-29",
  "statements":   [
    "UPDATE <name-of-table> set deleted_at=NOW() WHERE id=:ID",
    "SELECT * FROM <name-of-table> WHERE id=:ID and unix_timestamp > $ctx.args.newerThan"
  ],
  variableMap: {
    ":ID": $ctx.args.id,
##    ":TIMESTAMP": $ctx.args.newerThan -- This will be handled as a string!!!
  }
}

Requires

Author: Serverless-appsync
Source Code: https://github.com/serverless-appsync/serverless-appsync-simulator 
License: MIT License

#serverless #sync #graphql 

Generis: Versatile Go Code Generator

Generis

Versatile Go code generator.

Description

Generis is a lightweight code preprocessor adding the following features to the Go language :

  • Generics.
  • Free-form macros.
  • Conditional compilation.
  • HTML templating.
  • Allman style conversion.

Sample

package main;

// -- IMPORTS

import (
    "html"
    "io"
    "log"
    "net/http"
    "net/url"
    "strconv"
    );

// -- DEFINITIONS

#define DebugMode
#as true

// ~~

#define HttpPort
#as 8080

// ~~

#define WriteLine( {{text}} )
#as log.Println( {{text}} )

// ~~

#define local {{variable}} : {{type}};
#as var {{variable}} {{type}};

// ~~

#define DeclareStack( {{type}}, {{name}} )
#as
    // -- TYPES

    type {{name}}Stack struct
    {
        ElementArray []{{type}};
    }

    // -- INQUIRIES

    func ( stack * {{name}}Stack ) IsEmpty(
        ) bool
    {
        return len( stack.ElementArray ) == 0;
    }

    // -- OPERATIONS

    func ( stack * {{name}}Stack ) Push(
        element {{type}}
        )
    {
        stack.ElementArray = append( stack.ElementArray, element );
    }

    // ~~

    func ( stack * {{name}}Stack ) Pop(
        ) {{type}}
    {
        local
            element : {{type}};

        element = stack.ElementArray[ len( stack.ElementArray ) - 1 ];

        stack.ElementArray = stack.ElementArray[ : len( stack.ElementArray ) - 1 ];

        return element;
    }
#end

// ~~

#define DeclareStack( {{type}} )
#as DeclareStack( {{type}}, {{type:PascalCase}} )

// -- TYPES

DeclareStack( string )
DeclareStack( int32 )

// -- FUNCTIONS

func HandleRootPage(
    response_writer http.ResponseWriter,
    request * http.Request
    )
{
    local
        boolean : bool;
    local
        natural : uint;
    local
        integer : int;
    local
        real : float64;
    local
        escaped_html_text,
        escaped_url_text,
        text : string;
    local
        integer_stack : Int32Stack;

    boolean = true;
    natural = 10;
    integer = 20;
    real = 30.0;
    text = "text";
    escaped_url_text = "&escaped text?";
    escaped_html_text = "<escaped text/>";

    integer_stack.Push( 10 );
    integer_stack.Push( 20 );
    integer_stack.Push( 30 );

    #write response_writer
        <!DOCTYPE html>
        <html lang="en">
            <head>
                <meta charset="utf-8">
                <title><%= request.URL.Path %></title>
            </head>
            <body>
                <% if ( boolean ) { %>
                    <%= "URL : " + request.URL.Path %>
                    <br/>
                    <%@ natural %>
                    <%# integer %>
                    <%& real %>
                    <br/>
                    <%~ text %>
                    <%^ escaped_url_text %>
                    <%= escaped_html_text %>
                    <%= "<%% ignored %%>" %>
                    <%% ignored %%>
                <% } %>
                <br/>
                Stack :
                <br/>
                <% for !integer_stack.IsEmpty() { %>
                    <%# integer_stack.Pop() %>
                <% } %>
            </body>
        </html>
    #end
}

// ~~

func main()
{
    http.HandleFunc( "/", HandleRootPage );

    #if DebugMode
        WriteLine( "Listening on http://localhost:HttpPort" );
    #end

    log.Fatal(
        http.ListenAndServe( ":HttpPort", nil )
        );
}

Syntax

#define directive

Constants and generic code can be defined with the following syntax :

#define old code
#as new code

#define old code
#as
    new
    code
#end

#define
    old
    code
#as new code

#define
    old
    code
#as
    new
    code
#end

#define parameter

The #define directive can contain one or several parameters :

{{variable name}} : hierarchical code (with properly matching brackets and parentheses)
{{variable name#}} : statement code (hierarchical code without semicolon)
{{variable name$}} : plain code
{{variable name:boolean expression}} : conditional hierarchical code
{{variable name#:boolean expression}} : conditional statement code
{{variable name$:boolean expression}} : conditional plain code

They can have a boolean expression to require they match specific conditions :

HasText text
HasPrefix prefix
HasSuffix suffix
HasIdentifier text
false
true
!expression
expression && expression
expression || expression
( expression )

The #define directive must not start or end with a parameter.

#as parameter

The #as directive can use the value of the #define parameters :

{{variable name}}
{{variable name:filter function}}
{{variable name:filter function:filter function:...}}

Their value can be changed through one or several filter functions :

LowerCase
UpperCase
MinorCase
MajorCase
SnakeCase
PascalCase
CamelCase
RemoveComments
RemoveBlanks
PackStrings
PackIdentifiers
ReplacePrefix old_prefix new_prefix
ReplaceSuffix old_suffix new_suffix
ReplaceText old_text new_text
ReplaceIdentifier old_identifier new_identifier
AddPrefix prefix
AddSuffix suffix
RemovePrefix prefix
RemoveSuffix suffix
RemoveText text
RemoveIdentifier identifier

#if directive

Conditional code can be defined with the following syntax :

#if boolean expression
    #if boolean expression
        ...
    #else
        ...
    #end
#else
    #if boolean expression
        ...
    #else
        ...
    #end
#end

The boolean expression can use the following operators :

false
true
!expression
expression && expression
expression || expression
( expression )

#write directive

Templated HTML code can be sent to a stream writer using the following syntax :

#write writer expression
    <% code %>
    <%@ natural expression %>
    <%# integer expression %>
    <%& real expression %>
    <%~ text expression %>
    <%= escaped text expression %>
    <%! removed content %>
    <%% ignored tags %%>
#end

Limitations

  • There is no operator precedence in boolean expressions.
  • The --join option requires to end the statements with a semicolon.
  • The #writer directive is only available for the Go language.

Installation

Install the DMD 2 compiler (using the MinGW setup option on Windows).

Build the executable with the following command line :

dmd -m64 generis.d

Command line

generis [options]

Options

--prefix # : set the command prefix
--parse INPUT_FOLDER/ : parse the definitions of the Generis files in the input folder
--process INPUT_FOLDER/ OUTPUT_FOLDER/ : reads the Generis files in the input folder and writes the processed files in the output folder
--trim : trim the HTML templates
--join : join the split statements
--create : create the output folders if needed
--watch : watch the Generis files for modifications
--pause 500 : time to wait before checking the Generis files again
--tabulation 4 : set the tabulation space count
--extension .go : generate files with this extension

Examples

generis --process GS/ GO/

Reads the Generis files in the GS/ folder and writes Go files in the GO/ folder.

generis --process GS/ GO/ --create

Reads the Generis files in the GS/ folder and writes Go files in the GO/ folder, creating the output folders if needed.

generis --process GS/ GO/ --create --watch

Reads the Generis files in the GS/ folder and writes Go files in the GO/ folder, creating the output folders if needed and watching the Generis files for modifications.

generis --process GS/ GO/ --trim --join --create --watch

Reads the Generis files in the GS/ folder and writes Go files in the GO/ folder, trimming the HTML templates, joining the split statements, creating the output folders if needed and watching the Generis files for modifications.

Version

2.0

Author: Senselogic
Source Code: https://github.com/senselogic/GENERIS 
License: View license

#go #golang #code 

Cómo Compilar E Implementar Una Aplicación De Back-end

En este tutorial, aprenderemos cómo crear e implementar un backend de aplicación de administración de imágenes.

Podrá almacenar un registro de una imagen en la base de datos, recuperar el registro de la imagen de la base de datos, actualizar el registro e incluso eliminar el registro por completo, según sea el caso.

Para lograr todo esto, usaremos Express (un marco Node.js), Postgres (una base de datos), Cloudinary (un almacenamiento de imágenes basado en la nube), GitHub (para el control/almacenamiento de versiones) y Heroku (una plataforma de alojamiento).

Todas estas herramientas son gratuitas. Así que no tienes que preocuparte por cómo pagarlos. Gracias a estos grandes innovadores.

requisitos previos

Si es nuevo en la mayoría de estas tecnologías, le aconsejo que siga mi otro tutorial sobre cómo crear un servidor y cargar imágenes en Cloudinary .

Si está totalmente interesado en Postgres, consulte este tutorial .

Cuando estés listo, ¡manos a la obra!

Cómo almacenar y recuperar un registro de imagen

Crear base de datos y tabla

Por lo tanto, querrá comenzar clonando este proyecto si aún no lo tiene.

En tu pgAdmin :

  • Crea una base de datos y nómbralatutorial
  • Crea una tabla y nómbralatutorial
  • Cree un rol de inicio de sesión/grupo y asígnele el nombre tutorial. (No olvides darle todos los privilegios.)

De vuelta en el directorio de su proyecto, instale los paquetes node-postgres ( npm i pg) y make-runnable ( npm i make-runnable).

En su package.jsonarchivo, reemplace el contenido de "scripts"con "create": "node ./services/dbConnect createTables". Usaremos esto para ejecutar el dbConnectarchivo que estamos a punto de crear.

Cree un services/dbConnectarchivo que contenga el siguiente código:


const pg = require("pg");

const config = {
  user: "tutorial",
  database: "tutorial",
  password: "tutorial",
  port: 5432,
  max: 10, // max number of clients in the pool
  idleTimeoutMillis: 30000,
};

const pool = new pg.Pool(config);

pool.on("connect", () => {
  console.log("connected to the Database");
});

const createTables = () => {
  const imageTable = `CREATE TABLE IF NOT EXISTS
    images(
      id SERIAL PRIMARY KEY,
      title VARCHAR(128) NOT NULL,
      cloudinary_id VARCHAR(128) NOT NULL,
      image_url VARCHAR(128) NOT NULL
    )`;
  pool
    .query(imageTable)
    .then((res) => {
      console.log(res);
      pool.end();
    })
    .catch((err) => {
      console.log(err);
      pool.end();
    });
};

pool.on("remove", () => {
  console.log("client removed");
  process.exit(0);
});

//export pool and createTables to be accessible from anywhere within the application
module.exports = {
  createTables,
  pool,
};

require("make-runnable");

Ahora estamos listos para crear la tabla en nuestra base de datos. Si estás listo, ¡vamos al rock and roll!

Ejecuta el siguiente código en tu terminal:


  npm run create

Si la imagen a continuación es su resultado, entonces está listo para comenzar:

Texto alternativo

Verifique su pgAdmin , y debería tener su mesa asentada correctamente en su base de datos como en la imagen a continuación:

Texto alternativo

Muy bien, ha sido un largo camino. Es hora de unir Node, Postgres y Cloudinary.

Cómo crear puntos finales para almacenar y recuperar registros de imágenes

Extremo 1: imagen persistente

Primero, requiere el dbConnect.jsarchivo en la parte superior del app.jsarchivo así:

  const db = require('services/dbConnect.js');

Luego, en el app.jsarchivo, cree un nuevo punto final (imagen persistente) con el siguiente código:


// persist image
app.post("/persist-image", (request, response) => {
  // collected image from a user
  const data = {
    title: request.body.title,
    image: request.body.image,
  }

  // upload image here
  cloudinary.uploader.upload(data.image)
  .then().catch((error) => {
    response.status(500).send({
      message: "failure",
      error,
    });
  });
})

Reemplace el thenbloque con el siguiente código:


.then((image) => {
    db.pool.connect((err, client) => {
      // inset query to run if the upload to cloudinary is successful
      const insertQuery = 'INSERT INTO images (title, cloudinary_id, image_url) 
         VALUES($1,$2,$3) RETURNING *';
      const values = [data.title, image.public_id, image.secure_url];
    })
  })

Los image.public_idy image.secure_urlse obtienen como parte de los detalles devueltos para una imagen después de que la imagen se haya cargado correctamente en Cloudinary.

Ahora mantenemos un registro de image.public_idy image.secure_url(como puede ver en el código anterior) para usarlo para recuperar, actualizar o eliminar el registro de la imagen cuando lo creamos conveniente.

Muy bien, ¡sigamos adelante!

Aún en el thenbloque, agregue el siguiente código debajo de la consulta que creamos:


// execute query
client.query(insertQuery, values)
      .then((result) => {
        result = result.rows[0];

        // send success response
        response.status(201).send({
          status: "success",
          data: {
            message: "Image Uploaded Successfully",
            title: result.title,
            cloudinary_id: result.cloudinary_id,
            image_url: result.image_url,
          },
        })
      }).catch((e) => {
        response.status(500).send({
          message: "failure",
          e,
        });
      })

Así que nuestro persist-imagepunto final ahora se ve así:


// persist image
app.post("/persist-image", (request, response) => {
  // collected image from a user
  const data = {
    title: request.body.title,
    image: request.body.image
  }

  // upload image here
  cloudinary.uploader.upload(data.image)
  .then((image) => {
    db.pool.connect((err, client) => {
      // inset query to run if the upload to cloudinary is successful
      const insertQuery = 'INSERT INTO images (title, cloudinary_id, image_url) 
         VALUES($1,$2,$3) RETURNING *';
      const values = [data.title, image.public_id, image.secure_url];

      // execute query
      client.query(insertQuery, values)
      .then((result) => {
        result = result.rows[0];

        // send success response
        response.status(201).send({
          status: "success",
          data: {
            message: "Image Uploaded Successfully",
            title: result.title,
            cloudinary_id: result.cloudinary_id,
            image_url: result.image_url,
          },
        })
      }).catch((e) => {
        response.status(500).send({
          message: "failure",
          e,
        });
      })
    })  
  }).catch((error) => {
    response.status(500).send({
      message: "failure",
      error,
    });
  });
});

Ahora vamos a probar todo nuestro arduo trabajo:

Abra su cartero y pruebe su punto final como se muestra en la imagen a continuación. El mío fue un éxito. ¿Espero que el tuyo tampoco haya tenido errores?

Texto alternativo

Abra su consola/panel de Cloudinary y verifique su media Library. Su nueva imagen debería estar sentada allí cómodamente como la mía a continuación:

Texto alternativo

Y ahora a la razón principal por la que estamos aquí: verifique la imagestabla en su pgAdmin . La mía es la que ves a continuación:

Texto alternativo

¡Oh, la la! Llegamos hasta aquí. Por favor, tómese un descanso si lo necesita. Estaré aquí esperándote cuando regreses. :)

Si está listo, recuperemos la imagen que conservamos hace un momento.

Punto final 2: Recuperar imagen

Comience con este código:


app.get("/retrieve-image/:cloudinary_id", (request, response) => {

});

A continuación, necesitaremos recopilar una identificación única del usuario para recuperar una imagen en particular. Así que agregue const { id } = request.params;al código anterior así:


app.get("/retrieve-image/:cloudinary_id", (request, response) => {
  // data from user
  const { cloudinary_id } = request.params;

});

Agregue el siguiente código justo debajo del código anterior:


db.pool.connect((err, client) => {
      // query to find image
    const query = "SELECT * FROM images WHERE cloudinary_id = $1";
    const value = [cloudinary_id];
    });

Debajo de la consulta, ejecute la consulta con el siguiente código:


// execute query
    client
      .query(query, value)
      .then((output) => {
        response.status(200).send({
          status: "success",
          data: {
            id: output.rows[0].cloudinary_id,
            title: output.rows[0].title,
            url: output.rows[0].image_url,
          },
        });
      })
      .catch((error) => {
        response.status(401).send({
          status: "failure",
          data: {
            message: "could not retrieve record!",
            error,
          },
        });
      });

Ahora nuestra retrieve-imageAPI se ve así:


app.get("/retrieve-image/:cloudinary_id", (request, response) => {
  // data from user
  const { cloudinary_id } = request.params;
  
  db.pool.connect((err, client) => {
    // query to find image
    const query = "SELECT * FROM images WHERE cloudinary_id = $1";
    const value = [cloudinary_id];

    // execute query
    client
      .query(query, value)
      .then((output) => {
        response.status(200).send({
          status: "success",
          data: {
            id: output.rows[0].cloudinary_id,
            title: output.rows[0].title,
            url: output.rows[0].image_url,
          },
        });
      })
      .catch((error) => {
        response.status(401).send({
          status: "failure",
          data: {
            message: "could not retrieve record!",
            error,
          },
        });
      });
  });
});

A ver qué tan bien lo hicimos:

En su cartero, copie cloudinary_idy agréguelo a la URL como en la imagen a continuación:

Texto alternativo

¡SÍ SÍ! También podemos recuperar nuestra imagen.

Si estás aquí, te mereces un aplauso y una ovación de pie por tu laboriosidad.

¡Felicidades! Acabas de alcanzar un gran hito.

El código para almacenar y recuperar registros de imágenes está aquí .

Cómo actualizar y eliminar un registro de imagen

Ahora veremos cómo eliminar y actualizar un registro de imagen según sea el caso. Comencemos con el punto final de eliminación.

Eliminar punto final

En el archivo app.js, comience con el siguiente código:


// delete image
app.delete("delete-image/:cloudinary_id", (request, response) => {

});

A continuación, queremos obtener el ID único de la imagen que queremos eliminar de la URL, es decir, cloudinary_id. Entonces, dentro del código anterior, agregue:


const { cloudinary_id } = request.params;

Ahora comenzamos el proceso de eliminación.

Primero, eliminamos de Cloudinary. Agregue el siguiente código para eliminar la imagen de Cloudinary:


cloudinary.uploader
    .destroy(cloudinary_id)
    .then((result) => {
      response.status(200).send({
        message: "success",
        result,
      });
    })
    .catch((error) => {
      response.status(500).send({
        message: "Failure",
        error,
      });
    });

En este punto, nuestra API solo puede eliminar la imagen de Cloudinary (puede consultarla en cartero). Pero también queremos deshacernos del registro que tenemos en nuestra base de datos de Postgres.

En segundo lugar, eliminamos de nuestra base de datos de Postgres. Para hacerlo, reemplace el código en el thenbloque con lo siguiente query:


db.pool.connect((err, client) => {
     
      // delete query
      const deleteQuery = "DELETE FROM images WHERE cloudinary_id = $1";
      const deleteValue = [cloudinary_id];

})

Ejecute la consulta con el siguiente código debajo:


// execute delete query
      client.query(deleteQuery, deleteValue)
      .then((deleteResult) => {
        response.status(200).send({
          message: "Image Deleted Successfully!",
          deleteResult
        });
      }).catch((e) => {
        response.status(500).send({
          message: "Image Couldn't be Deleted!",
          e
        });
      });

Así que nuestro Endpoint debería verse así:


// delete image
app.delete("/delete-image/:cloudinary_id", (request, response) => {
  // unique ID
  const { cloudinary_id } = request.params;

  // delete image from cloudinary first
  cloudinary.uploader
    .destroy(cloudinary_id)

    // delete image record from postgres also
    .then(() => {
      db.pool.connect((err, client) => {
     
      // delete query
      const deleteQuery = "DELETE FROM images WHERE cloudinary_id = $1";
      const deleteValue = [cloudinary_id];

      // execute delete query
      client
        .query(deleteQuery, deleteValue)
        .then((deleteResult) => {
          response.status(200).send({
            message: "Image Deleted Successfully!",
            deleteResult,
          });
        })
        .catch((e) => {
          response.status(500).send({
            message: "Image Couldn't be Deleted!",
            e,
          });
        });
      })
    })
    .catch((error) => {
      response.status(500).send({
        message: "Failure",
        error,
      });
    });
});

Ha llegado el momento de que pongamos a prueba nuestro Endpoint.

El siguiente es mi Cloudinary media librarycon dos imágenes que ya subí. Tome nota de su identificación única ( public_id).

Texto alternativo

Si aún no lo tiene, utilice el extremo de imagen persistente para cargar algunas imágenes.

Ahora procedamos al cartero:

Texto alternativo

Observe, la identificación única, ya que coincide con una de las imágenes en mi biblioteca de medios de Cloudinary.

Desde la salida, ejecutamos el comando DELETE y eso eliminó una FILA de nuestra TABLA de imágenes en nuestra base de datos.

Ahora esta es mi biblioteca de medios con una de las imágenes restantes:

Texto alternativo

Walahhhh... Ahora podemos deshacernos de una imagen.

Tómese un descanso si lo necesita. ✌🏾

Si está listo, estoy listo para actualizar las imágenes.

Actualizar API de imagen

Debajo de la delete-imageAPI, comencemos a crear la update-imageAPI con el siguiente código:


// update image
app.put("/update-image/:cloudinary_id", (request, response) => {

});

All codes will live in there.

Recopile la identificación única de Cloudinary y los detalles de la nueva imagen del usuario con el siguiente código:


// unique ID
  const { cloudinary_id } = request.params;

// collected image from a user
  const data = {
    title: request.body.title,
    image: request.body.image,
  };

Elimina la imagen de Cloudinary con el siguiente código:


// delete image from cloudinary first
  cloudinary.uploader
    .destroy(cloudinary_id)
      // upload image here
    .then()
    .catch((error) => {
      response.status(500).send({
        message: "failed",
        error,
      });
    });

A continuación, cargue otra imagen en Cloudinary. Para hacer eso, ingrese el siguiente código en el thenbloque:


() => {
      cloudinary.uploader
        .upload(data.image)
        .then()
        .catch((err) => {
          response.status(500).send({
            message: "failed",
            err,
          });
        });
    }

Ahora reemplacemos nuestro registro inicial con los nuevos detalles de la imagen. Reemplace el contenido del thenbloque con lo siguiente:


(result) => {
          db.pool.connect((err, client) => {
            
            // update query
            const updateQuery =
              "UPDATE images SET title = $1, cloudinary_id = $2, image_url = $3 WHERE cloudinary_id = $4";
            const value = [
              data.title,
              result.public_id,
              result.secure_url,
              cloudinary_id,
            ];
          });
        }

Ejecutamos la consulta usando el siguiente código justo debajo de la declaración de la consulta:


// execute query
            client
              .query(updateQuery, value)
              .then(() => {

                // send success response
                response.status(201).send({
                  status: "success",
                  data: {
                    message: "Image Updated Successfully"
                  },
                });
              })
              .catch((e) => {
                response.status(500).send({
                  message: "Update Failed",
                  e,
                });
              });

En este punto, esto es lo que tengo:


// update image
app.put("/update-image/:cloudinary_id", (request, response) => {
  // unique ID
  const { cloudinary_id } = request.params;

  // collected image from a user
  const data = {
    title: request.body.title,
    image: request.body.image,
  };

    // delete image from cloudinary first
    cloudinary.uploader
      .destroy(cloudinary_id)

      // upload image here
      .then(() => {
        cloudinary.uploader
          .upload(data.image)

          // update the database here
          .then((result) => {
            db.pool.connect((err, client) => {
            // update query
            const updateQuery =
              "UPDATE images SET title = $1, cloudinary_id = $2, image_url = $3 WHERE cloudinary_id = $4";
            const value = [
              data.title,
              result.public_id,
              result.secure_url,
              cloudinary_id,
            ];

            // execute query
            client
              .query(updateQuery, value)
              .then(() => {

                // send success response
                response.status(201).send({
                  status: "success",
                  data: {
                    message: "Image Updated Successfully"
                  },
                });
              })
              .catch((e) => {
                response.status(500).send({
                  message: "Update Failed",
                  e,
                });
              });
            });
          })
          .catch((err) => {
            response.status(500).send({
              message: "failed",
              err,
            });
          });
      })
      .catch((error) => {
        response.status(500).send({
          message: "failed",
          error,
        });
      });
  
});

¡Es tiempo de prueba!

Este es mi cartero en la imagen de abajo:

Texto alternativo

Tome nota de la ID única de Cloudinary que coincide con la imagen que queda en mi biblioteca de medios de Cloudinary.

Ahora eche un vistazo a mi biblioteca de medios Cloudinary en la imagen que sigue:

Texto alternativo

Tome nota de la nueva imagen que reemplaza a la inicial en mi biblioteca de medios arriba.

Además, vea que la ID única de Cloudinary coincida con la de mi base de datos con el nuevo título. Ver imagen a continuación:

Texto alternativo

¡Yayeh! ¡Lo hiciste increíblemente bien! 💪

Acabamos de completar una aplicación de gestión de imágenes con Node.js, Cloudinary y Postgres.

Optimización de código con enrutamiento rápido

Express Routing nos permite optimizar nuestro código Node.js o darle una estructura más modular al separar la lógica comercial de los controladores. Queremos usar eso para limpiar nuestro código hasta ahora.

Comenzaremos creando una nueva carpeta con el nombre routesen el directorio raíz:


mk dir routes

En la carpeta de rutas, crea un archivo con el nombre: routes.js.

Para ventanas:


echo . > routes.js

Para Mac:


touch routes.js

Vacíe el routes.jsarchivo si hay algo e ingrese el siguiente código:


const express = require('express');

const router = express.Router();



module.exports = router;

Agrega el siguiente código arriba de la última línea:


const cloudinary = require("cloudinary").v2;
require("dotenv").config();
const db = require("../services/dbConnect.js");

// cloudinary configuration
cloudinary.config({
  cloud_name: process.env.CLOUD_NAME,
  api_key: process.env.API_KEY,
  api_secret: process.env.API_SECRET,
});

De vuelta en el archivo App.js, elimine el siguiente código:


const cloudinary = require("cloudinary").v2;
require("dotenv").config();
const db = require("./services/dbConnect.js");

// cloudinary configuration
cloudinary.config({
  cloud_name: process.env.CLOUD_NAME,
  api_key: process.env.API_KEY,
  api_secret: process.env.API_SECRET,
});

Mueva todas las API a routes.js.

Cambie todas las apariciones de appa con routercuidado.

Mi routes.jsarchivo ahora se ve así .

De vuelta en el app.jsarchivo, importe el routes.jsarchivo así:


// import the routes file
const routes = require("./routes/routes")

Ahora registre las rutas así:


// register the routes 
app.use('/', routes);

Este es mi app.jsarchivo en este momento:


const express = require("express");
const app = express();

// import the routes file
const routes = require("./routes/routes")

// body parser configuration
const bodyParser = require("body-parser");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// register the routes 
app.use('/', routes);

module.exports = app;

Es hora de probar y ver si nuestras rutas siguen funcionando como antes.

Asegúrate de que el tuyo funcione como el mío a continuación:

imagen persistente

imagen persistente

recuperar-imagen

recuperar imagen

actualizar-imagen

actualizar imagen

Eliminar Imagen

Eliminar Imagen

¡Guau! Hemos podido separar nuestras rutas de nuestro app.jsarchivo.

El código para esto está aquí .

Aunque nuestro routes.jsarchivo todavía es largo, tenemos una buena base para separar nuestra lógica comercial de nuestros controladores. Ha llegado el momento de hacer precisamente eso.

Cómo mover cada punto final a un archivo diferente

Comience creando una nueva carpeta en la routescarpeta y asígnele el nombre controllers.

En la carpeta de controladores, cree 5 archivos y asígneles el nombre de los 5 puntos finales.

Nuestra carpeta y archivos deben estar estructurados de la siguiente manera:

estructura de carpetas y archivos

Volviendo al archivo route.js, trabajemos en la image-uploadAPI. Cortar el siguiente código:


(request, response) => {
  // collected image from a user
  const data = {
    image: request.body.image,
  };

  // upload image here
  cloudinary.uploader
    .upload(data.image)
    .then((result) => {
      response.status(200).send({
        message: "success",
        result,
      });
    })
    .catch((error) => {
      response.status(500).send({
        message: "failure",
        error,
      });
    });
}

En el imageUploadarchivo, equipare el código que ya cortó desde el image-uploadpunto final para que le exports.imageUploadguste:


exports.imageUpload = (request, response) => {
    // collected image from a user
    const data = {
      image: request.body.image,
    };
  
    // upload image here
    cloudinary.uploader
      .upload(data.image)
      .then((result) => {
        response.status(200).send({
          message: "success",
          result,
        });
      })
      .catch((error) => {
        response.status(500).send({
          message: "failure",
          error,
        });
      });
  }

Ahora importemos lo necesario para que este código funcione. Así que este es mi imageUploadarchivo en este momento:


const cloudinary = require("cloudinary").v2;
require("dotenv").config();

// cloudinary configuration
cloudinary.config({
  cloud_name: process.env.CLOUD_NAME,
  api_key: process.env.API_KEY,
  api_secret: process.env.API_SECRET,
});

exports.imageUpload = (request, response) => {
    // collected image from a user
    const data = {
      image: request.body.image,
    };
  
    // upload image here
    cloudinary.uploader
      .upload(data.image)
      .then((result) => {
        response.status(200).send({
          message: "success",
          result,
        });
      })
      .catch((error) => {
        response.status(500).send({
          message: "failure",
          error,
        });
      });
  }

Importemos y registremos la imageUploadAPI en el routes.jsarchivo así:


const imageUpload = require("./controllers/imageUpload");

// image upload API
router.post("image-upload", imageUpload.imageUpload);

Ahora tenemos esta línea de código que apunta a la imageUploadAPI en el imageUpload.jsarchivo del routes.jsarchivo.

¡Qué asombroso! Nuestro código es más legible.

Asegúrese de probar la API para asegurarse de que funciona correctamente. El mío funciona perfectamente. Ver imagen a continuación:

resultado de la prueba de carga de imagen

¡Ahora es tu turno!

Aplique lo que ha aprendido a las otras API. Veamos lo que tienes.

Te estaré esperando del otro lado...

Si estás aquí, entonces creo que has hecho el tuyo y están funcionando perfectamente, o al menos, ya diste lo mejor de ti. ¡Prestigio!

Mira el mío aquí .

Felicidades. Usted es maravilloso :)

El código de optimización de código está aquí .

Muy bien, al siguiente paso.

Cómo implementar en GitHub y Heroku

Ahora que hemos completado nuestra aplicación, implementémosla en Heroku para que podamos acceder a ella incluso sin estar en nuestra computadora portátil donde se escribió el código.

Lo guiaré a través de la carga de nuestra aplicación en GitHub y su implementación en Heroku .

Sin más preámbulos, ensuciémonos las manos.

Cómo subir el código a GitHub

Subir o subir a GitHub es tan fácil como comer tu comida favorita. Consulte este recurso para aprender a enviar su proyecto desde su máquina local a GitHub.

Cómo implementar en Heroku

Comencemos por crear una cuenta en Heroku .

Si ha creado una cuenta, es posible que se le haya pedido que cree una aplicación (es decir, una carpeta donde se alojará su aplicación). Puedes hacer eso, pero yo haré el mío usando mi terminal ya que el terminal viene con algunas funcionalidades adicionales que necesitaremos más adelante.

Abra su proyecto en una terminal si aún no lo ha hecho. Usaré el terminal integrado VS Code.

Instale la CLI de Heroku:

npm install heroku

Texto alternativo

Inicie sesión en la CLI de Heroku. Esto abrirá una ventana del navegador, que puede usar para iniciar sesión.

heroku login

Texto alternativo

Crea una aplicación. Puede tener cualquier nombre. estoy node-postgres-cloudinaryusando

heroku create node-postgres-cloudinary

Texto alternativo

Vaya a su tablero de Heroku y encontrará la aplicación recién creada.

Texto alternativo

Waalaah!

Así es como se ve el mío en la imagen de arriba. Ya tengo algunas aplicaciones allí, pero puedes ver la que acabo de crear.

Ahora agreguemos la base de datos PostgreSQL a la aplicación.

Cómo agregar Heroku Postgres

Haga clic en la aplicación que acaba de crear. Te llevará al tablero de la aplicación.

Texto alternativo

Haga clic en la Resourcespestaña/menú.

Texto alternativo

En la Add-onsSección, busque y seleccione Heroku Postgres.

Texto alternativo

Asegúrese de seleccionar el Hobby Dev - Freeplan en la ventana emergente que aparece a continuación:

Texto alternativo

Haga clic en el provisionbotón para agregarlo a la aplicación de la siguiente manera:

Texto alternativo

Haga clic en Heroku Postgrespara ir al Heroku Postgrestablero.

Texto alternativo

Haga clic en la settingspestaña:

Texto alternativo

Haga clic en View Credentials:

Texto alternativo

En las Credenciales, nos interesa la CLI de Heroku. Lo usaremos en un momento.

Texto alternativo

De vuelta a la terminal.

Confirmemos si Heroku Postgresse agregó con éxito. Introduce lo siguiente en la terminal:

heroku addons

Texto alternativo

¡Síiiiiiiiii! Se agregó con éxito.

Antes de continuar, asegúrese de que su PostgreSQL pathesté configurado correctamente si está en Windows . Siga este enlace para aprender a configurar un archivo path. La ruta debe ser así: C:\Program Files\PostgreSQL\<VERSION>\bin.

La versión dependerá de la instalada en su máquina. El mío es: C:\Program Files\PostgreSQL\12\binya que estoy usando el version 12.

La siguiente imagen podría ser útil:

Texto alternativo

Es posible que deba navegar a la carpeta donde está instalado PostgreSQL en su máquina para encontrar su propia ruta.

Texto alternativo

Inicie sesión en el Heroku Postgresuso de la CLI de Heroku desde nuestras Heroku Postgrescredenciales. Este es el mío, el tuyo será diferente:

heroku pg:psql postgresql-slippery-19135 --app node-postgres-cloudinary

Si recibió un error, lo más probable es que su ruta no esté configurada correctamente.

Cómo preparar nuestra conexión de base de datos para que coincida con la de Heroku

Por el momento, mi base de datos se ve así:


const pg = require("pg");

const config = {
  user: "tutorial",
  database: "tutorial",
  password: "tutorial",
  port: 5432,
  max: 10, // max number of clients in the pool
  idleTimeoutMillis: 30000,
};

const pool = new pg.Pool(config);

pool.on("connect", () => {
  console.log("connected to the Database");
});

Si intenta conectar Heroku a esto, obtendrá un error. Esto se debe a que Heroku ya tiene una connection stringconfiguración. Así que tenemos que configurar nuestra conexión para que Heroku pueda conectarse fácilmente.

Voy a refactorizar mi archivo de conexión a la base de datos ( dbConnect.js) y .envel archivo para que esto suceda.

  • dbConnect.js

const pg = require('pg');
require('dotenv').config();

// set production variable. This will be called when deployed to a live host
const isProduction = process.env.NODE_ENV === 'production';

// configuration details
const connectionString = `postgresql://${process.env.DB_USER}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_DATABASE}`;

// if project has been deployed, connect with the host's DATABASE_URL
// else connect with the local DATABASE_URL
const pool = new pg.Pool({
  connectionString: isProduction ? process.env.DATABASE_URL : connectionString,
  ssl: isProduction,
});

// display message on success if successful
pool.on('connect', () => {
  console.log('Teamwork Database connected successfully!');
});

  • archivo .env

DB_USER="tutorial"
DB_PASSWORD="tutorial"
DB_HOST="localhost"
DB_PORT="5432"
DB_DATABASE="tutorial"

Con la configuración del archivo dbconnecty .env, estamos listos para exportar nuestra base de datos y tablas desde nuestra máquina local a heroku postgres.

Cómo exportar bases de datos y tablas

Vaya a su pgAdminy localice la base de datos para este tutorial. El mío es un tutorial.

Texto alternativo

Haga clic derecho sobre él y seleccione Backup. Esto abrirá una nueva ventana.

Texto alternativo

Ingrese un nombre para el archivo SQL como lo hice yo. Seleccione el plainformato. Luego haga clic en Copia de seguridad. Esto guardará el archivo en su carpeta de documentos.

Localice el archivo y muévalo al directorio del proyecto. Puede estar en cualquier lugar del directorio, pero elijo mover el mío al servicesdirectorio porque contiene los archivos relacionados con la base de datos.

De vuelta en la terminal, navegue a la carpeta que contiene el archivo SQL y ejecute el siguiente código para agregar las tablas que acabamos de exportar a la base de heroku postgresdatos:

cat <your-SQL-file> | <heroku-CLI-from-the-heroku-posgres-credentials>

Así es como se ve el mío:

cat tutorial.sql | heroku pg:psql postgresql-slippery-19135 --app node-postgres-cloudinary

Texto alternativo

¿Notaste que cambié el directorio a servicios ( cd services)? Ahí es donde se encuentra mi sqlarchivo.

¡Guau! Acabamos de exportar con éxito nuestra base de datos y tablas a Heroku.

ya casi termina...

Cómo decirle a GitHub que hicimos cambios

Agregue los archivos en los que hemos realizado cambios:

$ git add .

El punto ( .) agrega todos los archivos.

Confirme sus últimos cambios:

$ git commit -m "refactored the dbConnect and .env file to fit in to heroku; Added the database SQL file"

Empuje los archivos confirmados:

$ git push origin -u master

Texto alternativo

Finalmente implementando nuestra aplicación

Ve al tablero de tu aplicación:

Texto alternativo

Seleccione el método de implementación de GitHub:

Texto alternativo

Busque y seleccione un repositorio, y haga clic en connect:

Texto alternativo

Seleccione la rama que desea implementar (en mi caso, es la masterrama):

Texto alternativo

Habilite la implementación automática haciendo clic en el Enable automatic deploymentbotón como en la imagen de arriba.

Haga clic en el Deploybotón en el despliegue manual

Texto alternativo

No tendremos que hacer todo esto para implementaciones posteriores.

Ahora tiene un botón que le indica "ver el sitio" después de completar la compilación. Pinchalo. Esto abrirá su aplicación en una nueva pestaña.

Texto alternativo

¡Oh, no! ¿Un insecto? ¿¿Error de la aplicación??

No te preocupes, es solo un pequeño problema. Algo que nunca debe olvidar hacer al realizar implementaciones. La mayoría de los servicios de alojamiento lo requerirán.

Cómo solucionar el error de la aplicación Heroku

Regrese al directorio raíz de su proyecto.

Cree un archivo y asígnele un nombre Procfile(no tiene extensión).

En el archivo, ingrese el siguiente código:

web: node index.js

Texto alternativo

Esto dirige a Heroku al archivo del servidor ( index.js), que es el punto de entrada de la aplicación. Si su servidor está en un archivo diferente, modifíquelo según sea necesario.

Guarde el archivo y envíe los nuevos cambios a GitHub.

Espere de 2 a 5 minutos para que Heroku detecte automáticamente los cambios en su repositorio de GitHub y represente los cambios en la aplicación.

Ahora puede actualizar esa página de error y ver que su arduo trabajo da sus frutos:

Texto alternativo

También puede probar la retrieve imageruta y verla funcionando.

¡Felicidades! Qué hazaña has logrado.

Otras rutas (persistir-imagen, actualizar-imagen y eliminar-imagen) no funcionarán porque no hemos aprovisionado o agregado cloudinarycomplementos. Es tan sencillo como el PostgreSQLque acabamos de hacer. Así que puedes darle una oportunidad.

Conclusión

Comenzamos este tutorial con el objetivo de aprender cómo construir una aplicación de backend usando Express, Postgres, Cloudinary, Github y Heroku.

Aprendimos cómo almacenar, recuperar, eliminar y actualizar un registro de imagen. Luego organizamos nuestro código con Express Routing, lo subimos a GitHub y lo implementamos en Heroku. Eso fue mucho.

Espero que estén de acuerdo en que valió la pena porque aprendimos mucho. Debe intentar agregar el complemento Cloudinary usted mismo para mejorar aún más su conocimiento.

¡Gracias por leer! 

Enlace: https://www.freecodecamp.org/news/how-to-build-a-backend-application/

#express  #postgres  #cloudinary  #github  #heroku 

Best of Crypto

Best of Crypto

1648079400

A General Purpose OpenAPI Code Generator for Algorand

generator

This is a general purpose OpenAPI code generator. It is currently used to completely generate the HTTP code in the Java SDK, and generate some of the HTTP code in our Golang SDK.

Usage

We currently have two HTTP endpoints. One for algod and one for indexer, so in most cases, this tool would be run once with each OpenAPI spec.

Build as a self-executing jar:

~$ mvn package -DskipTests
~$ java -jar target/generator-*-jar-with-dependencies.jar -h

You'll see that there are a number of subcommands:

  • java - the original Java SDK generator.
  • responses - generate randomized test files for SDK unit tests.
  • template - a generator that uses velocity templates rather than Java code to configure the code generation.

Code layout

The command line interface uses JCommander to define the command line interface. See Main.java.

The main code involves an OpenAPI parser / event generator and several listeners for the actual generation.

object layout

Templates

The template subcommand is using Apache Velocity as the underlying template engine. Things like variables, loops, and statements are all supported. So business logic can technically be implemented in the template if it's actually necessary.

Template files

There are three phases: client, query, and model. Each phase must provide two templates, one for the file generation and one to specify the filename to be used. If all results should go to the same file. For query and model generation the template will be executed once for each query / model. If you want to put everything in one file return the same filename twice in a row and the processing will exit early.

phasefilenamepurpose
clientclient.vmClient class with functions to call each query.
clientclient_filename.vmFile to write to the client output directory.
queryquery.vmTemplate to use for generating query files.
queryquery_filename.vmFile to write to the query output directory.
modelmodel.vmTemplate to use for generating model files.
modelmodel_filename.vmFile to write to the model output directory.

Output directories

The template command will only run the templates which have an output directory is provided. So if you just want to regenerate models, only use the -m option.

  -c, --clientOutputDir
    Directory to write client file(s).
  -m, --modelsOutputDir
    Directory to write model file(s).
  -q, --queryOutputDir
    Directory to write query file(s).

Property files

The template subcommand accepts a --propertyFiles option. It can be provided multiple times, or as a comma separated list of files. Property files will be processed and bound to a velocity variable available to templates.

template variables

For details on a type you can put it directly into your template. It will be serialized along with its fields for your reference. Here is a high level description of what is available:

templatevariabletypepurpose
allstrStringHelpers.javaSome string utilities are available. See StringHelpers.java for details. There are simple things like $str.capitalize("someData") -> SomeData, and also some more complex helpers like $str.formatDoc($query.doc, "// ") which will split the document at the word boundary nearest to 80 characters without going over, and add a prefix to each new line.
allorderOrderHelpers.javaSome ordering utilities available. See OrderHelpers.java for details. An example utility function is $order.propertiesWithOrdering($props, $preferred_order), where $props is a list of properties and $preferred_order is a string list to use when ordering the properties list.
allpropFilePropertiesThe contents of all property files are available with this variable. For example if package=com.algorand.v2.algod is in the property file, the template may use ${propFile.package}.
allmodelsHashMap<StructDef, List<TypeDef>>A list of all models.
allqueriesList<QueryDef>A list of all queries.
queryqQueryDefThe current query definition.
modeldefStructDefThe current model definition if multiple files are being generated.
modelpropsList<TypeDef>A list of properties for the current model.

Example usage

In the following example, we are careful to generate the algod code first because the algod models are a strict subset of the indexer models. For that reason, we are able to reuse some overlapping models from indexer in algod.

~$ java -jar generator*jar template
        -s algod.oas2.json
        -t go_templates
        -c algodClient
        -m allModels
        -q algodQueries
        -p common_config.properties,algod_config.properties
~$ java -jar generator*jar template
        -s indexer.oas2.json
        -t go_templates
        -c indexerClient
        -m allModels
        -q indexerQueries
        -p common_config.properties,indexer_config.properties

Test Template

There is a test template that gives you some basic usage in the test_templates directory.

You can generate the test code in the output directory with the following commands:

~$ mkdir output
~$ java -jar target/generator-*-jar-with-dependencies.jar \
    template \
    -s /path/to/a/spec/file/indexer.oas2.json \
    -t test_templates/ \
    -m output \
    -q output \
    -c output \
    -p test_templates/my.properties

Golang Template

The Golang templates are in the go_templates directory.

The Golang HTTP API is only partially generated. The hand written parts were not totally consistent with the spec and that makes it difficult to regenerate them. Regardless, an attempt has been made. In the templates there are some macros which map "generated" values to the hand written ones. For example the query types have this mapping:

#macro ( queryType )
#if ( ${str.capitalize($q.name)} == "SearchForAccounts" )
SearchAccounts## The hand written client doesn't quite match the spec...
#elseif ( ${str.capitalize($q.name)} == "GetStatus" )
Status##
#elseif ( ${str.capitalize($q.name)} == "GetPendingTransactionsByAddress" )
PendingTransactionInformationByAddress##
#elseif ( ${str.capitalize($q.name)} == "GetPendingTransactions" )
PendingTransactions##
#else
${str.capitalize($q.name)}##
#end
#end

Other mappings are more specific to the language, such as the OpenAPI type to SDK type:

#macro ( toQueryType $param )##
#if ( $param.algorandFormat == "RFC3339 String" )
string##
#elseif ( $param.type == "integer" )
uint64##
#elseif ( $param.type == "string" )
string##
#elseif ( $param.type == "boolean" )
bool##
#elseif( $param.type == "binary" )
string##
#else
UNHANDLED TYPE
- ref: $!param.refType
- type: $!param.type
- array type: $!param.arrayType
- algorand format: $!param.algorandFormat
- format: $!param.format
##$unknown.type ## force a template failure because $unknown.type does not exist.
#end
#end

Because of this, we are phasing in code generation gradually by skipping some types. The skipped types are specified in the property files:

common_config.properties

model_skip=AccountParticipation,AssetParams,RawBlockJson,etc,...

algod_config.properties

query_skip=Block,BlockRaw,SendRawTransaction,SuggestedParams,etc,...

indexer_config.properties

query_skip=LookupAssetByID,LookupAccountTransactions,SearchForAssets,LookupAssetBalances,LookupAssetTransactions,LookupBlock,LookupTransactions,SearchForTransactions

Java Template

The Java templates are in the java_templates directory.

These are not used yet, they are the initial experiments for the template engine. Since the Java SDK has used code generation from the beginning, we should be able to fully migrate to the template engine eventually.

Automation

Preparing an external repository for automatic code generation

In general, the automation pipeline will build and run whatever Dockerfile is found in a repository's templates directory. For instructions on how to configure the templates directory, look at the repository template directory example.

If you are trying to verify that automatic code generation works as intended, we recommend creating a testing branch from that repository and using the SKIP_PR=true environment variable to avoid creating pull requests. If all goes according to plan, generated files should be available in the container's /repo directory.

Setting up the automatic generator

The automatic generator scripts depend on certain prerequisites that are listed in automation/REQUIREMENTS.md. Once those conditions have been satisfied, automatically generating code for external repositories should be as easy as building and running a particular SDK's templates/Dockerfile file.


Download Details:
Author: algorand
Source Code: https://github.com/algorand/generator
License:

#algorand  #blockchain  #cryptocurrency #java #golang #openapi 

Desmond Ivana

1602569524

The E-Scooters Wave Is Coming: Steer Your Business To Success

E-scooters are becoming more and more familiar. The compactness, coupled with the skill of evading jam-packed traffics, makes the fast-paced world lean towards this micro-mobility innovation. Besides, with COVID-19 propelling the need for safety and privacy, you do not have drivers in an E-scooters ecosystem! With the system being entirely automated, people can smart-lock and unlock E-scooters without any hassle.

Various top manufacturers are spending quality hours exhaustively on their R&D to shift from fuel-led automobiles to electric power-generating vehicles. Although people hesitate to make investments when it comes to buying an e-vehicle, using such vehicles for commuting is no big deal. If you’re an entrepreneur aiming to launch an Uber for E-Scooters app, now is the time to roll up your sleeves as E-scooters are being legalized in numerous countries, including New York.

Now, let’s discuss the remunerative advantages of E-scooters and why entrepreneurs needn’t hesitate to initiate their E-scooter App development.

Lucrative Benefits of E-Scooters

Outplay traffic effortlessly: One of the main concerns of people worldwide is not reaching the destination on time due to prolonged traffic. With four-wheelers becoming more predominant, the situation is steeping towards the worsening phase. With its compact nature, E-scooters can help people sail past traffic without a sweat. This way, people conserve and utilize their time efficiently.

The environmental impact: As simple as it may sound, automobiles pollute the environment on a massive scale. It is high-time people raise their concerns against environmental degradation. E-scooters are the best alternatives from the environmental perspective. These scooters run on a 500W electric motor, eliminating any form of pollution.

Inexpensive in every aspect: The maintenance and fuel costs of automobiles is way too high as vehicles get older. However, with an E-scooter, all it takes is a rechargeable battery with less or no maintenance at all. Moreover, entrepreneurs get to enhance their profits seamlessly, even after providing economical rides to passengers. There’s only an initial investment cost that an entrepreneur needs to take care of.

The 5-Step Workflow of an E-Scooters App

While building a smartphone application, it is essential to focus on the platform’s workflow. An E-scooter app with a user-friendly architecture and immersive workflow can create an instant impact among the audience. Let’s discuss the simple yet intuitive 5-step workflow here,

  • Users register with the platform and locate E-scooters nearby by enabling their location preferences.

  • Users choose their best-suited E-scooters based on numerous metrics like pricing, battery capacity, ratings, etc.

  • Users unlock the vehicle by scanning the QR code. They initiate their trip and drive towards their destination.

  • Upon reaching the destination, users park the E-scooters securely and smart-lock the vehicle.

  • The app displays the total fare with a detailed breakdown. Users pay the amount via a multitude of payment gateways and share their experience in the form of ratings & reviews.

Features that make the E-Scooter app stand apart

Apps like Lime, Bird, etc., have already set a benchmark when it comes to the E-Scooter app market. You need USPs to lure customer attention. Some of the unique elements worth-considering include,

  • QR scanning - To initiate and terminate rides.

  • In-app wallet - To pay for rides effortlessly.

  • Multi-lingual support - To access the app in the customers’ preferred language.

  • Schedule bookings - To book rides well-in-advance.

  • In-app chat/call - To establish a connection between the support team and users.

  • VoIP-based Call masking - To mask users’ contact details.

  • Geofencing - To map virtual boundaries and keep an eye on E-scooters.

Capitalize on the growing market

Establishing your E-Scooters Rental app at the spur of the moment is highly essential if you wish to scale your business in the shortest possible time. Some of the reasons to initiate your app development right away include,

The unexplored market: The E-Scooter market is still in its nascent stages. Rolling out an app with the right feature-set and approach can help you yield unrestricted revenue.

Competitors are experiencing massive growth: Apps like Lime, Bird, etc., witness unprecedented growth in the past few years. Lime was valued at $2.4 billion in 2019. On the other hand, Bird has spread across 100 cities in Europe. With competitors reaping profits, it is high-time entrepreneurs needn’t hesitate to invest in this business opportunity.

The ‘E’ shift among customers: People are gradually moving towards e-vehicles as a measure to conserve time and environment. By rolling out an on-demand app for E-scooters that is economical, people will inevitably turn towards your platform for the daily commute.

Conclusion

In this modern world, saving time and energy is the need of the hour. Add to that the indispensable role of conserving the environment. E-scooters cater to all these aspects comprehensively. Make the most out of the situation and have no second thoughts about initiating your E-Scooter app development.

#uber for e-scooters #e-scooter app development #e-scooter app #e-scooter rental app #uber like app for e-scooters #uber for e-scooters app