
本文字数:9751;估计阅读时间:25 分钟
作者:Fiveonefour & ClickHouse Team
本文在公众号【ClickHouseInc】首发

这篇文章是一份实用指南,讲解如何在你的 Web 应用中构建一个基于 ClickHouse 的 API,操作方式对熟悉 Postgres 的开发者来说将非常直观。
你将学会创建一个可用于生产环境的 ClickHouse Cloud 服务,实现从 Postgres 到 ClickHouse 的实时数据同步。
我们将使用 Moose OLAP 为 ClickHouse 表生成原生的 TypeScript 接口,构建完全类型安全的分析 API,并将其集成进应用,最终部署到生产环境。
如果你曾在用户产品中上线分析功能,很可能是基于你现有的事务型(OLTP)数据库,比如 Postgres 或 MongoDB 来构建的。我们都经历过这种做法——这是启动并交付对用户有价值功能最快的方式。但随着用户数量、数据量和查询频率的不断增长,你可能已经开始遇到查询变慢的问题;用户越多、数据越多、查询越频繁,仪表盘就越卡。这不仅影响用户体验,也让你不得不花大量时间调优性能,从而拖慢了新功能的交付节奏。

说明:我们的开源参考应用中的示例仪表盘,采用 Vite + React 的 Web 前端,后端并行使用事务型与分析型数据库
你如果看过一些基准测试,应该已经知道 ClickHouse 是当前最快的分析型(OLAP)数据库。ClickHouse 专为分析场景设计,能在毫秒级完成对数十亿行数据的查询。但将一个新服务接入现有 Web 技术栈,听起来确实有点棘手——你可能对如何集成 Postgres 很熟悉,但不太确定该怎么接入 ClickHouse。
这篇文章正是为此而写的,它将手把手教你如何在 Web 应用中构建基于 ClickHouse 的 API,方式对熟悉 Postgres 的开发者来说也十分友好。同时,它还为未来的团队协作与功能扩展打下良好的开发体验(DX)基础。
我们将使用 ClickHouse 提供极速分析能力,配合 Moose OLAP 带来流畅而熟悉的开发体验:
-
ClickHouse 的极速性能,配合受 ORM 启发的 Schema 代码定义
-
从数据库到底层 React Hooks 的端到端类型安全与契约
-
面向 Web 应用的本地优先开发方式,实现快速迭代
-
支持预览环境和托管迁移的安全部署方案

整篇文章将通过一个开源参考应用提供配套的代码示例。你可以直接体验它的前端,也可以在 GitHub 上查看完整代码库。
将 Postgres 与 ClickHouse 同步
ClickHouse(以及本文中使用的整个技术栈)都是开源的,可以本地运行,也可以部署在你自己的基础设施上进行自托管。但最快的上手方式是直接在 ClickHouse Cloud 启动一个免费试用集群,只需几分钟就能获得一个可用于生产环境的集群,无需管理任何服务器或集群。稍后你还会看到如何将 ClickHouse Cloud 中的一部分数据同步到本地容器,作为本地 ClickHouse 开发实例的数据源。
准备好 ClickHouse 集群后,第一步是准备数据。
在你的应用中,事务型数据库(如 Postgres)是数据的唯一来源。而 ClickHouse 会通过变更数据捕获(CDC)机制与事务库保持实时同步。ClickHouse Cloud 提供了 ClickPipes——一个简单但高性能的 CDC 引擎,只需不到一分钟就能完成配置。它不仅会将现有数据一次性导入 ClickHouse,还会持续实时同步未来的所有更改。

只需几分钟,你的 Postgres 数据就能完整镜像到一个可用于生产环境的 ClickHouse 集群中。接下来,我们要让这些数据在应用代码中变得可用。
将表结构同步为代码
如果你用过 Drizzle 或 Prisma 等事务型 ORM,应该对 “db pull” 的流程很熟悉——直接从数据库结构生成 TypeScript 类型。Moose OLAP 提供了类似的功能,但它是为 ClickHouse 原生打造的。(大多数事务型 ORM 与 OLAP 数据库不太兼容,因为 ClickHouse 有自己的引擎机制、默认值策略和建模方式,这些在 Postgres 或 MySQL 中都不存在。)
你需要先安装 MooseStack CLI(命令行工具 moose),然后在项目根目录运行命令启动流程:
# initialize a new MooseStack project called "analytics service"
moose init analytics-service --from-remote
按照提示连接远程 ClickHouse 数据库后,MooseStack 会自动探查数据库结构,并生成一个新的 analytics-service 文件夹,包含以下内容:
-
可复用于 API 和查询的数据模型(TypeScript 类型)
-
每张 ClickHouse 表对应的 TypeScript “OlapTable” 对象
借助 MooseStack OLAP,你可以通过原生 TypeScript 接口与 ClickHouse 表和结构进行交互,例如这样:
/* Link to full source code:
https://github.com/514-labs/area-code/blob/main/ufa-lite/services/analytical-moose-foobar/app/externalModels.ts
*/
export interface foo {
id: Key>;
name: string;
description: string | undefined;
status: string;
priority: number & ClickHouseInt<"int32">;
is_active: boolean;
metadata: string | undefined;
tags: string[];
score: string & ClickHouseDecimal<10, 2> | undefined;
large_text: string | undefined;
created_at: string & typia.tags.Format<"date-time"> & ClickHousePrecision<6>;
updated_at: string & typia.tags.Format<"date-time"> & ClickHousePrecision<6>;
_peerdb_synced_at: string & typia.tags.Format<"date-time"> & ClickHousePrecision<9> & ClickHouseDefault<"now64()">;
_peerdb_is_deleted: number & ClickHouseInt<"int8">;
_peerdb_version: number & ClickHouseInt<"int64">;
}
export const FooTable = new OlapTable("foo", {
orderByFields: ["id"],
engine: ClickHouseEngines.ReplacingMergeTree,
ver: "_peerdb_version",
settings: { index_granularity: "8192" },
lifeCycle: LifeCycle.EXTERNALLY_MANAGED,
});
至此,最关键的步骤已经完成:ClickHouse 的 Schema 已成功以代码形式呈现。它具备类型定义、版本控制,并与应用的其他代码共存。但代码只是第一步,你还需要实际运行它:写查询、试错、修复和迭代 API,而不影响生产环境。
这正是下一步要做的:将 MooseStack 作为你本地开发环境的一部分来运行。
本地优先的开发体验与初始化数据的 ClickHouse
想想你平时如何做前端开发:你绝不会在本地验证之前就上线一个 UI 改动。
使用 Moose CLI,你可以获得类似的开发体验:你的分析后端作为本地服务与应用并排运行,你可以随时启动服务并用生产数据进行初始化,在不触及 staging 或生产环境的情况下自由尝试和调整。
在我们的参考应用中,这些模块被组织在一个 monorepo 代码仓中(当然你也可以不用 monorepo):
-
/apps/web:基于 Vite + React 的前端应用
-
/services/transactional:由 Supabase + Drizzle ORM 支持的 Fastify API 服务,提供标准 CRUD 接口
-
/services/analytical:专为分析而构建的 MooseStack + ClickHouse 后端服务
因此在本地开发时,只需将这些组件一起启动即可:
cd apps/web && pnpm dev
cd services/transactional && pnpm dev
cd services/analytical && pnpm dev
# or from the root of the monorepo
pnpm dev
如果你按本文操作至今,那么你的本地开发环境现在应包含以下内容:
-
React 前端 → 本地运行
-
应用服务器 → 本地运行(访问 Postgres 的 API)
-
MooseStack 分析服务 → 本地运行(访问 ClickHouse 的 API)
初始化本地 ClickHouse 数据
当你运行 moose dev 命令时,CLI 会自动启动一个本地的 ClickHouse 容器。默认情况下,虽然表结构会被创建,但里面并没有数据。为了能测试查询和 API,我们可以从与你 Postgres 同步的 ClickHouse Cloud 集群中,导入真实的生产数据作为初始化内容。
moose seed clickhouse --connection-string $MOOSE_REMOTE_CLICKHOUSE_URL --limit 100000
示例中会将每张表最多 10 万行数据复制到本地 ClickHouse 实例。这样你就可以完全在本地环境中进行开发,无需访问远程服务,并且操作的数据也具备真实代表性。
进度回顾:目前为止你已经完成了这些重要步骤
-
创建了一个具备生产能力的 ClickHouse 服务
-
成功实现了 Postgres 到 ClickHouse 的实时数据同步
-
将数据库结构转化为 TypeScript 类型与契约
-
并在本地的 ClickHouse 开发环境中,加载了这些结构并初始化了数据样本
此时,分析能力已经不再是“一个只能远程访问的数据库”。它已经融入了你的日常开发流程。你可以在本地运行查询、通过版本控制不断演化数据结构,并像前端或 API 一样享受热更新体验。
构建类型安全的分析 API
从这一刻起,你的 Web 应用不再需要直接访问 ClickHouse。你新增的 analytics-service 可以接收 API 请求,转化为带类型的查询,并自动保障类型契约的正确性。我们现在就来实践,为前端提供带类型约束的 API 端点。
如果你曾用过 OLTP 系统中的 ORM 工具,这一流程会非常熟悉。在 Postgres/MySQL 中,像 Drizzle 或 Prisma 这样的工具允许你用 TypeScript 定义 Schema,生成验证器,并在路由或表单中复用。当 Schema 更新时,验证器也会同步更新,构建时即可发现问题。
在 Moose 中,分析 API 的契约完全由 TypeScript 描述。你定义的请求与响应类型会自动编译为运行时验证器。这样你既能在开发阶段获得类型提示,也能在运行时保障类型安全,无需额外手动编写校验逻辑。
/* Link to full source code: https://github.com/514-labs/area-code/blob/main/ufa-lite/services/analytical-moose-foobar/app/apis/foo/consumption/foo-score-over-time-api.ts */
import { Api } from "@514labs/moose-lib";
import { FooTable } from "../../externalModels";
export type FoosScoreOverTimeDataPoint = {
date: string;
averageScore: number;
totalCount: number;
};
export type GetFoosScoreOverTimeParams = { days?: number };
export type GetFoosScoreOverTimeResponse = {
data: FoosScoreOverTimeDataPoint[];
queryTime: number;
};
export const scoreOverTimeApi = new Api<
GetFoosScoreOverTimeParams,
GetFoosScoreOverTimeResponse
>("foo-score-over-time", async ({ days = 90 }, { client, sql }) => {
/* scoreOverTimeApi function code, see below */
});
在查询部分,Moose OLAP 让 SQL 具备结构感知能力。OlapTable 对象会暴露出带类型的字段列,因此你在编写查询时可以直接引用它们。这样,SQL 不再是脆弱的字符串拼接,而是由编辑器理解的结构化代码:一旦引用了不存在的字段,IDE 就会立刻提示,而不用等到运行时才发现错误。
/* Link to source code: https://github.com/514-labs/area-code/blob/main/ufa-lite/services/analytical-moose-foobar/app/apis/foo/consumption/foo-score-over-time-api.ts */
/* scoreOverTimeApi function code */
const start = new Date();
start.setDate(start.getDate() - days);
const end = new Date();
const startStr = start.toISOString().split("T")[0];
const endStr = end.toISOString().split("T")[0];
const query = sql`
SELECT
toDate(${FooTable.columns.created_at}) AS date,
AVG(${FooTable.columns.score}) AS averageScore,
COUNT(*) AS totalCount
FROM ${FooTable}
WHERE toDate(${FooTable.columns.created_at}) BETWEEN toDate(${startStr}) AND toDate(${endStr})
AND ${FooTable.columns.score} IS NOT NULL
GROUP BY toDate(${FooTable.columns.created_at})
ORDER BY date ASC
`;
测试你的 API 端点
每次运行 moose dev,MooseStack CLI 都会热更新你的 API,并自动生成一个符合 OpenAPI 3.0 标准的规范文件(保存在 .moose/openapi.json 中)。这意味着你可以立即使用 curl、Swagger UI 或 VSCode 插件等工具来测试接口。
curl -X 'GET'
'http://localhost:4410/api/foo-score-over-time?days=10'
-H 'accept: application/json'
{
"data": [
{
"date": "2025-06-15",
"averageScore": 41.81,
"totalCount": "6"
},
{
"date": "2025-06-16",
"averageScore": 22.3,
"totalCount": "5"
},
{
"date": "2025-06-17",
"averageScore": 52.29,
"totalCount": "6"
},
...
]
}
每条查询请求也都会实时记录在运行 moose dev 的终端中。如果发生错误,你会同时在 API 响应和开发日志中看到提示。这样的实时反馈机制可以让你快速定位问题并立即修复,而不必等到部署后再去追查。
API Executing API: foo-score-over-time
API Query: SELECT toDate(`created_at`) as date, AVG(`score`) as averageScore, COUNT(*) as totalCount FROM `foo` WHERE toDate(`created_at`) >= toDate('2025-06-15') AND toDate(`created_at`) <= toDate('2025-09-13') AND `score` IS NOT NULL GROUP BY toDate(`created_at`) ORDER BY date ASC
API Query completed: 14ms
在你将旧有低性能的 API 逐步迁移到全新的、极速的 ClickHouse 驱动 API 的过程中,也别忘了善用你常用的 AI 助手。这个快速迭代的开发闭环,对 AI 智能体开发者尤其友好——我们都知道,它们并不总能一次生成完美代码。
打通前后端连接
生成的 OpenAPI 规范不仅仅是文档,它也是前后端集成的桥梁。你无需手动维护 API 契约同步,只需将 OpenAPI 文件交给 Orval、Kubb 等 SDK 生成器即可。
在我们的参考应用中,我们使用 Kubb 从自动生成的 OpenAPI 规范中直接生成一套带完整类型定义的 React fetch 客户端。集成过程非常简单:只需配置 Moose 开发服务器在热更新时触发 SDK 代码生成,并将 Kubb 指向 Moose 输出的 .moose/openapi.yaml 文件即可。
[http_server_config]
on_reload_complete_script="pnpm generate-sdk"
# Link to full source code: https://github.com/514-labs/area-code/blob/main/ufa-lite/services/analytical-moose-foobar/moose.config.toml
这样一来,每次 API 或 schema 改动,前端数据请求逻辑就会自动同步更新。在 React 中,只需导入生成的客户端并在组件中调用即可。
[http_server_config]
on_reload_complete_script="pnpm generate-sdk"
# Link to full source code: https://github.com/514-labs/area-code/blob/main/ufa-lite/services/analytical-moose-foobar/moose.config.toml
这形成了一个完整的闭环:你的 schema 定义驱动 API,Moose API 自动保障类型契约,前端使用自动生成的客户端始终保持最新状态。整个流程在本地实时运行,随着代码的更改自动响应。你将拥有真正的端到端类型安全体验,无需手动同步类型,也能像调用老旧的 Postgres/MySQL/Mongo 接口一样,轻松集成分析 API。
用 Boreal 上线生产环境
一旦 schema 验证通过、API 本地测试完毕,你就可以准备上线了。由 MooseStack 背后的团队 514 推出的 Boreal,能为分析服务提供如 Vercel 或 Heroku 般的现代开发体验。你只需连接 GitHub 仓库,创建一个分支,Boreal 就会为你生成一个独立的预览环境,并在 ClickHouse 集群中创建对应的 staging 数据库。一旦合并到主分支,MooseStack 服务和 schema 变更便会自动部署上线。
Boreal 的部署不仅包括代码,还会将你最近的 schema 改动转化为数据库迁移操作。为保障上线安全,它会自动将代码逻辑与数据库现状进行比对。如果某些字段因上游流程(如 ClickPipes)被删除,Boreal 会阻止上线,直到你更新代码为止,确保 API 不会指向已删除的字段。你只需更新本地 schema,调整 API,重新测试并再次合并,这次就能确保一切顺利对齐。
moose db pull --connection-string $MOOSE_REMOTE_CLICKHOUSE_URL
MooseStack 本身是开源且支持自托管的。但就像 ClickHouse Cloud 是最快构建生产数据库的方式,Boreal 是构建生产级分析服务的最快通道——内建 CI/CD、预览分支与 GitHub 集成,一应俱全。
快速上手指南
1. 使用 GitHub 账号登录 boreal.cloud
2. 链接包含你 MooseStack 项目的 GitHub 仓库
3. 使用管理员权限连接你的 ClickHouse Cloud 集群
从这里开始,你的工作流就和本地开发一样简单:打开一个 PR,合并到主分支,其余工作交给 Boreal 完成——包括构建 MooseStack 服务、验证 ClickHouse schema 并部署最新 API。
总结
要为用户提供流畅、响应迅速的分析体验,一套专用的实时分析数据库是必不可少的,而 ClickHouse 是当前最强的解决方案。其实,将 ClickHouse 集成进应用并不复杂。将毫秒级 OLAP 的 ClickHouse 与以本地开发为核心的 MooseStack 搭配使用,既能满足用户对性能的期待,也能为团队带来顺畅的开发体验。
你可以从 ClickHouse Cloud 和 ClickPipes 开始,将事务数据库中的数据镜像过来;用 Moose OLAP 将 schema 同步为代码;用 Moose API 构建带运行时校验的接口;再通过自动生成的 hooks 快速集成到 React 中。当准备上线时,Boreal 能为你提供熟悉且高效的生产发布流程,内含预览环境、数据库迁移与 CI/CD 自动部署。
从现在起,在你的事务型数据库旁无缝引入 ClickHouse,按自己的节奏推进迁移,持续发布功能,再也不用担心用户看到加载中的旋转图标。
征稿启示
面向社区长期正文,文章内容包括但不限于关于 ClickHouse 的技术研究、项目实践和创新做法等。建议行文风格干货输出&图文并茂。质量合格的文章将会发布在本公众号,优秀者也有机会推荐到 ClickHouse 官网。请将文章稿件的 WORD 版本发邮件至:Tracy.Wang@clickhouse.com

4276

被折叠的 条评论
为什么被折叠?



