如何使用 PostgreSQL 部署 React 应用程序?
PostgreSQL 是一种先进的企业级关系数据库管理系统,有许多使用案例。它是目前第二大最流行的 SQL 数据库,仅次于 MySQL。
在本文中,我们将深入探讨 PostgreSQL 的精髓,探讨 SQL 数据库和 NoSQL 数据库之间的区别,并提供以 PostgreSQL 为后盾部署网络应用的分步指南。
Contents
什么是 PostgreSQL?
PostgreSQL 是一个免费的开源对象关系数据库,支持 SQL 和 JSON。
它最初于 1996 年发布,因此被认为是一个成熟、强大和安全的关系数据库管理系统(RDBMS)。
Postgres 可用作通用事务数据库、地理空间数据库、动态网络应用程序数据库、联盟数据库等。
与 MySQL 等其他流行数据库相比,它支持表继承、用户定义类型、异步复制和多版本并发控制(MVCC)。
它以性能、可扩展性、可扩展性、容错性和符合 ACID 标准而著称。
RDBMS 支持大多数主流操作系统,包括 Windows、Linux 和 macOS。
此外,它还支持大多数流行的编程语言,如 Java、Python、C、Go、Perl 和 JavaScript。
SQL 与 NoSQL 数据库
数据库可根据其数据结构分为两类:
- 关系数据库(SQL)
- 非关系型数据库(NoSQL)
关系数据库(SQL)
关系数据库使用 SQL 或结构化查询语言。SQL 是一种特定领域的语言,用于数据查询和操作。
该语言支持简单命令、事务和嵌入式程序,如存储函数或视图。
SQL 数据库基于预定义的模式。它们由带有一组列的表组成,每个列都有自己的数据类型。它们通常具有 ACID 特性:
- 原子性
- 一致性
- 隔离
- 耐用性
自 20 世纪 70 年代以来,SQL 数据库已得到广泛应用。
最流行的 SQL 数据库是 MySQL、PostgreSQL、SQLite 和 Oracle Database。
非关系型数据库(NoSQL)
非关系型数据库或非 SQL 数据库不遵循严格的模式。它们非常适合存储大量非结构化或动态数据,最常见的是 JSON 数据。
NoSQL 数据库有多种类型,包括
- 文件数据库
- 键值数据库
- 图形数据库
近年来,由于存在大量非结构化数据,NoSQL 数据库越来越受欢迎。
最常用的 NoSQL 数据库包括 Redis、Cassandra 和 AWS DynamoDB。
SQL 和 NoSQL 孰优孰劣?
SQL 和 NoSQL 数据库之间的选择取决于您的使用情况和数据。
如果您要处理大量非结构化数据,那么一定要使用 NoSQL。另一方面,如果您的数据主要是结构化数据,SQL 则是更好的选择。
您应该考虑的另外两个因素是性能和扩展。NoSQL 数据库往往比 SQL 数据库更快。SQL 数据库只能纵向扩展,而 NoSQL 数据库可以横向扩展。
最后,有些网络框架只支持 SQL 数据库,有些则只支持 NoSQL。
如何使用 PostgreSQL 部署 React 应用程序?
在本节文章中,您将学习如何将Postgres 支持的网络应用程序部署到Back4app。
先决条件
- 具有使用JavaScript ES6、React 和Next.js的经验
- 对Docker 以及 BaaS 和 CaaS 云模式有基本了解
- 在计算机上安装 JavaScript IDE 和Docker Desktop
- 免费的Back4app和GitHub账户
什么是 Back4app Stack?
在深入了解部署流程之前,让我们简要讨论一下 Back4app 提供的解决方案。
- Back4app (BaaS)是一个功能齐全的后端解决方案。它包括用户管理、身份验证、实时数据库(NoSQL 或 PostgreSQL)、自定义代码执行、自动生成 API、SDK、推送通知等。
- Back4app Containers (CaaS)是一个由 Docker 驱动的容器管理和部署平台。通过它,您只需点击几下即可启动 Docker 容器!
- Back4app AI-agent是一款全新的人工智能代理。它可以让您通过对话的方式执行所有与云有关的任务。该代理与 Back4app 的其他两个解决方案紧密集成。
在本文中,我们将使用 Back4app BaaS 和 Back4app Containers。不过,您应该查看《如何在网络开发中使用人工智能?
项目概述
我们将建立一个简单的预算跟踪网络应用程序。该网络应用程序将允许用户添加、删除支出,并计算不同的统计数据(如支出金额、预算百分比)。
应用程序将分为后台和前台。后端将使用 Back4app(由 PostgreSQL 支持)构建,前端将使用 React(使用 Next.js)构建。
我们将使用Parse SDK将两者连接起来,并将前端部署到 Back4app Containers 中。
后台
让我们从后台开始。
创建 Back4app 应用程序
要创建 Back4app 应用程序,请首先导航到Back4app 面板,然后点击 “创建新应用程序”。
接下来,选择 “后台即服务”,因为我们正在构建一个后台。
给应用程序起一个描述性的名称,选择 “PostgreSQL “作为数据库,然后点击 “创建”。
在撰写本文时,从开发人员的角度来看,这两种数据库类型并无太大区别。相同的 Parse SDK 方法适用于这两种数据库。
Back4app 需要一些时间来准备应用程序所需的一切。这包括数据库、应用层、自动扩展、自动备份和安全设置。
一旦您的应用程序准备就绪,您就会被重定向到应用程序的实时数据库视图。
数据库架构
下面,我们来设计数据库。
由于我们的应用程序相对简单,我们只需要一个类。就叫它 “支出
“吧。
要创建新的数据库类,请单击 “创建类”,将其命名为Expense
,并确保选中 “启用公共读写”。
启用公共读写被认为是不好的做法,因为它允许任何人对你的类执行 CRUD 操作。安全问题不在本文讨论范围之内。不过,回顾一下Parse 服务器的安全性也许是个好主意。
默认情况下,数据库类包含以下四个字段:
+-----------+------------------------------------------------------------------------+
| Name | Explanation |
+-----------+------------------------------------------------------------------------+
| objectId | Object's unique identifier |
+-----------+------------------------------------------------------------------------+
| updatedAt | Date time of the object's last update. |
+-----------+------------------------------------------------------------------------+
| createdAt | Date time of object's creation. |
+-----------+------------------------------------------------------------------------+
| ACLs | Allow you to control the access to the object (e.g. read, update). |
+-----------+------------------------------------------------------------------------+
我们将在构建前端时使用它们,因此请快速浏览一下它们。
接下来,在 “支出 "
类中添加以下字段:
+-----------+-------------+--------------------+----------+
| Data type | Name | Default value | Required |
+-----------+-------------+--------------------+----------+
| String | name | <leave blank> | yes |
+-----------+-------------+--------------------+----------+
| String | description | <leave blank> | no |
+-----------+-------------+--------------------+----------+
| Number | price | 0 | yes |
+-----------+-------------+--------------------+----------+
然后,用一些样本数据填充数据库。
提供名称、描述和价格,创建几个项目。您也可以导入这些数据。
测试数据稍后将允许我们测试后台和前台。
云代码
Back4app 允许您通过云代码函数执行自定义 JavaScript 代码。这些函数可以作为作业安排,也可以通过 Parse 或 HTTP 请求调用。
由于它们是在托管环境下运行,因此无需处理和扩展自己的服务器。
要了解有关功能即服务(FaaS)的更多信息,请查看什么是无服务器功能?
我们将利用云代码功能来计算支出统计数据。
要创建一个,请选择侧边栏上的 “Cloud Code > Functions & Web Hosting”。然后打开cloud/main.js,粘贴以下代码:
// cloud/main.js
const totalBudget = 100;
Parse.Cloud.define("getStatistics", async (request) => {
const query = new Parse.Query("Expense");
const totalExpenses = await query.count();
const results = await query.find();
const totalSpent = results.reduce(
(sum, expense) => sum + expense.get("price"), 0);
const spentPercentage = totalSpent > 0 ?
Math.round((totalSpent / totalBudget) * 100) : 0;
return {
totalExpenses,
totalSpent,
totalBudget,
spentPercentage
};
});
- 此代码定义了一个名为
getStatistics
的新云代码函数。 - 该函数汇总数据并计算
总
花费
和花费百分比
。
最后,点击 “部署”,将功能部署到云中。
后台工作就完成了。这很简单!
前端
在本节中,我们将实现应用程序的前端。
创建下一个应用程序
引导 Next.js 应用程序的最简单方法是使用create-next-app
工具。要使用它,请打开终端并运行以下命令:
$ npx create-next-app@latest back4app-postgres
√ Would you like to use TypeScript? ... No
√ Would you like to use ESLint? ... Yes
√ Would you like to use Tailwind CSS? ... Yes
√ Would you like to use `src/` directory? ... Yes
√ Would you like to use App Router? (recommended) ... Yes
√ Would you like to customize the default import alias (@)? ... No
Creating a new Next.js app in /back4app-postgres.
如果你从未使用过
create-next-app
工具,它会自动安装。
确保启用TailwindCSS,因为我们将使用它而不是组件库。
接下来,首先删除public/文件夹中的内容,清理已引导的项目。
只保留src/app/globals.css 的前三行:
/* app/src/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
并用以下代码替换app/src/globals.css:
// src/app/page.js
export default function Page() {
return (
<p>Back4app rocks!</p>
);
}
启动开发服务器
$ next dev
打开您最喜欢的网络浏览器并导航至http://localhost:3000/。如果一切顺利,您应该会看到 “Back4app rocks!
意见
前台将有以下端点
/
显示支出和支出统计表/add/
显示添加新支出的表格/delete/
/ 显示删除支出的确认信息
要实现这些端点,请创建以下目录结构:
src/
├── add/
│ └── page.js
└── delete/
└── [objectId]/
└── page.js
此外,创建包含Container.js和Header.js 的组件文件夹:
src/
└── components/
├── Container.js
└── Header.js
在Container.js 中粘贴以下内容:
// src/app/components/Container.js
const Container = ({children}) => {
return (
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
{children}
</div>
)
}
export default Container;
对Header.js 也是如此:
// src/app/components/Header.js
import Container from "@/app/components/Container";
import Link from "next/link";
const Header = () => {
return (
<Container>
<div className="py-4">
<Link href="/">
<div
className="text-2xl font-semibold text-indigo-500 hover:text-indigo-700"
>
back4app-postgres
</div>
</Link>
</div>
</Container>
)
}
export default Header;
像这样在layout. js中使用Container.js和Header.js:
// src/app/layout.js
"use client";
import {Inter} from "next/font/google";
import "./globals.css";
import Header from "@/app/components/Header";
import Container from "@/app/components/Container";
const inter = Inter({ subsets: ["latin"] });
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>
<Header/>
<Container>
{children}
</Container>
</body>
</html>
);
}
最后,将视图代码粘贴到相应的文件中:
重新运行开发服务器,并在浏览器中访问http://localhost:3000。您应该会看到类似下面的内容:
点击 “添加支出 “按钮后,您将跳转到支出添加表单。
Parse SDK
有多种方法可以连接到 Back4app 后台:
- RESTful API
- GraphQL API
- Parse SDK
我们选择后者,因为它是最强大、最简单的设置。
Parse SDK 是一个工具包,其中包含用于查询数据、管理数据、运行云代码功能等的便捷工具。
它适用于多种编程语言和框架,如 JavaScript、PHP、Flutter 和 Objective-C。
首先通过 npm 安装 Parse:
$ npm install parse
要在 React 视图中使用 Parse,我们首先要对其进行初始化。但在此之前,我们要创建一个 React 上下文,以便将 Parse 实例传递给所有视图。
在src/app文件夹中创建上下文文件夹,并在其中创建parseContext.js文件:
import {createContext} from "react";
const ParseContext = createContext();
export default ParseContext;
然后在layout.js中初始化 Parse,并用ParseContext.Provider
对整个应用程序进行如下封装:
// src/app/layout.js
import Parse from "parse/dist/parse";
import ParseContext from "@/app/context/parseContext";
Parse.initialize(
"<your_parse_application_id>",
"<your_parse_javascript_key>",
);
Parse.serverURL = "https://parseapi.back4app.com/";
export default function RootLayout({ children }) {
return (
<ParseContext.Provider value={Parse}>
<html lang="en">
// ...
</html>
</ParseContext.Provider>
);
}
确保更换 <your_parse_application_id>
和 <your_parse_javascript_key>
替换为您的实际密钥。要获取密钥,请进入 Back4app 面板,选择侧边栏上的 “应用程序设置 > 安全和密钥”。
现在我们可以像这样在视图中获取Parse
实例:
const parse = useContext(ParseContext);
然后,稍微修改一下视图,调用 Parse 方法。
src/app/page.js:
// src/app/page.js
export default function Page() {
// ...
const parse = useContext(ParseContext);
const fetchExpenses = () => {
const query = new parse.Query("Expense");
query.find().then((fetchedExpenses) => {
const expenses = fetchedExpenses.map(expense => ({
objectId: expense.id,
name: expense.get("name"),
description: expense.get("description"),
price: expense.get("price"),
createdAt: expense.get("createdAt"),
}));
setExpenses(expenses);
console.log("Expenses fetched successfully.");
}).catch((error) => {
console.error("Error while fetching expenses:", error);
});
}
const fetchStatistics = () => {
parse.Cloud.run("getStatistics").then((statistics) => {
setStatistics(statistics);
console.log("Statistics fetched successfully.");
}).catch((error) => {
console.error("Error while fetching statistics:", error);
});
}
// ...
}
src/app/add/page.js:
// src/app/add/page.js
export default function Page() {
// ...
const parse = useContext(ParseContext);
const onAddClick = () => {
const Expense = parse.Object.extend("Expense");
const expense = new Expense();
expense.set("name", name);
expense.set("description", description);
expense.set("price", parseFloat(price));
expense.save().then((expense) => {
console.log("Expense created successfully with objectId: ", expense.id);
router.push("/");
}, (error) => {
console.error("Error while creating expense: ", error);
}
);
}
const onCancelClick = () => {
router.push("/");
}
// ...
}
src/app/delete/[objectId]/page.js:
// src/app/delete/[objectId]/page.js
export default function Page() {
// ...
const parse = useContext(ParseContext);
const onDeleteClick = () => {
const Expense = parse.Object.extend("Expense");
const query = new parse.Query(Expense);
query.get(objectId).then((expense) => {
return expense.destroy();
}).then((response) => {
console.log("Expense deleted successfully");
router.push("/");
}).catch((error) => {
console.error("Error while deleting expense: ", error);
});
}
const onCancelClick = () => {
router.push("/");
}
// ...
}
不要忘记文件顶部的导入:
import {useContext} from "react";
import ParseContext from "@/app/context/parseContext";
很好,就是这样。
现在,您的前台已与后台连接。如果您在浏览器中访问应用程序,就会发现数据已从后台正确加载。前台的所有更改现在都会反映在后台。
Dockerize
由于 Back4app Containers 是 CaaS 平台,因此在部署前必须对项目进行 docker 化。建议通过 Dockerfile 对项目进行 docker 化。
Dockerfile 是一个蓝图脚本,提供创建容器映像的指令。
在项目根目录下创建Dockerfile:
# Dockerfile
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
RUN npm install -g next
EXPOSE 3000
CMD ["next", "start", "-p", "3000"]
该 Dockerfile 使用node:18-alpine
映像、建立工作目录、管理依赖关系、复制项目并构建应用程序。
完成后,它会暴露端口3000
并启动 Next.js 服务器监听该端口。
接下来,创建一个.dockerignore文件,以最小化镜像的大小:
# .dockerignore
.idea/
node_modules/
.next/
/out/
build/
.vercel
.dockerignore文件的工作方式与.gitignore文件类似。
在本地构建并运行镜像,确保一切正常:
$ docker build -t back4app-postgres:1.0 .
$ docker run -it -p 3000:3000 back4app-postgres:1.0
打开网络浏览器并导航至http://localhost:3000。网络应用程序应仍能正常运行。
推送到 VCS
要将代码部署到 Back4app Containers,必须将其推送到 GitHub。
- 登录GitHub 账户
- 创建一个新的资源库。
- 复制远程源 URL,例如
[email protected]:duplxey/repo.git。
- 启动 Git 仓库:
git
init - 添加远程:
git remote add origin
- 添加所有文件:
git add .
- 创建提交:
git commit -m "project init"
- 推送源代码:
git push origin main
打开您最喜欢的网络浏览器,确保所有代码都已添加到版本库中。
部署代码
现在,应用程序已经 docker 化并托管在 GitHub 上,我们终于可以部署它了。
导航至 Back4app 面板,再次点击 “构建新应用程序 “按钮。
选择 “容器即服务”,因为我们要部署的是 docker 化应用程序。
如果您是第一次使用 Back4app Containers,您必须将 GitHub 链接到 Back4app 账户。
在选择 Back4app 可以访问的资源库时,确保允许访问上一步创建的资源库。
接下来,”选择 “存储库。
Back4app Containers 允许您配置端口、自动部署、环境变量和健康检查等部署设置。
由于我们的应用程序非常简单,因此只需提供一个名称,其他一切均可保留为默认值。
点击 “创建应用程序 “后,Back4app 将从 GitHub 提取代码,构建 Docker 镜像,将其推送到容器注册表并进行部署。
片刻之后,您的应用程序就可以通过侧边栏上的 URL 访问了。
结论
在本文中,您将了解 PostgreSQL 是什么、SQL 和 NoSQL 数据库之间的区别,以及如何在 Back4app 上部署 Postgres 支持的网络应用程序。
为了检验你的理解能力,我建议你实施其中的一些想法:
- 用户认证
- 不要采用全局预算,而要以用户为基础
- 为 Back4app Containers 应用程序添加自定义域
从back4app-postgres软件仓库获取最终源代码。