【无需信用卡】使用最强组合免费创建、部署Web应用【Vercel + Heroku + MongoDB Atlas】

我认为在2021年10月时点,可在无需信用卡的服务中进行最佳组合,在构建Web应用程序的开发环境和发布之间进行。

使用主机服务

Vercel (微賽尔)

    • ReactのフレームワークNext.jsで作ったアプリをデプロイできる。

 

    • 日本のCDNを利用できるのでNetlifyに比べて応答が早いらしい。

 

    無料のHobbyプランは個人利用で非営利目的のみ(広告はOKらしい)。

它是一种云平台,用于托管、构建和部署应用程序。

    • 今回はRuby on Railsで作ったアプリをデプロイしてAPIとして利用する。

 

    • アプリを稼働できるのは1つのアカウントにつき550時間/月。

 

    PostgreSQLのアドオンはレコード数10000行の制限がある。

MongoDB Atlas => MongoDB Atlas 是一种数据库服务。

    無料プランでは容量512MBまで利用できる。

开发环境操作系统:Windows 10 20H2

由于Windows操作系统上Ruby on Rails的稳定性不佳,所以我们将使用WSL2功能在Windows上运行Linux进行开发。

建立开发环境

image.png

用Ruby on Rails制作应用程序本次我们将创建一个简单的应用程序来管理书籍数据。
首先创建一个工作目录,然后将当前目录切换到创建的目录中。

mkdir -p ~/projects/book-manager
cd ~/projects/book-manager

创建一个Rails项目,然后切换到创建的目录里。

rails new rails-api --api --skip-bundle --skip-active-record --skip-test --skip-system-test
cd rails-api

我要启动在Windows上安装的Visual Studio Code。

image.png在Visual Studio Code中安装以下扩展功能。

    • Japanese Language Pack for Visual Studio Code

 

    Remote – WSL

关闭 Visual Studio Code。

在Ubuntu上输入以下命令,使用Visual Studio Code打开当前目录。

code .

确保在Visual Studio Code窗口的左下方显示“WSL: Ubuntu-20.04”。

image.png推荐您追加安装以下扩展功能。

    • Ruby

 

    • VSCode Ruby

 

    • Rails

 

    • Ruby on Rails

 

    • endwise

 

    • Rinbow End

 

    Material Icon Theme

打开 Gemfile 文件,然后添加 gem ‘webpacker’,版本约为 5.x。

在Ubuntu上执行以下命令,安装Gem库。

bundle install

进行WebPacker的初始设置。

rails webpacker:install

开启Rails服务器。

rails s

使用浏览器访问 http://localhost:3000,确认Rails的页面显示出来。

image.png请在已经启动Rails服务器的Ubuntu控制台外再打开一个Ubuntu控制台。

安装 MongoDB我要在Ubuntu上安装MongoDB。

wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list
sudo apt-get update
sudo apt-get install -y mongodb-org

增加一个作为服务启动 MongoDB 的 init 脚本。

    1. 下载 MongoDB 的 init.d 脚本。curl https://raw.githubusercontent.com/mongodb/mongo/master/debian/init.d | sudo tee /etc/init.d/mongodb >/dev/null

分配可执行文件的访问权限。sudo chmod +x /etc/init.d/mongodb

启动 MongoDB 服务。sudo service mongodb start

创建MongoDB数据库
我将登录MongoDB账户。

mongo

我們將分別建立開發和測試用的數據庫和集合。

use book_manager_development;
db.createCollection('books');
use book_manager_test;
db.createCollection('books');

按下 Ctrl + C 以退出登录。

在Rails中安装用于使用MongoDB的库。
在Gemfile中添加mongoid的描述。

gem 'mongoid', '~> 7.0.5'

在Ubuntu上执行以下命令,安装Gem库。

cd ~/projects/book-manager/rails-api
bundle install

创建Mongoid的配置文件。

rails g mongoid:config

在mongoid的配置文件中,我们需要记述连接的数据库名称等信息。
我们将编辑config/mongoid.yml文件如下所示。

development:
  clients:
    default:
      database: book_manager_development
      hosts:
        - localhost:27017
      options:
        server_selection_timeout: 1
test:
  clients:
    default:
      database: book_manager_test
      hosts:
        - localhost:27017
      options:
        read:
          mode: :primary
        max_pool_size: 1

在Ubuntu上运行以下命令,创建Book模型和相关文件。

rails g scaffold Book title:string price:integer

Next.js应用程序开发在Ubuntu上执行以下命令,创建Next.js项目。

cd ~/projects/book-manager
npx create-next-app@latest –ts

当被问到项目名称时,请输入nextjs。

进入创建的目录,用Visual Studio Code打开当前目录。

cd nextjs
code .

为了避免与Rails应用程序的端口冲突,需要修改package.json文件中的”scripts”中的”dev”代码,并指定除了3000之外的其他端口。

  "scripts": {
    "dev": "next dev -p 3001",

在Ubuntu上执行以下命令,启动Next.js应用程序。

yarn dev

请在浏览器中访问http://localhost:3001,确认Next.js页面已显示。

image.png
建议在启动Next.js服务器的Ubuntu控制台之外,再打开一个Ubuntu控制台。

安装用于API通信的axios库。

Paraphrase:
安装 axios 库来进行 API 通信。

cd ~/projects/book-manager/nextjs
npm i axios

在nextjs目录中创建名为book.d.ts的类型定义文件。
_id是在MongoDB中创建数据时自动附加的ID。

export type Book = {
    title:string;
    price:number;
    _id:{
        $oid: string;
    };
};

在nextjs的目录中,创建一个名为http-common.ts的文件,用于编写API通信的共同处理。

import axios from "axios";

export default axios.create({
    baseURL:"http://localhost:3000",
    headers:{
        "Content-type":"application/json",
    },
});

在nextjs目录中创建一个名为Book.service.ts的文件,用于编写API通信处理。该文件对应于rails-api/app/controllers/books_controller.rb中的方法。

import http from "./http-common"
import { Book } from  "./Book";

class BookService {
    index() {
        return http.get<Book[]>("/books");
    }
    create(book: Book) {
        return http.post("/books", book);
    }
    update(book: Book) {
        return http.put("/books/" + book._id.$oid, book);
    }
    delete(id: string) {
        return http.delete("/books/" + id);
    }
}

export default new BookService();

将nextjs/pages/index.tsx文件按照如下方式进行编辑。

import { useEffect, useState } from 'react';
import type { NextPage } from 'next';
import { Book } from '../Book';
import BookService from '../Book.service';

const Home: NextPage = () => {
  const [books, setBooks] = useState<Book[]>([]);
  const [titleFormValue, setTitleFormValue] = useState("");
  const [priceFormValue, setPriceFormValue] = useState(0);

  useEffect(() => {
    getIndex();
  }, []);

  const getIndex = () => {
    BookService.index().then((res) => {
      if(res.status !== 200) return;
      setBooks(res.data);
    }).catch((reason) => {
      console.error(reason);
    });
  };

  const onCreateButtonClick = () => {
    BookService.create({title:titleFormValue, price:priceFormValue, _id:{$oid:""}}).then((res) => {
      if(res.status !== 201) return;
      setTitleFormValue("");
      setPriceFormValue(0);
      getIndex();
    }).catch((reason) => {
      console.error(reason);
    });
  };

  const onDeleteButtonClick = (id: string) => {
    BookService.delete(id).then((res) => {
      if(res.status !== 204) return;
      getIndex();
    }).catch((reason) => {
      console.error(reason);
    });
  };

  return (
    <>
    <h2>新しい本を登録</h2>
    タイトル
    <input type="text" value={titleFormValue} onChange={(e) => {setTitleFormValue(e.target.value)}} />
    価格
    <input type="number" value={priceFormValue} onChange={(e) => {setPriceFormValue(e.target.valueAsNumber)}}/>
    <button onClick={onCreateButtonClick}>登録</button>
    <h2>本リスト</h2>
    <table>
      <thead>
        <tr>
          <td>ID</td>
          <td>タイトル</td>
          <td>価格</td>
          <td>操作</td>
        </tr>
      </thead>
      <tbody>
        {books.map((book) => {
          return(
            <tr key={book._id.$oid}>
              <td>{book._id.$oid}</td>
              <td>{book.title}</td>
              <td>{book.price}</td>
              <td><button onClick={() => {onDeleteButtonClick(book._id.$oid)}}>削除</button></td>
            </tr>
          );
        })}
      </tbody>
    </table>
    </>
  );
};

export default Home;

CORS设置
取消rails-api/Gemfile中gem ‘rack-cors’的注释。

执行bundle install。

将rails-api/config/initializers/cors.rb进行如下编辑。

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'localhost:3001'

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end

重新启动Rails服务器。

使用浏览器访问 http://localhost:3001,并确认能够进行书籍的注册、显示和删除功能。

image.png

代码修正在部署之前
我们将按如下方式编辑 nextjs/http-commons.ts 文件。
在生产环境中,我们将发送请求到部署在 Heroku 上的 Rails 应用的 URL。

import axios from "axios";

export default axios.create({
    baseURL:
    process.env.NODE_ENV === "production"
    ? process.env.NEXT_PUBLIC_RAILS_API_URL
    : "http://localhost:3000",
    headers: {
        "Content-type":"application/json",
    },
});

在rails-api/config/mongoid.yml文件中添加以下代码。
指定生产环境下连接MongoDB Atlas所需的字符串。

production:
  clients:
    default:
      uri: <%= ENV['MONGODB_URL'] %>
      options:
        server_selection_timeout: 5

请按照以下方式编辑rails-api/config/initializers/cors.rb文件,以允许来自已在Vercel上部署的Next.js应用程序的请求在生产环境中被许可。

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'localhost:3001', ENV['FRONTEND_URL'] || ''

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end

部署

MongoDB Atlas: MongoDB Atlas我要创建一个MongoDB Atlas账户。

登录后,在Atlas中创建数据库。
单击“建立数据库”。

image.png选择免费的Shared计划。

image.png选择提供商和地区。
我认为选择东京地区更好,但是Heroku的免费计划只能选择美国或欧洲的地区,所以我觉得选择us-east-1地区更好。

image.png几分钟后即可开始使用。

image.png点击「连接」,设置可连接的IP地址、用户名和密码。

image.png在“选择连接方法”中,选择“使用MongoDB Shell进行连接”。
在“选择您的mongo shell版本”中,选择4.4(最好是已安装在Ubuntu上的MongoDB版本)。

image.png
在Ubuntu上运行给定的字符串。

mongo "mongodb+srv://cluster0.uhkba.mongodb.net/myFirstDatabase" --username username

如果成功登录,我们将创建一个数据库和一个集合。

use book_manager_db;
db.createCollection('books');

在MongoDB Atlas页面上再次点击“Connect”按钮,然后在选择连接方式时选择“Connect your application”。
选择DRIVER为“Ruby”,选择VERSION为“2.5或更高版本”,并记下显示的连接字符串。

image.png

将应用程序部署至Heroku我将创建一个Heroku账号。

完成登录后,点击“创建新应用”。

image.png输入应用程序名称并点击“创建应用程序”。

image.png在Ubuntu上安装Heroku CLI,如果你能够创建一个应用程序,请执行以下命令。

curl https://cli-assets.heroku.com/install-ubuntu.sh | sh

设置git的用户名和电子邮件地址。

git config --global user.name "設定したい名前"
git config --global user.email "設定したいメールアドレス"

在Rails项目中进行Git提交。

cd ~/projects/book-manager/rails-api
git add .
git commit -m "Initial commit"

登录Heroku并推送。

heroku login
heroku git:remote -a アプリ名
git push heroku master

将项目部署至 Vercel。创建一个Vercel账号。

在Ubuntu上执行以下命令,安装Vercel CLI并进行部署。

cd ~/projects/book-manager/nextjs
sudo npm i -g vercel
vercel

设置环境变量

在Vercel中设置环境变量打开 Vercel 的控制台,选择刚刚部署的项目。

打开设置>环境变量。

image.png设定以下环境变量。

環境変数名(キー)説明NEXT_PUBLIC_RAILS_API_URLHerokuにデプロイしたRailsアプリのURL为了使环境变量生效,请再次进行部署。

在Heroku上配置环境变量。在Heroku的仪表板上选择项目,然后在“设置 > Config Vars”中设置以下环境变量。

環境変数名(キー)説明MONGODB_URLMongoDB Atlasの接続用の文字列FRONTEND_URLVercelでデプロイしたアプリのドメイン

mongodb+srv://ユーザー名:パスワード@cluster0.uhkba.mongodb.net/book_manager_db?retryWrites=true&w=majority

为了使环境变量生效,请选择「重新启动所有 dynos」来重新启动应用程序。

image.png我们已经完成了部署!让我们确认一下在Vercel上部署的应用是否正常运行。

确保Heroku上运行的应用不会进入睡眠模式的方法。在Heroku的免费计划中,如果应用程序超过30分钟没有访问,它就会进入睡眠状态,直到下次被访问时应用程序才会启动,导致响应变慢。
作为解决方法,可以利用诸如cron-job.org等的服务,定期访问应用程序的方式。
但是,如果没有进行信用卡认证,每月只能运行550小时(进行认证后为1000小时)。建议在用户较少的时间段,如凌晨至早晨,不要自动访问,以节省运行时间。

请看以下参考文章。

这个应用程序的源代码是这次创建的。我已经将它上传到GitLab上。

用中文本地化的一种选择是:Ruby on Rails 应用程序。

下一个.js应用程序

bannerAds