如何自动部署FeathersJS应用程序到Heroku

132 阅读9分钟

本教程包括:

  1. 创建一个FeathersJS API
  2. 在Heroku上创建和设置一个项目
  3. 自动部署到Heroku

自动化不仅仅是构建解决方案,以取代复杂或耗时的手动流程。正如流行的说法,"任何可以自动化的东西都应该被自动化"。例如,向应用程序部署更新可以而且应该自动化。在本教程中,我将向你展示如何设置FeathersJS应用程序的免提部署到Heroku。我将指导你实现一个持续部署(CD)管道,以便在变化被推送后立即发布应用程序的更新。我们将使用的示例应用程序是一个管理测验问题的API。它提供了创建、读取、更新和删除测验题的端点。

前提条件

在你开始之前,确保这些项目已经安装在你的系统上。

  • 至少10.0.0的NodeJS版本
  • 一个最新的JavaScript包管理器,如NPMYarn
  • Feathers CLI。你可以通过运行这个命令来安装它。
npm install -g @feathersjs/cli

对于存储库管理和持续集成/持续部署,你需要。

我们的教程是不分平台的,但以CircleCI为例。如果你没有CircleCI账户,请**在这里注册一个免费账户。**

为什么是FeathersJS和Heroku?

FeathersJS是一个轻量级的网络框架,用于使用JavaScript或TypeScript创建实时应用程序和REST APIs。FeathersJS的工具集包括一个架构模式,可以轻松地创建可扩展的REST API和实时应用程序。你可以在几分钟内建立原型,在几天内建立生产就绪的应用程序。

Heroku是一个平台即服务(PaaS),使开发者能够完全在云中构建、运行和操作应用程序。通过处理维护基于云的应用程序的更多要求,Heroku允许开发人员专注于构建应用程序而不是维护服务器。

开始使用

为该项目创建一个新的文件夹。

mkdir feathers_heroku

cd feathers_heroku

接下来,使用Feathers CLIgenerate 命令生成一个新的应用程序。

feathers generate app

对于这个项目,我们将使用JavaScript。另外,我们只想做一个REST API。响应CLI的问题,如这里所列。

? Do you want to use JavaScript or TypeScript? JavaScript
? Project name feathers-heroku
? Description
? What folder should the source files live in? src
? Which package manager are you using (has to be installed globally)? npm
? What type of API are you making? REST
? Which testing framework do you prefer? Jest
? This app uses authentication No
? Which coding style do you want to use? ESLint

在你做其他事情之前,更新索引页,让访问者知道API正在建设中。在public/index.html ,在main 元素中添加一个 "正在建设 "的信息。

<main class="container">
  <!--  Image declaration-->
  <h1 class="center-text">This API is under construction</h1>
  <!--  Footer declaration-->
</main>

接下来,使用下面这个命令运行你的应用程序。

npm run dev

默认情况下,应用程序在3030端口运行。在你的浏览器中输入http://localhost:3030/ ,就可以去那里。

FeathersJS app homepage

配置Heroku

你的下一步是在Heroku上创建一个新的应用程序。你可以在Heroku仪表板上完成这项工作。点击新建,然后点击**新建应用程序。**如图所示,填写表格。如果你愿意,你可以为应用程序使用不同的名称和地区。

Create a Heroku app

单击 "创建应用程序"按钮。然后你将被转到新创建的应用程序的部署视图。

接下来,你需要添加一个构建包。单击 "设置"选项卡。在buildpacks部分,点击Add buildpack

Add a buildpack

在打开的表格中,你可以选择一个官方支持的构建包,或者为你的构建包提供一个 URL。选择nodejs来使用官方支持的Heroku nodejs构建包。点击保存更改。Node.js将被用于构建你的下一个部署。

你需要的最后一件事是一个API密钥。你将用它来连接你的CircleCI管道和Heroku。为了得到你的API密钥,打开账户设置页面,向下滚动到API密钥部分。

Heroku API key

点击 "揭示"按钮,复制显示的API密钥。

配置CircleCI

接下来,我们需要添加CircleCI的管道配置。对于这个项目,管道将由两个步骤组成。

  1. 建立:在这里,我们构建项目并安装项目的依赖性。理想情况下,我们应该在这个阶段运行项目测试。然而,为了保持本教程的合理长度,我们将跳过测试。
  2. 部署到Heroku:如果构建阶段成功完成,你可以将最新的变化部署到Heroku。

在你项目的根部,创建一个名为.circleci 的文件夹。在其中,创建一个名为config.yml 的文件。在这个新创建的文件中,添加这个配置。

# Use the latest 2.1 version of CircleCI pipeline process engine.
version: 2.1

orbs:
  heroku: circleci/heroku@1.2.6
  node: circleci/node@4.7.0

jobs:
  build:
    executor: node/default
    steps:
      - checkout
      - node/install-packages:
          cache-path: ~/project/node_modules
          override-ci-command: npm install

workflows:
  sample:
    jobs:
      - build
      - heroku/deploy-via-git:
          force: true # this parameter instructs the push to use a force flag when pushing to the heroku remote, see: https://devcenter.heroku.com/articles/git
          requires:
            - build

这个配置拉入了Heroku的球体circleci/heroku 。这个球体使你能够访问一组强大的Heroku工作和命令。其中一个作业,heroku/deploy-via-git ,将你的应用程序从GitHub repo直接部署到你的Heroku账户。该配置使用Node.js orbcircleci/node ,它允许你安装默认启用缓存的软件包。

在配置中还指定了一个名为build 的工作,检查最新的代码并安装package.json 文件中指定的包。

最后,有一个工作流程,运行build 工作,然后是heroku/deploy-via-git 工作。注意,有一个requires 选项,告诉CircleCI只有在构建工作完成后才运行deploy-via-git

接下来,在GitHub上建立一个仓库,并将该项目链接到CircleCI。

登录你的CircleCI账户。如果你用GitHub账户注册,你所有的仓库将显示在你的项目仪表板上。

在你的feathers_heroku 项目旁边,点击Set Up Project

CircleCI会检测项目中的config.yml 文件。点击使用现有的配置,然后开始构建。您的第一个工作流程将开始运行,然而,它将失败!这是预料之中的。这是预料之中的事。

Pipeline build fails

部署过程失败是因为你没有提供你的Heroku API密钥。现在就去解决这个问题吧。点击项目设置按钮,然后点击环境变量。添加两个新的变量,如下所示。

  • HEROKU_APP_NAME 变量是Heroku中的应用程序名称(deno-heroku-circleci )。
  • HEROKU_API_KEY 变量是你从账户设置页面获取的Heroku API密钥。

选择RerunWorkflow from Failed选项,重新运行Heroku部署。为了确认你的工作流是成功的,你可以在浏览器中打开你新部署的应用程序。你的应用程序的URL应该是这样的格式:https://<HEROKU_APP_NAME>.herokuapp.com/

迎接你的将是正在建设中的索引页面。

实施questions 服务

现在是时候在我们的API上添加处理问题的功能了。在本教程中,一个问题将有这些属性的字段。

  • 难度
  • 问题
  • 正确答案

在创建问题时,默认分配一个唯一的主键。

使用这个命令创建一个数据库支持的服务。

feathers generate service

对于这个服务,使用NeDB ,你可以直接按回车键来确认。使用Questions 作为服务名称,并以默认值确认所有其他提示。只需按下Enter键。

  ? What kind of service is it? NeDB
  ? What is the name of the service? Questions
  ? Which path should the service be registered on? /questions
  ? What is the database connection string? nedb://../data

通过这个命令,FeathersJS已经提供了你对问题进行CRUD操作所需要的一切。在这一点上,你有一个带有这些端点的API。

  • GET /questions 逐页列出所有问题。
  • POST /questions 创建一个新的问题。
  • GET questions/123 返回id为123的问题的细节。你也可以在这个请求中包括查询: 。questions/123?difficulty=medium
  • PATCH /questions/123 和 更新id为123的问题的细节。PUT /questions/123
  • DELETE /questions/123 删除ID为123的问题。

你可以为你的应用程序提供服务,并向任何一个端点发出请求。在本教程中,在使用你闪亮的新API之前,先对数据库进行播种并覆盖一些默认功能。

当你收到一个添加问题的请求时,你想在把它们保存到数据库之前,只从请求中检索出先前指定的三个值。通过重写create 服务方法来做到这一点。打开src/services/questions/questions.class.js ,并编辑它以匹配这段代码。

// src/services/questions/questions.class.js
const { Service } = require("feathers-nedb");

exports.Questions = class Questions extends Service {
  create(data, params) {
    const { question, correctAnswer, difficulty } = data;
    const quizData = { question, correctAnswer, difficulty };
    return super.create(quizData, params);
  }
};

接下来,创建一个播种机,它将给你一些样本问题。在src/services/questions 目录中,创建一个名为questions.seed.js 的新文件。将这段代码添加到新创建的文件中。

// src/services/questions/questions.seed.js
exports.seedQuestions = [
  {
    difficulty: "medium",
    question: "The HTML5 standard was published in 2014.",
    correctAnswer: "True",
  },
  {
    difficulty: "medium",
    question:
      "Which computer hardware device provides an interface for all other connected devices to communicate?",
    correctAnswer: "Motherboard",
  },
  {
    difficulty: "medium",
    question: "On which day did the World Wide Web go online?",
    correctAnswer: "December 20, 1990",
  },
  {
    difficulty: "medium",
    question: "What is the main CPU is the Sega Mega Drive / Sega Genesis?",
    correctAnswer: "Motorola 68000",
  },
  {
    difficulty: "medium",
    question: "Android versions are named in alphabetical order.",
    correctAnswer: "True",
  },
  {
    difficulty: "medium",
    question:
      "What was the first Android version specifically optimized for tablets?",
    correctAnswer: "Honeycomb",
  },
  {
    difficulty: "medium",
    question:
      "Which programming language shares its name with an island in Indonesia?",
    correctAnswer: "Java",
  },
  {
    difficulty: "medium",
    question: "What does RAID stand for?",
    correctAnswer: "Redundant Array of Independent Disks",
  },
  {
    difficulty: "medium",
    question:
      "Which of the following computer components can be built using only NAND gates?",
    correctAnswer: "ALU",
  },
  {
    difficulty: "medium",
    question:
      "What was the name of the security vulnerability found in Bash in 2014?",
    correctAnswer: "Shellshock",
  },
];

接下来,打开src/services/questions/questions.service.js ,编辑它以匹配这段代码。

// src/services/questions/questions.service.js
// Initializes the `Questions` service on path `/questions`
const { Questions } = require("./questions.class");
const createModel = require("../../models/questions.model");
const hooks = require("./questions.hooks");
const { seedQuestions } = require("./questions.seed");

module.exports = async function (app) {
  const options = {
    Model: createModel(app),
    paginate: app.get("paginate"),
  };

  // Initialize our service with any options it requires
  app.use("/questions", new Questions(options, app));

  // Get our initialized service so that we can register hooks
  const service = app.service("questions");

  //get the total number of questions in the database
  const { total: totalQuestions } = await service.find({
    query: {
      $limit: 0,
    },
  });

  //seed the database if there are no questions saved
  if (totalQuestions === 0) {
    await seedQuestions.forEach((question) => {
      service.create(question);
    });
  }

  service.hooks(hooks);
};

请注意,这段代码首先检查数据库中的问题数量,只有在没有问题时才进行播种。这可以避免每次启动本地服务器时都运行播种机。

使用Postman测试FeathersJS的API

现在你可以使用Postman来测试你的API

发送一个POST 请求到http://localhost:3030/questions

Create Questions

发送一个GET 请求到http://localhost:3030/questions ,以检索问题列表。

Get all Questions

很好!最后,用最新的代码更新git仓库。不要忘记删除索引页上的 "正在建设中 "的信息。

git add .

git commit -m "Implement CRUD functionality for questions"

git push origin main

你的CircleCI构建再次运行,完成后,你的Heroku应用将以最新代码更新。干得好!

总结

在本教程中,我向您展示了如何使用GitHub、CircleCI和Heroku为FeathersJS API建立一个CI/CD管道。