Как создать и развернуть бэкенд-приложение с помощью Express, Postgres, Github и Heroku

В этой статье мы узнаем, как построить и развернуть бэкэнд приложения для управления изображениями.

Оно сможет хранить запись изображения в базе данных, получать запись изображения из базы данных, обновлять запись и даже полностью удалять ее в зависимости от ситуации.

Для достижения всего этого мы будем использовать Express (фреймворк Node.js), Postgres (базу данных), Cloudinary (облачное хранилище изображений), GitHub (для контроля/хранения версий) и Heroku (платформу для хостинга).

Все эти инструменты бесплатны. Поэтому вам не нужно беспокоиться о том, как за них заплатить. Спасибо этим великим новаторам.

Предварительные условия
Если вы новичок в большинстве этих технологий, я бы посоветовал вам изучить мой другой учебник о том, как создать сервер и загрузить изображения на Cloudinary.

Если вы еще не знакомы с Postgres, ознакомьтесь с этим руководством.

Как только вы будете готовы, приступайте к работе!

Как хранить и извлекать записи изображений
Создание базы данных и таблицы
Итак, вы захотите начать с клонирования этого проекта, если у вас его еще нет.

В своем pgAdmin:

Создайте базу данных и назовите ее учебник
Создайте таблицу и назовите ее tutorial
Создайте роль Login/Group и назовите ее tutorial. (Не забудьте дать ей все привилегии).
Вернувшись в каталог проекта, установите пакеты node-postgres (npm i pg) и make-runnnable (npm i make-runnable).

В вашем файле package.json замените содержимое строки

«scripts» на «create»: «node ./services/dbConnect
createTables». Мы будем использовать это для выполнения файла dbConnect, который мы собираемся создать.

Создайте файл services/dbConnect, который будет содержать следующий код:


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");

Вход в полноэкранный режим Выйти из полноэкранного режима

Теперь мы готовы к созданию таблицы в нашей базе данных. Если вы готовы, давайте начнем!

Выполните следующий код в терминале:

npm run create

Если вы видите результат, как на картинке ниже, значит, все готово:

Проверьте свой pgAdmin, и ваша таблица должна быть правильно размещена в базе данных, как на изображении ниже:

Хорошо, это был долгий путь. Пришло время объединить Node, Postgres и Cloudinary.

Как создать конечные точки для хранения и получения записей изображений
Конечная точка 1: персистентное изображение
Сначала потребуйте файл dbConnect.js в верхней части файла app.js, как показано ниже:

const db = require('services/dbConnect.js');
Войдите в полноэкранный режим Выйти из полноэкранного режима

Затем в файле app.js создайте новую конечную точку (persist-image) со следующим кодом:

// сохранять изображение
app.post(«/persist-image», (request, response) => {
// собираем изображение от пользователя
const data = {
заголовок: request.body.title,
image: request.body.image,
}

// загружаем изображение сюда
cloudinary.uploader.upload(data.image)
.then().catch((error) => {
response.status(500).send({
сообщение: «failure»,
error,
});we
});
})

Замените блок then следующим кодом:

.then((image) => {
db.pool.connect((err, client) => {
// вставка запроса для выполнения в случае успешной загрузки в cloudinary
const insertQuery = ‘INSERT INTO images (title, cloudinary_id, image_url)
VALUES($1,$2,$3), ВОЗВРАЩАЮЩИЙ *’;
const values = [data.title, image.public_id, image.secure_url];
})
})

Значения image.public_id и image.secure_url получаются как часть данных, возвращаемых для изображения после того, как оно было успешно загружено в Cloudinary.

Теперь мы сохраняем записи image.public_id и image.secure_url (как видно из приведенного выше кода), чтобы использовать их для извлечения, обновления или удаления записи изображения, когда мы посчитаем нужным.

Хорошо, давайте двигаться дальше!

Все еще в блоке then добавьте следующий код под созданным нами запросом:

// выполнить запрос
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,
    });
  })
Вход в полноэкранный режим Выход из полноэкранного режима

Таким образом, наша конечная точка persist-image теперь выглядит следующим образом:

// сохраняем изображение
app.post(«/persist-image», (request, response) => {
// собранное изображение от пользователя
const data = {
заголовок: request.body.title,
изображение: request.body.image
}

// загружаем изображение сюда
cloudinary.uploader.upload(data.image)
.then((image) => {
db.pool.connect((err, client) => {
// вставной запрос, который нужно выполнить, если загрузка в cloudinary прошла успешно
const insertQuery = ‘INSERT INTO images (title, cloudinary_id, image_url)
VALUES($1,$2,$3), ВОЗВРАЩАЮЩИЙ *’;
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({
сообщение: «failure»,
error,
});
});
});

Теперь давайте проверим всю нашу тяжелую работу:

Откройте postman и проверьте свою конечную точку, как показано на рисунке ниже. Моя проверка прошла успешно. Надеюсь, у вас тоже не было ошибок?

Ладно, это был долгий путь. Пришло время объединить Node, Postgres и Cloudinary.

Как создать конечные точки для хранения и получения записей изображений
Конечная точка 1: сохранение изображения
Сначала потребуйте файл dbConnect.js в верхней части файла app.js следующим образом:

const db = require(‘services/dbConnect.js’);
Затем в файле app.js создайте новую конечную точку (persist-image) со следующим кодом:

// сохранять изображение
app.post(«/persist-image», (request, response) => {
// собираем изображение от пользователя
const data = {
заголовок: request.body.title,
image: request.body.image,
}

// загружаем изображение сюда
cloudinary.uploader.upload(data.image)
.then().catch((error) => {
response.status(500).send({
сообщение: «failure»,
error,
});
});
})

Замените блок then следующим кодом:

.then((image) => {
db.pool.connect((err, client) => {
// вставка запроса для выполнения в случае успешной загрузки в cloudinary
const insertQuery = ‘INSERT INTO images (title, cloudinary_id, image_url)
VALUES($1,$2,$3), ВОЗВРАЩАЮЩИЕ *’;
const values = [data.title, image.public_id, image.secure_url];
})
})

image.public_id и image.secure_url получаются как часть информации, возвращаемой для изображения после того, как изображение было успешно загружено в Cloudinary.

Теперь мы сохраняем записи image.public_id и image.secure_url (как видно из приведенного выше кода), чтобы использовать их для извлечения, обновления или удаления записи изображения, когда мы посчитаем нужным.

Хорошо, давайте двигаться дальше!

Все еще в блоке then добавьте следующий код под созданным нами запросом:

// выполнить запрос
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,
    });
  })
Вход в полноэкранный режим Выход из полноэкранного режима

Таким образом, наша конечная точка persist-image теперь выглядит следующим образом:

// сохраняем изображение
app.post(«/persist-image», (request, response) => {
// собранное изображение от пользователя
const data = {
заголовок: request.body.title,
изображение: request.body.image
}

// загружаем изображение сюда
cloudinary.uploader.upload(data.image)
.then((image) => {
db.pool.connect((err, client) => {
// вставной запрос, который нужно выполнить, если загрузка в cloudinary прошла успешно
const insertQuery = ‘INSERT INTO images (title, cloudinary_id, image_url)
VALUES($1,$2,$3), ВОЗВРАЩАЮЩИЙ *’;
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({
сообщение: «failure»,
error,
});
});
});

Теперь давайте проверим всю нашу тяжелую работу:

Откройте postman и проверьте свою конечную точку, как показано на рисунке ниже. Моя проверка прошла успешно. Надеюсь, у вас тоже не было ошибок?

Ладно, это был долгий путь. Пришло время объединить Node, Postgres и Cloudinary.

Как создать конечные точки для хранения и получения записей изображений
Конечная точка 1: сохранение изображения
Сначала потребуйте файл dbConnect.js в верхней части файла app.js следующим образом:

const db = require(‘services/dbConnect.js’);
Затем в файле app.js создайте новую конечную точку (persist-image) со следующим кодом:

// сохранять изображение
app.post(«/persist-image», (request, response) => {
// собираем изображение от пользователя
const data = {
заголовок: request.body.title,
image: request.body.image,
}

// загружаем изображение сюда
cloudinary.uploader.upload(data.image)
.then().catch((error) => {
response.status(500).send({
сообщение: «failure»,
error,
});
});
})

Замените блок then следующим кодом:

.then((image) => {
db.pool.connect((err, client) => {
// вставка запроса для выполнения в случае успешной загрузки в cloudinary
const insertQuery = ‘INSERT INTO images (title, cloudinary_id, image_url)
VALUES($1,$2,$3), ВОЗВРАЩАЮЩИЙ *’;
const values = [data.title, image.public_id, image.secure_url];
})
})

image.public_id и image.secure_url получаются как часть информации, возвращаемой для изображения после его успешной загрузки в Cloudinary.

Теперь мы сохраняем записи image.public_id и image.secure_url (как видно из кода выше), чтобы использовать их для извлечения, обновления или удаления записи изображения, когда мы посчитаем нужным.

Хорошо, давайте двигаться дальше!

Все еще в блоке then добавьте следующий код под созданным нами запросом:

// выполнить запрос
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,
    });
  })
Вход в полноэкранный режим Выход из полноэкранного режима

Таким образом, наша конечная точка persist-image теперь выглядит следующим образом:

// сохраняем изображение
app.post(«/persist-image», (request, response) => {
// собранное изображение от пользователя
const data = {
заголовок: request.body.title,
изображение: request.body.image
}

// загружаем изображение сюда
cloudinary.uploader.upload(data.image)
.then((image) => {
db.pool.connect((err, client) => {
// вставной запрос, который нужно выполнить, если загрузка в cloudinary прошла успешно
const insertQuery = ‘INSERT INTO images (title, cloudinary_id, image_url)
VALUES($1,$2,$3), ВОЗВРАЩАЮЩИЙ *’;
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({
сообщение: «failure»,
error,
});
});
});

Теперь давайте проверим всю нашу тяжелую работу:

Откройте postman и проверьте свою конечную точку, как показано на рисунке ниже. Моя проверка прошла успешно. Надеюсь, у вас тоже не было ошибок?

Откройте консоль/панель Cloudinary и проверьте медиатеку. Ваше новое изображение должно быть удобно расположено там, как у меня ниже:

А теперь перейдем к основной причине, по которой мы здесь собрались: проверьте таблицу images в вашем pgAdmin. Моя — это то, что вы видите ниже:

Оооолала! Мы проделали этот путь. Пожалуйста, сделайте перерыв, если он вам нужен. Я буду ждать, когда вы вернетесь. 🙂

Если вы готовы, то давайте получим изображение, которое мы сохранили минуту назад.

Конечная точка 2: Получение изображения
Начните с этого кода:

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

});

Далее нам нужно будет собрать уникальный ID пользователя, чтобы получить конкретное изображение. Поэтому добавьте const { id } = request.params; в код выше следующим образом:

app.get(«/retrieve-image/:cloudinary_id», (request, response) => { {
// данные от пользователя
const { cloudinary_id } = request.params;

});

Добавьте следующий код непосредственно под кодом выше:

db.pool.connect((err, client) => {
// запрос для поиска изображения
const query = «SELECT * FROM images WHERE cloudinary_id = $1»;
const value = [cloudinary_id];
});

Под запросом выполните запрос со следующим кодом:

// выполнить запрос
клиент
.query(query, value)
.then((output) => {
response.status(200).send({
status: «success»,
данные: {
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»,
данные: {
сообщение: «не удалось получить запись!»,
ошибка,
},
});
});

Теперь наш API retrieve-image выглядит следующим образом:

app.get(«/retrieve-image/:cloudinary_id», (request, response) => {
// данные от пользователя
const { cloudinary_id } = request.params;

db.pool.connect((err, client) => {
// запрос для поиска изображения
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,
      },
    });
  });
Вход в полноэкранный режим Выход из полноэкранного режима

});
});

Давайте посмотрим, насколько хорошо мы справились:

В своем postman скопируйте cloudinary_id и добавьте его в URL, как на изображении ниже:

YEEESSS! Мы также можем получить наше изображение.

Если вы здесь, то вы заслуживаете аплодисментов и оваций за ваше трудолюбие.

Поздравляю! Вы только что достигли великой вехи.

Код для хранения и извлечения записей изображений находится здесь.

Как обновить и удалить запись изображения
Теперь мы рассмотрим, как удалять и обновлять записи изображений в зависимости от ситуации. Начнем с конечной точки удаления.

Конечная точка удаления
В файле app.js начните со следующего кода:

// удалить изображение
app.delete(«delete-image/:cloudinary_id», (request, response) => {

});

Далее мы хотим получить уникальный ID изображения, которое мы хотим удалить, из URL, то есть cloudinary_id. Поэтому внутри кода выше добавьте:

const { cloudinary_id } = request.params;

Теперь мы начинаем процесс удаления.

Сначала мы удалим изображение из Cloudinary. Добавьте следующий код для удаления изображения из Cloudinary:

cloudinary.uploader
.destroy(cloudinary_id)
.then((result) => {
response.status(200).send({
сообщение: «success»,
результат,
});
})
.catch((error) => {
response.status(500).send({
сообщение: «Failure»,
ошибка,
});
});

На данный момент наш API может удалить изображение только из Cloudinary (вы можете проверить это в postman). Но мы также хотим избавиться от записи, которая есть в нашей базе данных Postgres.

Во-вторых, мы удаляем из нашей базы данных Postgres. Для этого замените код в блоке then на следующий запрос:

db.pool.connect((err, client) => {

  // delete query
  const deleteQuery = "DELETE FROM images WHERE cloudinary_id = $1";
  const deleteValue = [cloudinary_id];
Вход в полноэкранный режим Выход из полноэкранного режима

})
Выполните запрос со следующим кодом под ним:

// выполнить запрос на удаление
client.query(deleteQuery, deleteValue)
.then((deleteResult) => {
response.status(200).send({
сообщение: «Изображение удалено успешно!»,
deleteResult
});
}).catch((e) => {
response.status(500).send({
сообщение: «Image Couldn’t be Deleted!»,
e
});
});

Таким образом, наша конечная точка должна выглядеть следующим образом:

// удалить изображение
app.delete(«/delete-image/:cloudinary_id», (request, response) => {
// уникальный ID
const { cloudinary_id } = request.params;

// сначала удалите изображение из cloudinary
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,
  });
});
Вход в полноэкранный режим Выход из полноэкранного режима

});

Пришло время испытать нашу конечную точку на практике.

Ниже показана моя медиатека Cloudinary с двумя изображениями, которые я уже загрузил. Обратите внимание на их уникальный идентификатор (public_id).

Если у вас его еще нет, воспользуйтесь конечной точкой persist-image, чтобы загрузить несколько изображений.

Теперь перейдем к postman:

Обратите внимание на уникальный ID, поскольку он совпадает с одним из изображений в моей медиатеке Cloudinary.

Из результатов видно, что мы выполнили команду DELETE, которая удалила одну строку из таблицы изображений в нашей базе данных.

Теперь это моя медиатека с одним из оставшихся изображений:

Валахххх… Теперь мы можем избавиться от изображения.

Сделайте перерыв, если он вам нужен. ✌🏾

Если вы готовы, я готов обновить изображения.

API обновления изображений
Ниже API delete-image, давайте начнем создавать API update-image со следующим кодом:

// обновить изображение
app.put(«/update-image/:cloudinary_id», (request, response) => {

});

Все коды будут находиться здесь.

Соберите уникальный идентификатор Cloudinary ID и данные о новом изображении у пользователя с помощью следующего кода:

// уникальный ID
const { cloudinary_id } = request.params;

// собранное изображение от пользователя
const data = {
заголовок: request.body.title,
изображение: request.body.image,
};

Удалите изображение из Cloudinary с помощью следующего кода:

// сначала удалите изображение из cloudinary
cloudinary.uploader
.destroy(cloudinary_id)
// загружаем изображение сюда
.then()
.catch((error) => {
response.status(500).send({
сообщение: «failed»,
ошибка,
});
});

Далее загрузите еще одно изображение в Cloudinary. Для этого введите следующий код в блок then:

() => {
cloudinary.uploader
.upload(data.image)
.then()
.catch((err) => {
response.status(500).send({
сообщение: «failed»,
err,
});
});
}

Теперь давайте заменим нашу исходную запись новыми данными об изображении. Замените содержимое блока 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,
        ];
      });
    }
Вход в полноэкранный режим Выход из полноэкранного режима

Мы выполняем запрос с помощью следующего кода, расположенного непосредственно под объявлением запроса:

// выполнить запрос
клиент
.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,
            });
          });
Вход в полноэкранный режим Выход из полноэкранного режима

На данный момент вот что у меня есть:

// обновить изображение
app.put(«/update-image/:cloudinary_id», (request, response) => {
// уникальный ID
const { cloudinary_id } = request.params;

// собранное изображение от пользователя
const data = {
заголовок: request.body.title,
изображение: 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,
    });
  });
Вход в полноэкранный режим Выход из полноэкранного режима

});

Настало время тестирования!

Это мой почтальон на изображении ниже:

Обратите внимание на уникальный идентификатор cloudinary ID, который соответствует изображению, оставленному в моей медиабиблиотеке Cloudinary.

Теперь посмотрите на мою медиатеку Cloudinary на следующем изображении:

Обратите внимание на новое изображение, заменившее исходное в моей медиатеке выше.

Также обратите внимание, что уникальный идентификатор Cloudinary ID совпадает с уникальным идентификатором в моей базе данных с новым названием. Смотрите изображение ниже:

Ура! Вы сделали все просто потрясающе! 💪

Мы только что завершили создание приложения для управления изображениями с помощью Node.js, Cloudinary и Postgres.

Оптимизация кода с помощью Express Routing
Express Routing позволяет нам оптимизировать код Node.js или придать ему более модульную структуру, отделив бизнес-логику от контроллеров. Мы хотим использовать это для очистки нашего кода.

Начнем с создания новой папки с именем routes в корневом каталоге:

mk dir routes

В папке routes создайте файл с именем routes.js.

Для Windows:

echo . > routes.js

Для Mac:

touch routes.js

Опустошите файл routes.js, если там что-то есть, и введите следующий код:

const express = require(‘express’);

const router = express.Router();

module.exports = router;

Добавьте следующий код над последней строкой:

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

// конфигурация cloudinary
cloudinary.config({
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.API_KEY,
api_secret: process.env.API_SECRET,
});

Вернитесь в файл App.js и удалите следующий код:

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

// конфигурация cloudinary
cloudinary.config({
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.API_KEY,
api_secret: process.env.API_SECRET,
});

Перенесите все API в routes.js.

Осторожно замените все случаи использования app на router.

Теперь мой файл routes.js выглядит следующим образом.

Вернитесь в файл app.js, импортируйте файл routes.js следующим образом:

// импортируем файл routes
const routes = require(«./routes/routes»)

Теперь зарегистрируйте маршруты следующим образом:

// зарегистрируйте маршруты
app.use(‘/’, routes);

Это мой файл app.js на данный момент:

const express = require(«express»);
const app = express();

// импортируем файл маршрутов
const routes = require(«./routes/routes»)

// конфигурация парсера тел
const bodyParser = require(«body-parser»);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// регистрируем маршруты
app.use(‘/’, routes);

module.exports = app;

Пришло время протестировать и проверить, работают ли наши маршруты как раньше.

Убедитесь, что ваши маршруты работают так же, как и мои, приведенные ниже:

persist-image

update-image

delete-image

Вот это да! Мы смогли отделить наши маршруты от нашего файла app.js.

Код для этого находится здесь.

Несмотря на то, что наш файл routes.js все еще длинный, у нас есть хорошая основа для отделения бизнес-логики от контроллеров. Пришло время сделать именно это.

Как переместить каждую конечную точку в отдельный файл
Начните с создания новой папки в папке routes и назовите ее controllers.

В папке controllers создайте 5 файлов и назовите их в честь 5 конечных точек.

Наша папка и файлы должны быть структурированы следующим образом:

Вернувшись в файл routes.js, давайте поработаем над API image-upload. Вырежьте следующий код:

(request, response) => {
// собираем изображение от пользователя
const data = {
image: request.body.image,
};

// загружаем изображение сюда
cloudinary.uploader
.upload(data.image)
.then((result) => {
response.status(200).send({
сообщение: «success»,
результат,
});
})
.catch((error) => {
response.status(500).send({
сообщение: «failure»,
ошибка,
});
});
}

В файле imageUpload приравняйте код, который вы уже вырезали из конечной точки image-upload, к exports.imageUpload следующим образом:

exports.imageUpload = (request, response) => {
// собранное изображение от пользователя
const data = {
изображение: 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,
    });
  });
Вход в полноэкранный режим Выход из полноэкранного режима

}

Теперь давайте импортируем то, что необходимо для работы этого кода. Итак, это мой файл imageUpload в данный момент:

const cloudinary = require(«cloudinary»).v2;
require(«dotenv»).config();

// конфигурация cloudinary
cloudinary.config({
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.API_KEY,
api_secret: process.env.API_SECRET,
});

exports.imageUpload = (request, response) => {
// собранное изображение от пользователя
const data = {
изображение: 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,
    });
  });
Вход в полноэкранный режим Выход из полноэкранного режима

}

Импортируем и зарегистрируем API imageUpload в файле routes.js следующим образом:

const imageUpload = require(«./controllers/imageUpload»);

// API загрузки изображений
router.post(«image-upload», imageUpload.imageUpload);

Теперь у нас есть эта строка кода, указывающая на API imageUpload в файле imageUpload.js из файла routes.js.

Как здорово! Наш код стал более читабельным.

Обязательно протестируйте API, чтобы убедиться, что он работает правильно. Мой работает отлично. Смотрите изображение ниже:

Теперь ваша очередь!

Примените то, чему вы научились, к другим API. Давайте посмотрим, что у вас получилось.

Я буду ждать на другой стороне…

Если вы здесь, то я верю, что вы уже сделали свои API и они работают отлично — или, по крайней мере, вы уже постарались. Кудос!

Посмотрите на мои здесь.

Поздравляю. Вы великолепны 🙂

Код оптимизации кода находится здесь.

Итак, переходим к следующему шагу.

Как развернуть приложение на GitHub и Heroku
Теперь, когда мы завершили наше приложение, давайте развернем его на Heroku, чтобы мы могли получить доступ к нему, даже не находясь на нашем ноутбуке, на котором был написан код.

Я проведу вас через загрузку нашего приложения на GitHub и его развертывание на Heroku.

Без лишних слов, давайте испачкаем руки.

Как загрузить код на GitHub
Загрузить или выложить код на GitHub так же просто, как съесть любимое блюдо. Ознакомьтесь с этим ресурсом, чтобы узнать, как перенести свой проект с локальной машины на GitHub.

Как развернуть проект на Heroku
Давайте начнем с создания учетной записи на Heroku.

Если вы уже создали аккаунт, вам, возможно, будет предложено создать приложение (то есть папку, в которой будет размещено ваше приложение). Вы можете сделать это, но я буду делать это с помощью терминала, поскольку терминал имеет несколько дополнительных функций, которые нам понадобятся позже.

Откройте ваш проект в терминале, если вы еще этого не сделали. Я буду использовать встроенный терминал VS Code.

Установите Heroku CLI:

npm install heroku

Войдите в Heroku CLI. Откроется окно браузера, которое можно использовать для входа в систему.

heroku login

Создайте приложение. Оно может иметь любое название. Я использую node-postgres-cloudinary.

heroku create node-postgres-cloudinary

Перейдите на приборную панель Heroku и найдите только что созданное приложение.

Ваалаах!

Вот как выглядит мое приложение на изображении выше. У меня там уже есть несколько приложений, но вы можете видеть то, которое я только что создал.

Теперь давайте добавим базу данных PostgreSQL в приложение.

Как добавить Heroku Postgres
Нажмите на приложение, которое вы только что создали. Вы попадете на приборную панель приложения.

Нажмите на вкладку/меню Ресурсы

В разделе Дополнения найдите и выберите Heroku Postgres.

Убедитесь, что вы выбрали тарифный план Hobby Dev — Free в следующем всплывающем окне:

Нажмите на кнопку обеспечения, чтобы добавить его в приложение, как показано ниже:

Нажмите на Heroku Postgres, чтобы перейти на приборную панель Heroku Postgres.

Перейдите на вкладку настроек:

Нажмите на View Credentials:

В Credentials нас интересует Heroku CLI. Мы будем использовать его в ближайшее время.

Вернемся к терминалу.

Давайте подтвердим, что Heroku Postgres был успешно добавлен. Введите в терминале следующее:

heroku addons

Ураааааа! Он был успешно добавлен.

Прежде чем мы продолжим, убедитесь, что путь к PostgreSQL установлен правильно, если вы работаете под Windows. Перейдите по этой ссылке, чтобы узнать, как установить путь. Путь должен быть следующим: C:Program FilesPostgreSQL<VERSION>bin.

Версия будет зависеть от версии, установленной на вашей машине. Моя версия: C:Program FilesPostgreSQL12bin, так как я использую версию 12.

Следующее изображение может быть полезным:

Возможно, вам придется перейти в папку, где установлен PostgreSQL на вашей машине, чтобы узнать свой собственный путь.

Войдите в Heroku Postgres с помощью Heroku CLI, используя наши учетные данные Heroku Postgres. Это мои — ваши будут другими:

heroku pg:psql postgresql-slippery-19135 —app node-postgres-cloudinary
Если вы получили ошибку, скорее всего, путь задан неправильно.

Как подготовить наше подключение к базе данных, чтобы оно совпадало с подключением Heroku
На данный момент моя база данных выглядит следующим образом:

const pg = require(«pg»);

const config = {
пользователь: «tutorial»,
база данных: «tutorial»,
пароль: «tutorial»,
порт: 5432,
max: 10, // максимальное количество клиентов в пуле
idleTimeoutMillis: 30000,
};

const pool = new pg.Pool(config);

pool.on(«connect», () => {
console.log(«подключился к Базе Данных»);
});

Если вы попытаетесь подключить Heroku к этой базе данных, вы получите ошибку. Это происходит потому, что в Heroku уже настроена строка подключения. Поэтому мы должны настроить наше соединение так, чтобы Heroku мог легко подключиться.

Для этого я собираюсь изменить файл подключения к базе данных (dbConnect.js) и файл .env.

dbConnect.js

const pg = require(‘pg’);
require(‘dotenv’).config();

// устанавливаем производственную переменную. Она будет вызываться при развертывании на живом хосте
const isProduction = process.env.NODE_ENV === ‘production’;

// детали конфигурации
const connectionString = postgresql://${process.env.DB_USER}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_DATABASE};

// если проект был развернут, подключаемся с помощью DATABASE_URL хоста
// в противном случае подключаемся с локальным DATABASE_URL
const pool = new pg.Pool({
connectionString: isProduction ? process.env.DATABASE_URL : connectionString,
ssl: isProduction,
});

// выводим сообщение об успехе в случае успеха
pool.on(‘connect’, () => {
console.log(‘База данных Teamwork подключена успешно!’);
});

.env файл

DB_USER=»tutorial»
DB_PASSWORD=»tutorial»
DB_HOST=»localhost»
DB_PORT=»5432″
DB_DATABASE=»tutorial»

После настройки dbconnect и файла .env мы готовы экспортировать нашу базу данных и таблицы с локальной машины в heroku postgres.

Как экспортировать базу данных и таблицы
Зайдите в pgAdmin и найдите базу данных для этого учебника. Моя база данных — tutorial.

Щелкните на ней правой кнопкой мыши и выберите Резервное копирование. Откроется новое окно.

Введите имя для SQL-файла, как это сделал я. Выберите простой формат. Затем нажмите кнопку Резервное копирование. Это сохранит файл в папке документов.

Найдите файл и переместите его в каталог проекта. Он может находиться в любом месте каталога, но я решил переместить его в каталог служб, поскольку там хранятся файлы, связанные с базой данных.

Вернитесь в терминал, перейдите в папку с файлом SQL и выполните следующий код, чтобы добавить таблицы, которые мы только что экспортировали, в базу данных heroku postgres:

cat |
Вот как выглядит моя база данных:

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

Вы заметили, что я изменил каталог на services (cd services)? Именно там находится мой sql-файл.

Вот это да! Мы только что успешно экспортировали нашу базу данных и таблицы в Heroku.

Все почти закончилось…

Как сообщить GitHub, что мы внесли изменения
Добавьте файлы, в которые мы внесли изменения:

$ git add .
Точка (.) добавляет все файлы.

Зафиксируйте свои последние изменения:

$ git commit -m «refactored the dbConnect and .env file to fit in to heroku; Added the database SQL file».
Перенесите зафиксированные файлы:

$ git push origin -u master

Наконец, развертывание нашего приложения
Перейдите на приборную панель вашего приложения:

Найдите и выберите репозиторий и нажмите на кнопку подключения:

Выберите ветку, которую вы хотите развернуть (в моем случае это мастер-ветка):

Включите автоматическое развертывание, нажав на кнопку Enable automatic deployment, как на изображении выше.

Нажмите на кнопку Deploy в ручном развертывании.

Нам не придется делать все это для последующих развертываний.

Теперь у вас есть кнопка, предлагающая «просмотреть сайт» после завершения сборки. Нажмите на нее. Это откроет ваше приложение в новой вкладке

О нет! Ошибка? Ошибка приложения???

Не волнуйтесь, это всего лишь небольшая проблема. То, что вы никогда не должны забывать делать при развертывании. Большинство хостинг-сервисов требуют этого.

Как исправить ошибку приложения Heroku
Вернитесь в корневой каталог вашего проекта.

Создайте файл и назовите его Procfile (он не имеет расширения).

В файле введите следующий код:

web: node index.js
Вход в полноэкранный режим Выйти из полноэкранного режима

Это направляет Heroku к файлу сервера (index.js), который является точкой входа приложения. Если ваш сервер находится в другом файле, внесите необходимые изменения.

Сохраните файл и отправьте новые изменения на GitHub.

Подождите от 2 до 5 минут, пока Heroku автоматически обнаружит изменения в вашем репозитории GitHub и отобразит их в приложении.

Теперь вы можете обновить страницу ошибки и увидеть, что ваша тяжелая работа окупилась:

Вы также можете протестировать маршрут получения изображения и убедиться, что он работает.

Поздравляем! Вы достигли больших успехов.

Другие маршруты (persist-image, update-image и delete-image) не будут работать, потому что мы не установили или не добавили дополнение cloudinary. Это так же просто, как и в случае с PostgreSQL, который мы только что сделали. Так что вы можете попробовать.

Заключение
Мы начали это руководство с целью узнать, как создать бэкенд-приложение с использованием Express, Postgres, Cloudinary, Github и Heroku.

Мы узнали, как хранить, извлекать, удалять и обновлять записи изображений. Затем мы организовали наш код с помощью Express Routing, разместили его на GitHub и развернули на Heroku. Это было очень много.

Надеюсь, вы согласитесь, что оно того стоило, потому что мы многому научились. Вам стоит попробовать самостоятельно добавить дополнение Cloudinary, чтобы еще больше отточить свои знания.

Спасибо за чтение!

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *