Nhost:基于PostgreSQL与Hasura的现代BaaS平台实战指南
1. 项目概述:一个开箱即用的现代应用后端平台
如果你正在开发一个Web或移动应用,尤其是那种需要用户登录、文件上传、数据库操作和实时功能的现代应用,那么搭建和维护后端服务绝对是一个耗时且容易出错的过程。你需要考虑身份验证、数据库管理、API网关、文件存储、实时订阅等一系列复杂组件。今天要聊的这个项目——nhost/nhost,就是为了解决这个痛点而生的。它是一个开源的、基于云原生架构的后端即服务(BaaS)平台,或者更准确地说,是一个“后端即代码”的解决方案。它的核心目标是让开发者,无论是独立开发者还是小团队,都能在几分钟内获得一个功能齐全、可扩展、且完全由你掌控的生产级后端。
简单来说,Nhost 提供了一个预配置好的、容器化的后端环境,这个环境以 PostgreSQL 数据库为核心,并集成了几个非常强大的开源工具:Hasura 作为 GraphQL API 引擎,用于即时生成和管理你的数据 API;Auth 作为身份验证服务(基于 GoTrue),处理用户注册、登录、社交登录等;Storage 作为对象存储服务(基于 MinIO),让你轻松处理文件上传和下载;以及 Functions 用于运行无服务器的后端逻辑。所有这些服务都通过一个统一的控制台进行管理,并且可以通过 Git 进行版本控制和部署。这意味着,你不再需要从零开始搭建和连接这些分散的服务,Nhost 已经为你准备好了“全家桶”,并且这个“全家桶”的配方(配置)是完全透明和可定制的。
它特别适合需要快速启动项目的创业团队、希望将精力聚焦在前端和业务逻辑的全栈开发者,以及任何厌倦了重复搭建后端基础设施的工程师。通过使用 Nhost,你可以将项目初期的后端开发时间从几周缩短到几小时,并且获得一个架构清晰、易于扩展的基础。接下来,我将深入拆解它的核心设计、如何上手实操、以及在实际项目中可能会遇到的“坑”和应对技巧。
2. 核心架构与组件深度解析
2.1 以 PostgreSQL 和 Hasura 为核心的实时数据层
Nhost 的基石是一个托管的 PostgreSQL 数据库。选择 PostgreSQL 是经过深思熟虑的,它不仅是功能最强大的开源关系数据库之一,支持 JSONB、全文搜索、地理空间数据等高级特性,而且其稳定性和生态系统也经过了长期考验。Nhost 为你自动管理这个数据库的备份、升级和基础运维,让你省去 DBA 的烦恼。
但仅仅有数据库还不够,如何高效、安全地暴露数据 API 是关键。这就是 Hasura 登场的地方。Hasura 是一个能够将你的 PostgreSQL 数据库实时转换为 GraphQL API 的神奇引擎。你不需要编写任何解析器或类型定义,只要在数据库中创建好表结构,Hasura 就会自动为你生成对应的 GraphQL 查询(Query)、变更(Mutation)和订阅(Subscription)操作。例如,你创建了一个products表,几秒钟后,你就可以通过 GraphQL 查询所有产品、按条件过滤、或者订阅产品库存的实时变化。
注意:Hasura 的自动生成能力虽然强大,但在生产环境中,必须仔细配置角色权限(Role-Based Access Control, RBAC)。Nhost 通过其 Auth 服务与 Hasura 深度集成,使得基于 JWT(JSON Web Token)的权限控制变得相对简单,但这仍然是配置中最需要细心的一环。权限配置不当是数据泄露最常见的原因。
这种组合带来了两个巨大优势:开发速度的极致提升和实时能力的原生支持。你几乎不需要写后端代码就能完成大部分 CRUD(增删改查)和实时数据推送功能。对于构建仪表盘、协作应用、聊天功能等需要实时更新的场景,Hasura 的订阅功能是“开箱即用”的,大大简化了技术复杂度。
2.2 一体化的身份验证与存储服务
身份验证(Auth)是现代应用的标配,但自己实现一套安全、支持多种登录方式(邮箱/密码、手机号、第三方 OAuth)的系统绝非易事。Nhost 的 Auth 服务基于 Netlify 的 GoTrue 项目,提供了一个完整的用户管理系统。它负责用户注册、登录、邮箱验证、密码重置、会话管理,并生成用于访问其他服务(如 Hasura)的 JWT。
它的配置非常直观。在 Nhost 控制台中,你可以轻松启用 GitHub、Google、Facebook 等社交登录提供商。更棒的是,用户信息(如 ID、邮箱、元数据)会自动同步到 PostgreSQL 数据库的一个auth.users表中,你可以通过 Hasura 直接关联查询业务数据和用户信息,无需复杂的关联查询逻辑。
存储服务(Storage)则基于 MinIO,一个高性能的、与 Amazon S3 API 兼容的对象存储。在前端,你可以直接使用 Nhost 提供的 JavaScript SDK 轻松实现文件上传、下载和管理。上传的文件会自动获得一个可公开或私密访问的 URL。与 Auth 服务集成后,你可以轻松设置存储桶(Bucket)的权限,例如“仅登录用户可上传”或“仅用户本人可访问自己上传的文件”。
实操心得:对于图片或视频等资源,建议在上传后利用 Nhost Functions(无服务器函数)触发处理流程,比如使用 Sharp 库生成缩略图,或者调用 FFmpeg 进行视频转码。Nhost 的存储服务在文件上传完成时会发出事件,你可以编写函数来监听并处理这些事件,实现自动化的媒体处理流水线。
2.3. 无服务器函数与事件驱动架构
虽然 Hasura 能处理大部分数据操作,但复杂的业务逻辑、第三方 API 集成、支付回调、数据验证或清理等任务,仍然需要自定义代码。这就是 Nhost Functions 的用武之地。它允许你使用 JavaScript/TypeScript(Node.js 环境)或 Go 编写无服务器函数。
这些函数可以通过 HTTP 端点直接调用,也可以由数据库事件(通过 Hasura 的事件触发器)或存储事件自动触发。例如,当用户在orders表中插入一条新记录时,你可以配置一个事件触发器,调用一个发送订单确认邮件的函数。这种事件驱动的模式,使得应用的不同部分能够解耦,更易于维护和扩展。
Functions 的部署体验非常流畅。你只需将函数代码推送到关联的 Git 仓库(如 GitHub),Nhost 会自动构建和部署。每个函数都有独立的日志、环境变量管理和版本控制。对于需要安装额外 npm 包的函数,你只需要在项目根目录提供package.json文件即可。
3. 从零开始:项目初始化与本地开发实战
3.1 环境准备与 CLI 工具安装
开始使用 Nhost 的第一步是安装其命令行工具nhost。这个工具是你管理本地开发环境、连接云端项目、执行部署的核心。它可以通过 npm 全局安装:
npm install -g nhost安装完成后,运行nhost --version确认安装成功。接下来,你需要注册一个 Nhost 账户(目前提供免费的云托管计划),并在控制台中创建一个新项目。创建项目时,你可以选择部署区域(如欧洲或北美),并给你的项目起个名字。创建成功后,控制台会为你提供一个项目子域名(如your-project.nhost.run)和一些连接信息。
为了在本地进行开发,我们需要将云端项目的配置“克隆”到本地。使用 CLI 登录并关联你的项目:
nhost login # 按照提示在浏览器中完成认证 nhost link # CLI 会列出你的云端项目,选择你想要关联的那个执行nhost link后,CLI 会在当前目录生成一个nhost文件夹,里面包含了config.yaml等配置文件。这个文件定义了你的服务(数据库、Hasura、Auth、Storage)如何配置和连接。非常重要的一点是,不要将包含敏感信息的config.yaml提交到公开的 Git 仓库。Nhost 会自动将.env文件中的环境变量注入到配置中,所以你应该将数据库连接字符串、JWT 密钥等敏感信息放在项目根目录的.env文件中,并将.env添加到.gitignore。
3.2 本地开发环境启动与配置
关联项目后,启动本地开发环境就非常简单了。在项目根目录运行:
nhost dev这个命令会做几件事:1) 读取你的nhost/config.yaml配置;2) 在本地使用 Docker 启动所有服务(PostgreSQL, Hasura, Auth, Storage, 等)的容器;3) 将本地服务与云端项目的元数据(如 Hasura 的迁移文件和元数据)同步。启动完成后,CLI 会输出每个服务的本地访问地址,例如 Hasura 控制台通常在http://localhost:1337,Auth 服务在http://localhost:1337/v1/auth。
现在,你可以打开浏览器访问本地的 Hasura 控制台。这是你进行数据建模和 API 测试的主战场。首先,你需要连接到数据库。在 Hasura 控制台的 “Data” 标签页下,点击 “Connect Database”,数据库 URL 通常已经由nhost dev自动配置好了,名为default。
连接成功后,你就可以开始创建数据表了。假设我们正在构建一个简单的任务管理应用,我们创建一个todos表:
- 在 “Data” -> “Create Table” 界面。
- 表名输入
todos。 - 添加列:
id: 类型uuid,设为主键(Primary Key),默认值设置为gen_random_uuid()。title: 类型text,设置为非空(Not Null)。is_completed: 类型boolean,默认值false。user_id: 类型uuid,设置为非空。这是关键列,它将用于关联auth.users表。created_at: 类型timestamptz,默认值now()。
- 在 “Foreign Keys” 部分,为
user_id列添加一个外键,引用auth.users表的id列。 - 点击 “Create Table”。
表创建完成后,Hasura 会立即为你生成对应的 GraphQL API。切换到 “API” 标签页,你就能尝试查询和变更数据了。
3.3 权限配置:保障数据安全的核心步骤
创建了表,但默认情况下,所有 GraphQL 操作都是不允许的(除非是管理员角色)。我们必须配置权限,让登录用户只能操作自己的数据。这是 Nhost/Hasura 安全模型的核心。
在 Hasura 控制台的 “Data” ->todos表 -> “Permissions” 标签页下,我们为用户角色(user)配置权限:
查询(Select)权限:点击
user角色对应的 “Select” 权限的编辑图标。- Row permissions(行级权限):这里定义用户能看到哪些行。我们添加一个规则:
user_id_eqX-Hasura-User-Id。这个X-Hasura-User-Id是一个会话变量,它来自用户 JWT Token 中的声明。这意味着,用户只能查询user_id等于自己用户 ID 的任务。 - Column permissions(列级权限):选择所有列,或者排除敏感列。
- 点击 “Save Permissions”。
- Row permissions(行级权限):这里定义用户能看到哪些行。我们添加一个规则:
插入(Insert)权限:点击
user角色对应的 “Insert” 权限。- Row permissions:通常设置为
true,因为插入新行时,user_id会通过列预设值自动填充。 - Column permissions:允许
title和is_completed。关键在这里:对于user_id列,我们勾选 “Column is preset”(列预设值),并设置为会话变量X-Hasura-User-Id。这样,每当用户插入数据时,user_id会自动设置为当前用户的 ID,前端无需传递,也防止了用户冒充他人。 - Column permissions:不允许用户设置
id和created_at(它们由数据库默认值生成)。 - 点击 “Save Permissions”。
- Row permissions:通常设置为
更新(Update)和删除(Delete)权限:配置方式类似,在 Row permissions 中同样设置
user_id_eqX-Hasura-User-Id,确保用户只能修改和删除自己的任务。
配置完成后,一个登录用户在前端使用 GraphQL 查询todos时,将自动只能看到和操作属于自己的记录。这种基于会话变量的权限系统非常灵活强大,你可以构建出极其复杂的权限逻辑。
4. 前端集成与身份验证流程实战
4.1 安装 SDK 与初始化客户端
在前端项目中(以 React 为例),我们首先需要安装 Nhost 的客户端 SDK。
npm install @nhost/nhost-js graphql # 如果你使用 React,还可以安装 React 专属的 hooks 包 npm install @nhost/react接下来,在应用的入口文件(如src/main.jsx或App.jsx)中,初始化 Nhost 客户端。你需要从 Nhost 控制台获取项目的子域名和区域信息,配置在环境变量中。
import { NhostClient, NhostProvider } from '@nhost/nhost-js'; import { NhostReactProvider } from '@nhost/react'; import { createClient } from 'graphql-ws'; const nhost = new NhostClient({ subdomain: import.meta.env.VITE_NHOST_SUBDOMAIN, // 例如 'your-project' region: import.meta.env.VITE_NHOST_REGION, // 例如 'eu-central-1' }); function App() { return ( <NhostReactProvider nhost={nhost}> {/* 你的应用组件 */} </NhostReactProvider> ); }4.2 实现用户注册、登录与状态管理
使用@nhost/react提供的 hooks,可以非常方便地管理身份验证状态。例如,创建一个登录组件:
import { useState } from 'react'; import { useSignInEmailPassword, useSignUpEmailPassword } from '@nhost/react'; function AuthComponent() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [isSignUp, setIsSignUp] = useState(false); const { signInEmailPassword, isLoading: signInLoading, error: signInError } = useSignInEmailPassword(); const { signUpEmailPassword, isLoading: signUpLoading, error: signUpError } = useSignUpEmailPassword(); const handleSubmit = async (e) => { e.preventDefault(); if (isSignUp) { await signUpEmailPassword(email, password); // 注册成功后,通常需要验证邮箱,Nhost 会自动发送验证邮件 } else { await signInEmailPassword(email, password); } }; return ( <form onSubmit={handleSubmit}> <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="邮箱" /> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="密码" /> <button type="submit" disabled={signInLoading || signUpLoading}> {isSignUp ? '注册' : '登录'} </button> <button type="button" onClick={() => setIsSignUp(!isSignUp)}> 切换到{isSignUp ? '登录' : '注册'} </button> {(signInError || signUpError) && <p>错误: {(signInError || signUpError).message}</p>} </form> ); }使用useAuthenticationStatushook 可以轻松获取当前用户的登录状态,并据此渲染不同的 UI:
import { useAuthenticationStatus, useSignOut } from '@nhost/react'; function Header() { const { isAuthenticated, isLoading } = useAuthenticationStatus(); const { signOut } = useSignOut(); if (isLoading) return <div>加载中...</div>; return ( <header> {isAuthenticated ? ( <> <span>欢迎回来!</span> <button onClick={signOut}>退出登录</button> </> ) : ( <Link to="/login">请登录</Link> )} </header> ); }4.3 执行 GraphQL 查询与变更
当用户登录后,其 JWT Token 会自动附加到发出的 GraphQL 请求中。我们可以使用@nhost/react的useQuery和useMutationhooks(它们是对urql或@apollo/client的封装,取决于你的配置)来与 Hasura API 交互。
首先,定义你的 GraphQL 查询和变更。例如,获取当前用户的所有任务:
# queries.js import { gql } from 'graphql-tag'; export const GET_MY_TODOS = gql` query GetMyTodos { todos { id title is_completed created_at } } `; export const INSERT_TODO = gql` mutation InsertTodo($title: String!) { insert_todos_one(object: { title: $title }) { id title } } `;然后在组件中使用:
import { useQuery, useMutation } from '@nhost/react'; import { GET_MY_TODOS, INSERT_TODO } from './queries'; function TodoList() { const { data, loading, error } = useQuery(GET_MY_TODOS); const [insertTodo, { loading: inserting }] = useMutation(INSERT_TODO); const handleAddTodo = async (title) => { await insertTodo({ title }); // mutation 执行后,`useQuery` 的 `data` 会自动更新(如果配置了缓存更新策略) }; if (loading) return <div>加载任务中...</div>; if (error) return <div>出错了: {error.message}</div>; return ( <div> <ul> {data?.todos.map((todo) => ( <li key={todo.id}>{todo.title}</li> ))} </ul> {/* 添加任务的表单 */} </div> ); }注意事项:
useQuery默认会发送请求并缓存结果。对于需要实时更新的数据(如聊天消息、通知),你应该使用订阅(Subscription)。Nhost SDK 同样支持订阅,它会在底层建立 WebSocket 连接,当数据库中的数据发生变化时,前端会实时收到更新。这为构建高度交互式的应用提供了极大便利。
5. 高级特性与生产环境部署指南
5.1 数据库迁移与元数据管理
在本地开发时,我们通过 Hasura 控制台直接创建表。但为了团队协作和持续集成/持续部署(CI/CD),我们必须将数据库结构(Schema)和 Hasura 的配置(如权限、关系)进行版本控制。Nhost 使用 Hasura 的迁移系统来实现这一点。
每当你通过 Hasura 控制台修改数据库(创建/修改表、视图、函数)或修改 Hasura 元数据(权限、远程模式、事件触发器)时,你应该通过 CLI 将其导出为迁移文件。
# 在项目根目录执行 nhost hasura migrate create "init" --from-server # 或者,如果你已经做了一些更改,需要将当前状态与之前保存的状态对比并生成新的迁移 nhost hasura migrate create "add_user_profile" --from-server这个命令会分析当前数据库和元数据状态,与上一次记录的迁移状态进行比较,然后在nhost/migrations目录下生成新的迁移文件。这些文件包含了 SQL 语句(用于数据库变更)和 YAML 文件(用于 Hasura 元数据)。务必将这些文件提交到 Git 仓库。这样,当其他团队成员拉取代码后,或者在生产环境部署时,可以通过运行迁移来同步状态。
部署到生产环境时,Nhost 会自动应用nhost/migrations目录下的所有迁移文件。你也可以通过 CLI 手动应用或回滚迁移。
5.2 自定义无服务器函数开发
当业务逻辑超出 Hasura 的能力范围时,就需要编写无服务器函数。在 Nhost 项目中,函数位于nhost/functions目录下。每个子目录代表一个独立的函数。
例如,我们创建一个发送欢迎邮件的函数,当用户注册时被触发。
创建函数目录和入口文件:
mkdir -p nhost/functions/send-welcome-email cd nhost/functions/send-welcome-email npm init -y npm install nodemailer创建
index.js:// nhost/functions/send-welcome-email/index.js const nodemailer = require('nodemailer'); const transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST, port: process.env.SMTP_PORT, secure: true, // 使用 TLS auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS, }, }); module.exports = async (req, res) => { // 这个函数可以由 HTTP 请求触发,也可以由数据库事件触发 // 假设我们从事件负载中获取用户邮箱 const { event } = req.body; const userEmail = event?.data?.new?.email; if (!userEmail) { return res.status(400).json({ error: 'No email provided' }); } const mailOptions = { from: '"Our App" <noreply@example.com>', to: userEmail, subject: '欢迎加入!', text: `欢迎注册我们的服务!`, html: `<strong>欢迎注册我们的服务!</strong>`, }; try { await transporter.sendMail(mailOptions); console.log(`欢迎邮件已发送至: ${userEmail}`); return res.status(200).json({ message: 'Email sent successfully' }); } catch (error) { console.error('发送邮件失败:', error); return res.status(500).json({ error: 'Failed to send email' }); } };在 Nhost 控制台或
nhost/config.yaml中配置环境变量SMTP_HOST,SMTP_PORT,SMTP_USER,SMTP_PASS。配置触发器。我们需要在用户注册时调用这个函数。这可以通过 Hasura 的事件触发器完成。在 Hasura 控制台的 “Events” -> “Triggers” 页面,创建一个新触发器:
- Trigger Name:
send_welcome_email - Table:
auth.users - Operations:
Insert - Webhook URL: 填写你的函数 URL,格式通常为
https://[你的项目子域名].functions.nhost.run/v1/send-welcome-email - 可以配置 payload,选择要发送的数据列(如
email)。
- Trigger Name:
现在,每当有新用户注册时,Hasura 就会向你的函数发送一个包含新用户数据的 POST 请求,函数便会执行发送欢迎邮件的逻辑。
5.3 生产环境部署与监控
当你完成本地开发和测试后,部署到 Nhost 的云环境非常简单。由于你的项目配置和代码已经通过 Git 管理,部署通常就是一次推送。
git add . git commit -m "准备部署" git push origin mainNhost 与 GitHub(或其他 Git 提供商)有深度集成。你可以在 Nhost 控制台中关联你的仓库,并设置自动部署。当代码推送到指定的分支(如main)时,Nhost 会自动执行以下操作:
- 拉取最新代码。
- 安装依赖(如果有)。
- 运行数据库迁移(应用
nhost/migrations中的变更)。 - 重新部署无服务器函数。
- 更新服务配置。
部署完成后,你的生产环境就准备就绪了。Nhost 控制台提供了基本的监控面板,你可以查看服务的请求量、错误率、函数调用次数和延迟等信息。对于更深入的监控和日志分析,建议将函数的日志(可通过 CLI 的nhost logs命令查看)和数据库日志导出到外部服务,如 Datadog 或 LogRocket。
生产环境关键检查清单:
- 环境变量:确保所有敏感的生产环境变量(数据库连接字符串、JWT 密钥、第三方 API 密钥)已在 Nhost 控制台中正确设置,并且没有意外提交到代码仓库。
- CORS 配置:在 Nhost 控制台的 “Settings” 中,正确配置你的前端应用域名,防止跨域问题。
- 自定义域名:为你的项目配置自定义域名,提升品牌形象。
- 备份策略:虽然 Nhost 提供自动备份,但了解备份频率和恢复流程是必要的。
- 性能与缩放:关注数据库连接数和函数冷启动时间。对于高流量应用,可能需要调整数据库规格或优化函数代码。
6. 常见问题排查与性能优化技巧
6.1 身份验证与权限问题
这是新手最常遇到问题的地方。症状通常是:“前端明明登录了,但查询数据时返回空数组或权限错误”。
- 排查步骤 1:检查 JWT Token。在前端,使用
nhost.auth.getAccessToken()获取当前的 Token,然后到 jwt.io 解码。确认 Token 中包含正确的x-hasura-user-id和x-hasura-default-role(应为user)声明。如果缺失,检查 Auth 服务的 JWT 模板配置。 - 排查步骤 2:检查 Hasura 权限规则。在 Hasura 控制台的 “Data” -> “Permissions” 标签页,使用 “GraphiQL” 界面右上角的 “Request Headers” 功能。你可以手动设置
Authorization: Bearer <你的JWT>头,然后以不同角色(如user)模拟执行查询,这能帮你确认权限规则是否按预期工作。 - 排查步骤 3:检查列预设值。对于插入操作,如果
user_id没有自动填充,一定是 “Column is preset” 配置有误或会话变量名不匹配。确保预设的会话变量名与 JWT 中的声明键名完全一致(通常是X-Hasura-User-Id)。
6.2 GraphQL 查询性能与 N+1 问题
Hasura 生成的 GraphQL API 非常灵活,可以一次性查询大量关联数据。但这可能导致性能问题,特别是出现“N+1查询”时。例如,查询一个用户列表及其所有任务,如果 GraphQL 查询写得不好,Hasura 可能会先执行1次查询获取用户列表,然后为每个用户执行1次查询获取任务,导致数据库压力剧增。
- 优化技巧 1:使用查询分析。在 Hasura 控制台执行 GraphQL 查询时,勾选 “Analyze” 选项,它会显示查询计划,帮助你识别潜在的性能瓶颈。
- 优化技巧 2:合理使用限制(limit)和分页。避免一次性拉取海量数据。在查询中使用
limit和offset,或更好的limit和order_by某个唯一键进行游标分页。 - 优化技巧 3:建立适当的数据库索引。对于经常用于
where条件、order_by或连接条件的列(如user_id,created_at),务必创建索引。这能极大提升查询速度。你可以通过 Hasura 控制台的 “Data” -> “SQL” 页面执行CREATE INDEX语句,并将此操作生成迁移文件。
6.3 无服务器函数冷启动与超时
Nhost Functions 运行在无服务器环境中,当一段时间没有请求时,函数实例会被回收,下一个请求到来时会触发“冷启动”,导致响应时间变长。此外,函数默认有执行超时限制。
- 优化技巧 1:保持函数轻量。避免在函数中引入庞大的 npm 包。只导入必需的模块。考虑将初始化耗时长的对象(如数据库连接池、第三方 SDK 客户端)放在函数处理程序外部,利用无服务器环境的复用机制。
- 优化技巧 2:使用更快的运行时。如果对延迟极其敏感,可以尝试使用 Go 来编写函数,Go 的冷启动速度通常比 Node.js 更快。
- 优化技巧 3:设置适当的超时时间。在
nhost/config.yaml中可以为每个函数配置timeout(默认可能为几秒)。对于长时间运行的任务(如图像处理),需要增加超时时间,但更好的做法是将耗时任务拆解,通过消息队列或事件驱动的方式异步处理。 - 避坑记录:我曾遇到一个函数,因为内部调用了一个响应很慢的外部 API,导致频繁超时。解决方案是将其改为“触发即忘”模式:函数只负责将任务信息写入数据库的一个“任务队列”表,然后立即返回。再由另一个定时运行的函数(或由数据库事件触发)来消费这个队列,处理慢速的外部调用。这样既解决了超时问题,也提高了系统的可靠性。
6.4 数据库连接池与连接耗尽
在函数中直接连接 PostgreSQL 数据库时,如果每个请求都创建新连接,很快就会耗尽数据库的最大连接数。虽然 Hasura 已经处理了 GraphQL 请求的连接,但自定义函数需要自己管理。
- 最佳实践:使用连接池并全局复用。不要在函数处理程序内部创建连接。像下面这样在函数模块顶层初始化一个连接池:
// nhost/functions/my-function/index.js const { Pool } = require('pg'); // 从环境变量读取数据库连接字符串 const connectionString = process.env.NHOST_POSTGRES_DATABASE_URL; const pool = new Pool({ connectionString, // 设置连接池大小,避免耗尽数据库连接 max: 5, }); module.exports = async (req, res) => { const client = await pool.connect(); try { const result = await client.query('SELECT * FROM my_table'); res.json(result.rows); } finally { client.release(); // 非常重要!一定要释放连接回池中 } };通过遵循这些排查方法和优化技巧,你可以让基于 Nhost 构建的应用运行得更稳定、更高效。这个平台将后端开发的复杂性封装了起来,但并不意味着你可以完全忽视底层原理。理解其工作机制,能帮助你在遇到问题时快速定位,并充分发挥其潜力。
